diff options
Diffstat (limited to 'fs/smb/client/dfs_cache.c')
| -rw-r--r-- | fs/smb/client/dfs_cache.c | 55 | 
1 files changed, 47 insertions, 8 deletions
diff --git a/fs/smb/client/dfs_cache.c b/fs/smb/client/dfs_cache.c index 4dada26d56b5..f2ad0ccd08a7 100644 --- a/fs/smb/client/dfs_cache.c +++ b/fs/smb/client/dfs_cache.c @@ -1120,24 +1120,63 @@ static bool target_share_equal(struct cifs_tcon *tcon, const char *s1)  	return match;  } -static bool is_ses_good(struct cifs_ses *ses) +static bool is_ses_good(struct cifs_tcon *tcon, struct cifs_ses *ses)  {  	struct TCP_Server_Info *server = ses->server; -	struct cifs_tcon *tcon = ses->tcon_ipc; +	struct cifs_tcon *ipc = NULL;  	bool ret; +	spin_lock(&cifs_tcp_ses_lock);  	spin_lock(&ses->ses_lock);  	spin_lock(&ses->chan_lock); +  	ret = !cifs_chan_needs_reconnect(ses, server) && -		ses->ses_status == SES_GOOD && -		!tcon->need_reconnect; +		ses->ses_status == SES_GOOD; +  	spin_unlock(&ses->chan_lock); + +	if (!ret) +		goto out; + +	if (likely(ses->tcon_ipc)) { +		if (ses->tcon_ipc->need_reconnect) { +			ret = false; +			goto out; +		} +	} else { +		spin_unlock(&ses->ses_lock); +		spin_unlock(&cifs_tcp_ses_lock); + +		ipc = cifs_setup_ipc(ses, tcon->seal); + +		spin_lock(&cifs_tcp_ses_lock); +		spin_lock(&ses->ses_lock); +		if (!IS_ERR(ipc)) { +			if (!ses->tcon_ipc) { +				ses->tcon_ipc = ipc; +				ipc = NULL; +			} +		} else { +			ret = false; +			ipc = NULL; +		} +	} + +out:  	spin_unlock(&ses->ses_lock); +	spin_unlock(&cifs_tcp_ses_lock); +	if (ipc && server->ops->tree_disconnect) { +		unsigned int xid = get_xid(); + +		(void)server->ops->tree_disconnect(xid, ipc); +		_free_xid(xid); +	} +	tconInfoFree(ipc, netfs_trace_tcon_ref_free_ipc);  	return ret;  }  /* Refresh dfs referral of @ses */ -static void refresh_ses_referral(struct cifs_ses *ses) +static void refresh_ses_referral(struct cifs_tcon *tcon, struct cifs_ses *ses)  {  	struct cache_entry *ce;  	unsigned int xid; @@ -1153,7 +1192,7 @@ static void refresh_ses_referral(struct cifs_ses *ses)  	}  	ses = CIFS_DFS_ROOT_SES(ses); -	if (!is_ses_good(ses)) { +	if (!is_ses_good(tcon, ses)) {  		cifs_dbg(FYI, "%s: skip cache refresh due to disconnected ipc\n",  			 __func__);  		goto out; @@ -1241,7 +1280,7 @@ static void refresh_tcon_referral(struct cifs_tcon *tcon, bool force_refresh)  	up_read(&htable_rw_lock);  	ses = CIFS_DFS_ROOT_SES(ses); -	if (!is_ses_good(ses)) { +	if (!is_ses_good(tcon, ses)) {  		cifs_dbg(FYI, "%s: skip cache refresh due to disconnected ipc\n",  			 __func__);  		goto out; @@ -1309,7 +1348,7 @@ void dfs_cache_refresh(struct work_struct *work)  	tcon = container_of(work, struct cifs_tcon, dfs_cache_work.work);  	list_for_each_entry(ses, &tcon->dfs_ses_list, dlist) -		refresh_ses_referral(ses); +		refresh_ses_referral(tcon, ses);  	refresh_tcon_referral(tcon, false);  	queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work,  | 
