Skip to content

Commit d1a931c

Browse files
sprasad-microsoftsmfrench
authored andcommitted
cifs: track individual channel status using chans_need_reconnect
We needed a way to identify the channels under the smb session which are in reconnect, so that the traffic to other channels can continue. So I replaced the bool need_reconnect with a bitmask identifying all the channels that need reconnection (named chans_need_reconnect). When a channel needs reconnection, the bit corresponding to the index of the server in ses->chans is used to set this bitmask. Checking if no channels or all the channels need reconnect then becomes very easy. Also wrote some helper macros for checking and setting the bits. Signed-off-by: Shyam Prasad N <sprasad@microsoft.com> Signed-off-by: Steve French <stfrench@microsoft.com>
1 parent 0b66fa7 commit d1a931c

File tree

6 files changed

+211
-24
lines changed

6 files changed

+211
-24
lines changed

fs/cifs/cifsglob.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -939,7 +939,6 @@ struct cifs_ses {
939939
struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */
940940
enum securityEnum sectype; /* what security flavor was specified? */
941941
bool sign; /* is signing required? */
942-
bool need_reconnect:1; /* connection reset, uid now invalid */
943942
bool domainAuto:1;
944943
bool binding:1; /* are we binding the session? */
945944
__u16 session_flags;
@@ -969,11 +968,25 @@ struct cifs_ses {
969968
spinlock_t chan_lock;
970969
/* ========= begin: protected by chan_lock ======== */
971970
#define CIFS_MAX_CHANNELS 16
971+
#define CIFS_ALL_CHANNELS_SET(ses) \
972+
((1UL << (ses)->chan_count) - 1)
973+
#define CIFS_ALL_CHANS_NEED_RECONNECT(ses) \
974+
((ses)->chans_need_reconnect == CIFS_ALL_CHANNELS_SET(ses))
975+
#define CIFS_CHAN_NEEDS_RECONNECT(ses, index) \
976+
test_bit((index), &(ses)->chans_need_reconnect)
977+
972978
struct cifs_chan chans[CIFS_MAX_CHANNELS];
973979
struct cifs_chan *binding_chan;
974980
size_t chan_count;
975981
size_t chan_max;
976982
atomic_t chan_seq; /* round robin state */
983+
984+
/*
985+
* chans_need_reconnect is a bitmap indicating which of the channels
986+
* under this smb session needs to be reconnected.
987+
* If not multichannel session, only one bit will be used.
988+
*/
989+
unsigned long chans_need_reconnect;
977990
/* ========= end: protected by chan_lock ======== */
978991
};
979992

fs/cifs/cifsproto.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,19 @@ bool is_server_using_iface(struct TCP_Server_Info *server,
601601
bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface);
602602
void cifs_ses_mark_for_reconnect(struct cifs_ses *ses);
603603

604+
unsigned int
605+
cifs_ses_get_chan_index(struct cifs_ses *ses,
606+
struct TCP_Server_Info *server);
607+
void
608+
cifs_chan_set_need_reconnect(struct cifs_ses *ses,
609+
struct TCP_Server_Info *server);
610+
void
611+
cifs_chan_clear_need_reconnect(struct cifs_ses *ses,
612+
struct TCP_Server_Info *server);
613+
bool
614+
cifs_chan_needs_reconnect(struct cifs_ses *ses,
615+
struct TCP_Server_Info *server);
616+
604617
void extract_unc_hostname(const char *unc, const char **h, size_t *len);
605618
int copy_path_name(char *dst, const char *src);
606619
int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov,

fs/cifs/cifssmb.c

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,12 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
166166
retries = server->nr_targets;
167167
}
168168

169-
if (!ses->need_reconnect && !tcon->need_reconnect)
169+
spin_lock(&ses->chan_lock);
170+
if (!cifs_chan_needs_reconnect(ses, server) && !tcon->need_reconnect) {
171+
spin_unlock(&ses->chan_lock);
170172
return 0;
173+
}
174+
spin_unlock(&ses->chan_lock);
171175

172176
nls_codepage = load_nls_default();
173177

@@ -188,8 +192,25 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
188192
goto out;
189193
}
190194

195+
/*
196+
* need to prevent multiple threads trying to simultaneously
197+
* reconnect the same SMB session
198+
*/
199+
spin_lock(&ses->chan_lock);
200+
if (!cifs_chan_needs_reconnect(ses, server)) {
201+
spin_unlock(&ses->chan_lock);
202+
/* this just means that we only need to tcon */
203+
if (tcon->need_reconnect)
204+
goto skip_sess_setup;
205+
206+
rc = -EHOSTDOWN;
207+
mutex_unlock(&ses->session_mutex);
208+
goto out;
209+
}
210+
spin_unlock(&ses->chan_lock);
211+
191212
rc = cifs_negotiate_protocol(0, ses);
192-
if (rc == 0 && ses->need_reconnect)
213+
if (!rc)
193214
rc = cifs_setup_session(0, ses, nls_codepage);
194215

