cifs: check reconnects for channels of active tcons too

With the new multichannel logic, when a channel needs reconnection,
the tree connect and other channels can still be active.
This fix will handle cases of checking for channel reconnect,
when the tcon does not need reconnect.

Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
Shyam Prasad N 2021-10-30 04:51:35 +00:00 committed by Steve French
parent e4e2787bef
commit 3663c9045f
3 changed files with 99 additions and 19 deletions

View File

@ -117,6 +117,7 @@ enum statusEnum {
CifsInSessSetup, CifsInSessSetup,
CifsNeedTcon, CifsNeedTcon,
CifsInTcon, CifsInTcon,
CifsNeedFilesInvalidate,
CifsInFilesInvalidate CifsInFilesInvalidate
}; };
@ -923,6 +924,7 @@ struct cifs_chan {
*/ */
struct cifs_ses { struct cifs_ses {
struct list_head smb_ses_list; struct list_head smb_ses_list;
struct list_head rlist; /* reconnect list */
struct list_head tcon_list; struct list_head tcon_list;
struct cifs_tcon *tcon_ipc; struct cifs_tcon *tcon_ipc;
struct mutex session_mutex; struct mutex session_mutex;

View File

@ -335,6 +335,7 @@ static int __cifs_reconnect(struct TCP_Server_Info *server,
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
cifs_swn_reset_server_dstaddr(server); cifs_swn_reset_server_dstaddr(server);
mutex_unlock(&server->srv_mutex); mutex_unlock(&server->srv_mutex);
mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
} }
} while (server->tcpStatus == CifsNeedReconnect); } while (server->tcpStatus == CifsNeedReconnect);
@ -4404,9 +4405,22 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
char *tree; char *tree;
struct dfs_info3_param ref = {0}; struct dfs_info3_param ref = {0};
/* only send once per connect */
spin_lock(&cifs_tcp_ses_lock);
if (tcon->ses->status != CifsGood ||
(tcon->tidStatus != CifsNew &&
tcon->tidStatus != CifsNeedTcon)) {
spin_unlock(&cifs_tcp_ses_lock);
return 0;
}
tcon->tidStatus = CifsInTcon;
spin_unlock(&cifs_tcp_ses_lock);
tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL); tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL);
if (!tree) if (!tree) {
return -ENOMEM; rc = -ENOMEM;
goto out;
}
if (tcon->ipc) { if (tcon->ipc) {
scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname); scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);
@ -4438,11 +4452,18 @@ out:
kfree(tree); kfree(tree);
cifs_put_tcp_super(sb); cifs_put_tcp_super(sb);
if (rc) {
spin_lock(&cifs_tcp_ses_lock);
tcon->tidStatus = CifsNeedTcon;
spin_unlock(&cifs_tcp_ses_lock);
}
return rc; return rc;
} }
#else #else
int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc) int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc)
{ {
int rc;
const struct smb_version_operations *ops = tcon->ses->server->ops; const struct smb_version_operations *ops = tcon->ses->server->ops;
/* only send once per connect */ /* only send once per connect */
@ -4456,6 +4477,13 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
tcon->tidStatus = CifsInTcon; tcon->tidStatus = CifsInTcon;
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
return ops->tree_connect(xid, tcon->ses, tcon->treeName, tcon, nlsc); rc = ops->tree_connect(xid, tcon->ses, tcon->treeName, tcon, nlsc);
if (rc) {
spin_lock(&cifs_tcp_ses_lock);
tcon->tidStatus = CifsNeedTcon;
spin_unlock(&cifs_tcp_ses_lock);
}
return rc;
} }
#endif #endif

View File

