Skip to content

Commit 3663c90

Browse files
sprasad-microsoftsmfrench
authored andcommitted
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 <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent e4e2787 commit 3663c90

File tree

3 files changed

+99
-19
lines changed

3 files changed

+99
-19
lines changed

fs/cifs/cifsglob.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ enum statusEnum {
117117
CifsInSessSetup,
118118
CifsNeedTcon,
119119
CifsInTcon,
120+
CifsNeedFilesInvalidate,
120121
CifsInFilesInvalidate
121122
};
122123

@@ -923,6 +924,7 @@ struct cifs_chan {
923924
*/
924925
struct cifs_ses {
925926
struct list_head smb_ses_list;
927+
struct list_head rlist; /* reconnect list */
926928
struct list_head tcon_list;
927929
struct cifs_tcon *tcon_ipc;
928930
struct mutex session_mutex;

fs/cifs/connect.c

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ static int __cifs_reconnect(struct TCP_Server_Info *server,
335335
spin_unlock(&cifs_tcp_ses_lock);
336336
cifs_swn_reset_server_dstaddr(server);
337337
mutex_unlock(&server->srv_mutex);
338+
mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
338339
}
339340
} while (server->tcpStatus == CifsNeedReconnect);
340341

@@ -4404,9 +4405,22 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
44044405
char *tree;
44054406
struct dfs_info3_param ref = {0};
44064407

4408+
/* only send once per connect */
4409+
spin_lock(&cifs_tcp_ses_lock);
4410+
if (tcon->ses->status != CifsGood ||
4411+
(tcon->tidStatus != CifsNew &&
4412+
tcon->tidStatus != CifsNeedTcon)) {
4413+
spin_unlock(&cifs_tcp_ses_lock);
4414+
return 0;
4415+
}
4416+
tcon->tidStatus = CifsInTcon;
4417+
spin_unlock(&cifs_tcp_ses_lock);
4418+
44074419
tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL);
4408-
if (!tree)
4409-
return -ENOMEM;
4420+
if (!tree) {
4421+
rc = -ENOMEM;
4422+
goto out;
4423+
}
44104424

44114425
if (tcon->ipc) {
44124426
scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);
@@ -4438,11 +4452,18 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
44384452
kfree(tree);
44394453
cifs_put_tcp_super(sb);
44404454

4455+
if (rc) {
4456+
spin_lock(&cifs_tcp_ses_lock);
4457+
tcon->tidStatus = CifsNeedTcon;
4458+
spin_unlock(&cifs_tcp_ses_lock);
4459+
}
4460+
44414461
return rc;
44424462
}
44434463
#else
44444464
int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc)
44454465
{
4466+
int rc;
44464467
const struct smb_version_operations *ops = tcon->ses->server->ops;
44474468

44484469
/* only send once per connect */
@@ -4456,6 +4477,13 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
44564477
tcon->tidStatus = CifsInTcon;
44574478
spin_unlock(&cifs_tcp_ses_lock);
44584479

4459-
return ops->tree_connect(xid, tcon->ses, tcon->treeName, tcon, nlsc);
4480+
rc = ops->tree_connect(xid, tcon->ses, tcon->treeName, tcon, nlsc);
4481+
if (rc) {
4482+
spin_lock(&cifs_tcp_ses_lock);
4483+
tcon->tidStatus = CifsNeedTcon;
4484+
spin_unlock(&cifs_tcp_ses_lock);
4485+
}
4486+
4487+
return rc;
44604488
}
44614489
#endif

fs/cifs/smb2pdu.c

Lines changed: 66 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -289,14 +289,18 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
289289
rc = -EHOSTDOWN;
290290
goto failed;
291291
}
292-
}
293-
294-
if (rc || !tcon->need_reconnect) {
292+
} else {
295293
mutex_unlock(&ses->session_mutex);
296294
goto out;
297295
}
296+
mutex_unlock(&ses->session_mutex);
298297