195216
/* do we need to reconnect tcon? */
@@ -198,6 +219,7 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
198219
goto out;
199220
}
200221

222+
skip_sess_setup:
201223
cifs_mark_open_files_invalid(tcon);
202224
rc = cifs_tree_connect(0, tcon, nls_codepage);
203225
mutex_unlock(&ses->session_mutex);
@@ -337,8 +359,13 @@ static int
337359
smb_init_no_reconnect(int smb_command, int wct, struct cifs_tcon *tcon,
338360
void **request_buf, void **response_buf)
339361
{
340-
if (tcon->ses->need_reconnect || tcon->need_reconnect)
362+
spin_lock(&tcon->ses->chan_lock);
363+
if (cifs_chan_needs_reconnect(tcon->ses, tcon->ses->server) ||
364+
tcon->need_reconnect) {
365+
spin_unlock(&tcon->ses->chan_lock);
341366
return -EHOSTDOWN;
367+
}
368+
spin_unlock(&tcon->ses->chan_lock);
342369

343370
return __smb_init(smb_command, wct, tcon, request_buf, response_buf);
344371
}
@@ -600,8 +627,12 @@ CIFSSMBTDis(const unsigned int xid, struct cifs_tcon *tcon)
600627
* the tcon is no longer on the list, so no need to take lock before
601628
* checking this.
602629
*/
603-
if ((tcon->need_reconnect) || (tcon->ses->need_reconnect))
604-
return 0;
630+
spin_lock(&tcon->ses->chan_lock);
631+
if ((tcon->need_reconnect) || CIFS_ALL_CHANS_NEED_RECONNECT(tcon->ses)) {
632+
spin_unlock(&tcon->ses->chan_lock);
633+
return -EIO;
634+
}
635+
spin_unlock(&tcon->ses->chan_lock);
605636

606637
rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon,
607638
(void **)&smb_buffer);
@@ -696,9 +727,14 @@ CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses)
696727
return -EIO;
697728

698729
mutex_lock(&ses->session_mutex);
699-
if (ses->need_reconnect)
730+
spin_lock(&ses->chan_lock);
731+
if (CIFS_ALL_CHANS_NEED_RECONNECT(ses)) {
732+
spin_unlock(&ses->chan_lock);
700733
goto session_already_dead; /* no need to send SMBlogoff if uid
701734
already closed due to reconnect */
735+
}
736+
spin_unlock(&ses->chan_lock);
737+
702738
rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB);
703739
if (rc) {
704740
mutex_unlock(&ses->session_mutex);

fs/cifs/connect.c

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -191,11 +191,23 @@ static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server
191191

192192
spin_lock(&cifs_tcp_ses_lock);
193193
list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
194-
ses->need_reconnect = true;
194+
spin_lock(&ses->chan_lock);
195+
if (cifs_chan_needs_reconnect(ses, server))
196+
goto next_session;
197+
198+
cifs_chan_set_need_reconnect(ses, server);
199+
200+
/* If all channels need reconnect, then tcon needs reconnect */
201+
if (!CIFS_ALL_CHANS_NEED_RECONNECT(ses))
202+
goto next_session;
203+
195204
list_for_each_entry(tcon, &ses->tcon_list, tcon_list)
196205
tcon->need_reconnect = true;
197206
if (ses->tcon_ipc)
198207
ses->tcon_ipc->need_reconnect = true;
208+
209+
next_session:
210+
spin_unlock(&ses->chan_lock);
199211
}
200212
spin_unlock(&cifs_tcp_ses_lock);
201213

@@ -1988,7 +2000,9 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
19882000
ses->status);
19892001

19902002
mutex_lock(&ses->session_mutex);
1991-
if (ses->need_reconnect) {
2003+
spin_lock(&ses->chan_lock);
2004+
if (cifs_chan_needs_reconnect(ses, server)) {
2005+
spin_unlock(&ses->chan_lock);
19922006
cifs_dbg(FYI, "Session needs reconnect\n");
19932007

19942008
rc = cifs_negotiate_protocol(xid, ses);
@@ -2009,7 +2023,9 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
20092023
free_xid(xid);
20102024
return ERR_PTR(rc);
20112025
}
2026+
spin_lock(&ses->chan_lock);
20122027
}
2028+
spin_unlock(&ses->chan_lock);
20132029
mutex_unlock(&ses->session_mutex);
20142030

20152031
/* existing SMB ses has a server reference already */
@@ -2067,6 +2083,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
20672083
ses->chans[0].server = server;
20682084
ses->chan_count = 1;
20692085
ses->chan_max = ctx->multichannel ? ctx->max_channels:1;
2086+
ses->chans_need_reconnect = 1;
20702087
spin_unlock(&ses->chan_lock);
20712088