@ -289,14 +289,18 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
rc = -EHOSTDOWN; rc = -EHOSTDOWN;
goto failed; goto failed;
} }
} } else {
if (rc || !tcon->need_reconnect) {
mutex_unlock(&ses->session_mutex); mutex_unlock(&ses->session_mutex);
goto out; goto out;
} }
mutex_unlock(&ses->session_mutex);
skip_sess_setup: skip_sess_setup:
mutex_lock(&ses->session_mutex);
if (!tcon->need_reconnect) {
mutex_unlock(&ses->session_mutex);
goto out;
}
cifs_mark_open_files_invalid(tcon); cifs_mark_open_files_invalid(tcon);
if (tcon->use_persistent) if (tcon->use_persistent)
tcon->need_reopen_files = true; tcon->need_reopen_files = true;
@ -3787,27 +3791,35 @@ void smb2_reconnect_server(struct work_struct *work)
{ {
struct TCP_Server_Info *server = container_of(work, struct TCP_Server_Info *server = container_of(work,
struct TCP_Server_Info, reconnect.work); struct TCP_Server_Info, reconnect.work);
struct cifs_ses *ses; struct TCP_Server_Info *pserver;
struct cifs_ses *ses, *ses2;
struct cifs_tcon *tcon, *tcon2; struct cifs_tcon *tcon, *tcon2;
struct list_head tmp_list; struct list_head tmp_list, tmp_ses_list;
int tcon_exist = false; bool tcon_exist = false, ses_exist = false;
bool tcon_selected = false, ses_selected = false;
int rc; int rc;
int resched = false; bool resched = false;
/* If server is a channel, select the primary channel */
pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server;
/* Prevent simultaneous reconnects that can corrupt tcon->rlist list */ /* Prevent simultaneous reconnects that can corrupt tcon->rlist list */
mutex_lock(&server->reconnect_mutex); mutex_lock(&pserver->reconnect_mutex);
INIT_LIST_HEAD(&tmp_list); INIT_LIST_HEAD(&tmp_list);
cifs_dbg(FYI, "Need negotiate, reconnecting tcons\n"); INIT_LIST_HEAD(&tmp_ses_list);
cifs_dbg(FYI, "Reconnecting tcons and channels\n");
spin_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
tcon_selected = ses_selected = false;
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
if (tcon->need_reconnect || tcon->need_reopen_files) { if (tcon->need_reconnect || tcon->need_reopen_files) {
tcon->tc_count++; tcon->tc_count++;
list_add_tail(&tcon->rlist, &tmp_list); list_add_tail(&tcon->rlist, &tmp_list);
tcon_exist = true; tcon_selected = tcon_exist = true;
} }
} }
/* /*
@ -3816,7 +3828,17 @@ void smb2_reconnect_server(struct work_struct *work)
*/ */
if (ses->tcon_ipc && ses->tcon_ipc->need_reconnect) { if (ses->tcon_ipc && ses->tcon_ipc->need_reconnect) {
list_add_tail(&ses->tcon_ipc->rlist, &tmp_list); list_add_tail(&ses->tcon_ipc->rlist, &tmp_list);
tcon_exist = true; tcon_selected = tcon_exist = true;
ses->ses_count++;
}
/*
* handle the case where channel needs to reconnect
* binding session, but tcon is healthy (some other channel
* is active)
*/
if (!tcon_selected && cifs_chan_needs_reconnect(ses, server)) {
list_add_tail(&ses->rlist, &tmp_ses_list);
ses_selected = ses_exist = true;
ses->ses_count++; ses->ses_count++;
} }
} }
@ -3824,7 +3846,7 @@ void smb2_reconnect_server(struct work_struct *work)
* Get the reference to server struct to be sure that the last call of * Get the reference to server struct to be sure that the last call of
* cifs_put_tcon() in the loop below won't release the server pointer. * cifs_put_tcon() in the loop below won't release the server pointer.
*/ */
if (tcon_exist) if (tcon_exist || ses_exist)
server->srv_count++; server->srv_count++;
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
@ -3842,13 +3864,41 @@ void smb2_reconnect_server(struct work_struct *work)
cifs_put_tcon(tcon); cifs_put_tcon(tcon);
} }
cifs_dbg(FYI, "Reconnecting tcons finished\n"); if (!ses_exist)
goto done;
/* allocate a dummy tcon struct used for reconnect */
tcon = kzalloc(sizeof(struct cifs_tcon), GFP_KERNEL);
if (!tcon) {
resched = true;
list_del_init(&ses->rlist);
cifs_put_smb_ses(ses);
goto done;
}
tcon->tidStatus = CifsGood;
tcon->retry = false;
tcon->need_reconnect = false;
/* now reconnect sessions for necessary channels */
list_for_each_entry_safe(ses, ses2, &tmp_ses_list, rlist) {
tcon->ses = ses;
rc = smb2_reconnect(SMB2_INTERNAL_CMD, tcon, server);
if (rc)
resched = true;
list_del_init(&ses->rlist);
cifs_put_smb_ses(ses);
}
kfree(tcon);
done:
cifs_dbg(FYI, "Reconnecting tcons and channels finished\n");
if (resched) if (resched)
queue_delayed_work(cifsiod_wq, &server->reconnect, 2 * HZ); queue_delayed_work(cifsiod_wq, &server->reconnect, 2 * HZ);
mutex_unlock(&server->reconnect_mutex); mutex_unlock(&pserver->reconnect_mutex);
/* now we can safely release srv struct */ /* now we can safely release srv struct */
if (tcon_exist) if (tcon_exist || ses_exist)
cifs_put_tcp_session(server, 1); cifs_put_tcp_session(server, 1);
} }