299298
skip_sess_setup:
299+
mutex_lock(&ses->session_mutex);
300+
if (!tcon->need_reconnect) {
301+
mutex_unlock(&ses->session_mutex);
302+
goto out;
303+
}
300304
cifs_mark_open_files_invalid(tcon);
301305
if (tcon->use_persistent)
302306
tcon->need_reopen_files = true;
@@ -3787,27 +3791,35 @@ void smb2_reconnect_server(struct work_struct *work)
37873791
{
37883792
struct TCP_Server_Info *server = container_of(work,
37893793
struct TCP_Server_Info, reconnect.work);
3790-
struct cifs_ses *ses;
3794+
struct TCP_Server_Info *pserver;
3795+
struct cifs_ses *ses, *ses2;
37913796
struct cifs_tcon *tcon, *tcon2;
3792-
struct list_head tmp_list;
3793-
int tcon_exist = false;
3797+
struct list_head tmp_list, tmp_ses_list;
3798+
bool tcon_exist = false, ses_exist = false;
3799+
bool tcon_selected = false, ses_selected = false;
37943800
int rc;
3795-
int resched = false;
3801+
bool resched = false;
37963802

3803+
/* If server is a channel, select the primary channel */
3804+
pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server;
37973805

37983806
/* Prevent simultaneous reconnects that can corrupt tcon->rlist list */
3799-
mutex_lock(&server->reconnect_mutex);
3807+
mutex_lock(&pserver->reconnect_mutex);
38003808

38013809
INIT_LIST_HEAD(&tmp_list);
3802-
cifs_dbg(FYI, "Need negotiate, reconnecting tcons\n");
3810+
INIT_LIST_HEAD(&tmp_ses_list);
3811+
cifs_dbg(FYI, "Reconnecting tcons and channels\n");
38033812

38043813
spin_lock(&cifs_tcp_ses_lock);
3805-
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
3814+
list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
3815+
3816+
tcon_selected = ses_selected = false;
3817+
38063818
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
38073819
if (tcon->need_reconnect || tcon->need_reopen_files) {
38083820
tcon->tc_count++;
38093821
list_add_tail(&tcon->rlist, &tmp_list);
3810-
tcon_exist = true;
3822+
tcon_selected = tcon_exist = true;
38113823
}
38123824
}
38133825
/*
@@ -3816,15 +3828,25 @@ void smb2_reconnect_server(struct work_struct *work)
38163828
*/
38173829
if (ses->tcon_ipc && ses->tcon_ipc->need_reconnect) {
38183830
list_add_tail(&ses->tcon_ipc->rlist, &tmp_list);
3819-
tcon_exist = true;
3831+
tcon_selected = tcon_exist = true;
3832+
ses->ses_count++;
3833+
}
3834+
/*
3835+
* handle the case where channel needs to reconnect
3836+
* binding session, but tcon is healthy (some other channel
3837+
* is active)
3838+
*/
3839+
if (!tcon_selected && cifs_chan_needs_reconnect(ses, server)) {
3840+
list_add_tail(&ses->rlist, &tmp_ses_list);
3841+
ses_selected = ses_exist = true;
38203842
ses->ses_count++;
38213843
}
38223844
}
38233845
/*
38243846
* Get the reference to server struct to be sure that the last call of
38253847
* cifs_put_tcon() in the loop below won't release the server pointer.
38263848
*/
3827-
if (tcon_exist)
3849+
if (tcon_exist || ses_exist)
38283850
server->srv_count++;
38293851

38303852
spin_unlock(&cifs_tcp_ses_lock);
@@ -3842,13 +3864,41 @@ void smb2_reconnect_server(struct work_struct *work)
38423864
cifs_put_tcon(tcon);
38433865
}
38443866

3845-
cifs_dbg(FYI, "Reconnecting tcons finished\n");
3867+
if (!ses_exist)
3868+
goto done;
3869+
3870+
/* allocate a dummy tcon struct used for reconnect */
3871+
tcon = kzalloc(sizeof(struct cifs_tcon), GFP_KERNEL);
3872+
if (!tcon) {
3873+
resched = true;
3874+
list_del_init(&ses->rlist);
3875+
cifs_put_smb_ses(ses);
3876+
goto done;
3877+
}
3878+
3879+
tcon->tidStatus = CifsGood;
3880+
tcon->retry = false;
3881+
tcon->need_reconnect = false;
3882+
3883+
/* now reconnect sessions for necessary channels */
3884+
list_for_each_entry_safe(ses, ses2, &tmp_ses_list, rlist) {
3885+
tcon->ses = ses;
3886+
rc = smb2_reconnect(SMB2_INTERNAL_CMD, tcon, server);
3887+
if (rc)
3888+
resched = true;
3889+
list_del_init(&ses->rlist);
3890+
cifs_put_smb_ses(ses);
3891+
}
3892+
kfree(tcon);
3893+
3894+
done:
3895+
cifs_dbg(FYI, "Reconnecting tcons and channels finished\n");
38463896
if (resched)
38473897
queue_delayed_work(cifsiod_wq, &server->reconnect, 2 * HZ);
3848-
mutex_unlock(&server->reconnect_mutex);
3898+
mutex_unlock(&pserver->reconnect_mutex);
38493899

38503900
/* now we can safely release srv struct */
3851-
if (tcon_exist)
3901+
if (tcon_exist || ses_exist)
38523902
cifs_put_tcp_session(server, 1);
38533903
}
38543904

0 commit comments

Comments
 (0)