20722089
rc = cifs_negotiate_protocol(xid, ses);
@@ -2081,7 +2098,11 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
20812098
if (rc)
20822099
goto get_ses_fail;
20832100

2084-
/* success, put it on the list and add it as first channel */
2101+
/*
2102+
* success, put it on the list and add it as first channel
2103+
* note: the session becomes active soon after this. So you'll
2104+
* need to lock before changing something in the session.
2105+
*/
20852106
spin_lock(&cifs_tcp_ses_lock);
20862107
list_add(&ses->smb_ses_list, &server->smb_ses_list);
20872108
spin_unlock(&cifs_tcp_ses_lock);
@@ -2161,6 +2182,9 @@ cifs_put_tcon(struct cifs_tcon *tcon)
21612182
/* tc_count can never go negative */
21622183
WARN_ON(tcon->tc_count < 0);
21632184

2185+
list_del_init(&tcon->tcon_list);
2186+
spin_unlock(&cifs_tcp_ses_lock);
2187+
21642188
if (tcon->use_witness) {
21652189
int rc;
21662190

@@ -2171,9 +2195,6 @@ cifs_put_tcon(struct cifs_tcon *tcon)
21712195
}
21722196
}
21732197

2174-
list_del_init(&tcon->tcon_list);
2175-
spin_unlock(&cifs_tcp_ses_lock);
2176-
21772198
xid = get_xid();
21782199
if (ses->server->ops->tree_disconnect)
21792200
ses->server->ops->tree_disconnect(xid, tcon);

fs/cifs/sess.c

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,53 @@ bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface)
6565
return false;
6666
}
6767

68+
unsigned int
69+
cifs_ses_get_chan_index(struct cifs_ses *ses,
70+
struct TCP_Server_Info *server)
71+
{
72+
unsigned int i;
73+
74+
for (i = 0; i < ses->chan_count; i++) {
75+
if (ses->chans[i].server == server)
76+
return i;
77+
}
78+
79+
/* If we didn't find the channel, it is likely a bug */
80+
WARN_ON(1);
81+
return 0;
82+
}
83+
84+
void
85+
cifs_chan_set_need_reconnect(struct cifs_ses *ses,
86+
struct TCP_Server_Info *server)
87+
{
88+
unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
89+
90+
set_bit(chan_index, &ses->chans_need_reconnect);
91+
cifs_dbg(FYI, "Set reconnect bitmask for chan %u; now 0x%lx\n",
92+
chan_index, ses->chans_need_reconnect);
93+
}
94+
95+
void
96+
cifs_chan_clear_need_reconnect(struct cifs_ses *ses,
97+
struct TCP_Server_Info *server)
98+
{
99+
unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
100+
101+
clear_bit(chan_index, &ses->chans_need_reconnect);
102+
cifs_dbg(FYI, "Cleared reconnect bitmask for chan %u; now 0x%lx\n",
103+
chan_index, ses->chans_need_reconnect);
104+
}
105+
106+
bool
107+
cifs_chan_needs_reconnect(struct cifs_ses *ses,
108+
struct TCP_Server_Info *server)
109+
{
110+
unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
111+
112+
return CIFS_CHAN_NEEDS_RECONNECT(ses, chan_index);
113+
}
114+
68115
/* returns number of channels added */
69116
int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
70117
{
@@ -306,11 +353,21 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
306353
spin_lock(&ses->chan_lock);
307354
ses->chan_count++;
308355
atomic_set(&ses->chan_seq, 0);
356+
357+
/* Mark this channel as needing connect/setup */
358+
cifs_chan_set_need_reconnect(ses, chan->server);
309359
spin_unlock(&ses->chan_lock);
310360

311361
out:
312362
ses->binding = false;
313363
ses->binding_chan = NULL;
364+
365+
if (rc && chan->server) {
366+
/* we rely on all bits beyond chan_count to be clear */
367+
cifs_chan_clear_need_reconnect(ses, chan->server);
368+
ses->chan_count--;
369+
}
370+
314371
mutex_unlock(&ses->session_mutex);
315372

316373
if (rc && chan->server)
@@ -998,9 +1055,15 @@ sess_establish_session(struct sess_data *sess_data)
9981055
mutex_unlock(&ses->server->srv_mutex);
9991056

10001057
cifs_dbg(FYI, "CIFS session established successfully\n");
1058+
if (ses->binding)
1059+
cifs_chan_clear_need_reconnect(ses, ses->binding_chan->server);
1060+
else
1061+
cifs_chan_clear_need_reconnect(ses, ses->server);
1062+
1063+
/* keep existing ses state if binding */
10011064
spin_lock(&GlobalMid_Lock);
1002-
ses->status = CifsGood;
1003-
ses->need_reconnect = false;
1065+
if (!ses->binding)
1066+
ses->status = CifsGood;
10041067
spin_unlock(&GlobalMid_Lock);
10051068

10061069
return 0;

0 commit comments

Comments
 (0)