Skip to content

Commit 724244c

Browse files
sprasad-microsoftsmfrench
authored andcommitted
cifs: protect session channel fields with chan_lock
Introducing a new spin lock to protect all the channel related fields in a cifs_ses struct. This lock should be taken whenever dealing with the channel fields, and should be held only for very short intervals which will not sleep. Currently, all channel related fields in cifs_ses structure are protected by session_mutex. However, this mutex is held for long periods (sometimes while waiting for a reply from server). This makes the codepath quite tricky to change. Signed-off-by: Shyam Prasad N <[email protected]> Reviewed-by: Paulo Alcantara (SUSE) <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 8e07757 commit 724244c

File tree

6 files changed

+71
-15
lines changed

6 files changed

+71
-15
lines changed

fs/cifs/cifs_debug.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,12 +414,14 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
414414
from_kuid(&init_user_ns, ses->linux_uid),
415415
from_kuid(&init_user_ns, ses->cred_uid));
416416

417+
spin_lock(&ses->chan_lock);
417418
if (ses->chan_count > 1) {
418419
seq_printf(m, "\n\n\tExtra Channels: %zu ",
419420
ses->chan_count-1);
420421
for (j = 1; j < ses->chan_count; j++)
421422
cifs_dump_channel(m, j, &ses->chans[j]);
422423
}
424+
spin_unlock(&ses->chan_lock);
423425

424426
seq_puts(m, "\n\n\tShares: ");
425427
j = 0;

fs/cifs/cifsglob.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -952,16 +952,21 @@ struct cifs_ses {
952952
* iface_lock should be taken when accessing any of these fields
953953
*/
954954
spinlock_t iface_lock;
955+
/* ========= begin: protected by iface_lock ======== */
955956
struct cifs_server_iface *iface_list;
956957
size_t iface_count;
957958
unsigned long iface_last_update; /* jiffies */
959+
/* ========= end: protected by iface_lock ======== */
958960

961+
spinlock_t chan_lock;
962+
/* ========= begin: protected by chan_lock ======== */
959963
#define CIFS_MAX_CHANNELS 16
960964
struct cifs_chan chans[CIFS_MAX_CHANNELS];
961965
struct cifs_chan *binding_chan;
962966
size_t chan_count;
963967
size_t chan_max;
964968
atomic_t chan_seq; /* round robin state */
969+
/* ========= end: protected by chan_lock ======== */
965970
};
966971

967972
/*

fs/cifs/connect.c

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1577,8 +1577,12 @@ static int match_session(struct cifs_ses *ses, struct smb3_fs_context *ctx)
15771577
* If an existing session is limited to less channels than
15781578
* requested, it should not be reused
15791579
*/
1580-
if (ses->chan_max < ctx->max_channels)
1580+
spin_lock(&ses->chan_lock);
1581+
if (ses->chan_max < ctx->max_channels) {
1582+
spin_unlock(&ses->chan_lock);
15811583
return 0;
1584+
}
1585+
spin_unlock(&ses->chan_lock);
15821586

15831587
switch (ses->sectype) {
15841588
case Kerberos:
@@ -1713,6 +1717,7 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
17131717
void cifs_put_smb_ses(struct cifs_ses *ses)
17141718
{
17151719
unsigned int rc, xid;
1720+
unsigned int chan_count;
17161721
struct TCP_Server_Info *server = ses->server;
17171722
cifs_dbg(FYI, "%s: ses_count=%d\n", __func__, ses->ses_count);
17181723

@@ -1754,12 +1759,24 @@ void cifs_put_smb_ses(struct cifs_ses *ses)
17541759
list_del_init(&ses->smb_ses_list);
17551760
spin_unlock(&cifs_tcp_ses_lock);
17561761

1762+
spin_lock(&ses->chan_lock);
1763+
chan_count = ses->chan_count;
1764+
spin_unlock(&ses->chan_lock);
1765+
17571766
/* close any extra channels */
1758-
if (ses->chan_count > 1) {
1767+
if (chan_count > 1) {
17591768
int i;
17601769

1761-
for (i = 1; i < ses->chan_count; i++)
1770+
for (i = 1; i < chan_count; i++) {
1771+
/*
1772+
* note: for now, we're okay accessing ses->chans
1773+
* without chan_lock. But when chans can go away, we'll
1774+
* need to introduce ref counting to make sure that chan
1775+
* is not freed from under us.
1776+
*/
17621777
cifs_put_tcp_session(ses->chans[i].server, 0);
1778+
ses->chans[i].server = NULL;
1779+
}
17631780
}
17641781

17651782
sesInfoFree(ses);
@@ -2018,9 +2035,11 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
20182035
mutex_lock(&ses->session_mutex);
20192036

20202037
/* add server as first channel */
2038+
spin_lock(&ses->chan_lock);
20212039
ses->chans[0].server = server;
20222040
ses->chan_count = 1;
20232041
ses->chan_max = ctx->multichannel ? ctx->max_channels:1;
2042+
spin_unlock(&ses->chan_lock);
20242043

20252044
rc = cifs_negotiate_protocol(xid, ses);
20262045
if (!rc)

fs/cifs/misc.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ sesInfoAlloc(void)
7575
INIT_LIST_HEAD(&ret_buf->tcon_list);
7676
mutex_init(&ret_buf->session_mutex);
7777
spin_lock_init(&ret_buf->iface_lock);
78+
spin_lock_init(&ret_buf->chan_lock);
7879
}
7980
return ret_buf;
8081
}

fs/cifs/sess.c

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -54,41 +54,53 @@ bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface)
5454
{
5555
int i;
5656

57+
spin_lock(&ses->chan_lock);
5758
for (i = 0; i < ses->chan_count; i++) {
58-
if (is_server_using_iface(ses->chans[i].server, iface))
59+
if (is_server_using_iface(ses->chans[i].server, iface)) {
60+
spin_unlock(&ses->chan_lock);
5961
return true;
62+
}
6063
}
64+
spin_unlock(&ses->chan_lock);
6165
return false;
6266
}
6367

6468
/* returns number of channels added */
6569
int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
6670
{
67-
int old_chan_count = ses->chan_count;
68-
int left = ses->chan_max - ses->chan_count;
71+
int old_chan_count, new_chan_count;
72+
int left;
6973
int i = 0;
7074
int rc = 0;
7175
int tries = 0;
7276
struct cifs_server_iface *ifaces = NULL;
7377
size_t iface_count;
7478

79+
if (ses->server->dialect < SMB30_PROT_ID) {
80+
cifs_dbg(VFS, "multichannel is not supported on this protocol version, use 3.0 or above\n");
81+
return 0;
82+
}
83+
84+
spin_lock(&ses->chan_lock);
85+
86+
new_chan_count = old_chan_count = ses->chan_count;
87+
left = ses->chan_max - ses->chan_count;
88+
7589
if (left <= 0) {
7690
cifs_dbg(FYI,
7791
"ses already at max_channels (%zu), nothing to open\n",
7892
ses->chan_max);
79-
return 0;
80-
}
81-
82-
if (ses->server->dialect < SMB30_PROT_ID) {
83-
cifs_dbg(VFS, "multichannel is not supported on this protocol version, use 3.0 or above\n");
93+
spin_unlock(&ses->chan_lock);
8494
return 0;
8595
}
8696

8797
if (!(ses->server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) {
8898
cifs_dbg(VFS, "server %s does not support multichannel\n", ses->server->hostname);
8999
ses->chan_max = 1;
100+
spin_unlock(&ses->chan_lock);
90101
return 0;
91102
}
103+
spin_unlock(&ses->chan_lock);
92104

93105
/*
94106
* Make a copy of the iface list at the time and use that
@@ -142,10 +154,11 @@ int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
142154
cifs_dbg(FYI, "successfully opened new channel on iface#%d\n",
143155
i);
144156
left--;
157+
new_chan_count++;
145158
}
146159

147160
kfree(ifaces);
148-
return ses->chan_count - old_chan_count;
161+
return new_chan_count - old_chan_count;
149162
}
150163

151164
/*
@@ -157,17 +170,22 @@ cifs_ses_find_chan(struct cifs_ses *ses, struct TCP_Server_Info *server)
157170
{
158171
int i;
159172

173+
spin_lock(&ses->chan_lock);
160174
for (i = 0; i < ses->chan_count; i++) {
161-
if (ses->chans[i].server == server)
175+
if (ses->chans[i].server == server) {
176+
spin_unlock(&ses->chan_lock);
162177
return &ses->chans[i];
178+
}
163179
}
180+
spin_unlock(&ses->chan_lock);
164181
return NULL;
165182
}
166183

167184
static int
168185
cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
169186
struct cifs_server_iface *iface)
170187
{
188+
struct TCP_Server_Info *chan_server;
171189
struct cifs_chan *chan;
172190
struct smb3_fs_context ctx = {NULL};
173191
static const char unc_fmt[] = "\\%s\\foo";
@@ -240,15 +258,20 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
240258
SMB2_CLIENT_GUID_SIZE);
241259
ctx.use_client_guid = true;
242260

243-
mutex_lock(&ses->session_mutex);
261+
chan_server = cifs_get_tcp_session(&ctx);
244262

263+
mutex_lock(&ses->session_mutex);
264+
spin_lock(&ses->chan_lock);
245265
chan = ses->binding_chan = &ses->chans[ses->chan_count];
246-
chan->server = cifs_get_tcp_session(&ctx);
266+
chan->server = chan_server;
247267
if (IS_ERR(chan->server)) {
248268
rc = PTR_ERR(chan->server);
249269
chan->server = NULL;
270+
spin_unlock(&ses->chan_lock);
250271
goto out;
251272
}
273+
spin_unlock(&ses->chan_lock);
274+
252275
spin_lock(&cifs_tcp_ses_lock);
253276
chan->server->is_channel = true;
254277
spin_unlock(&cifs_tcp_ses_lock);
@@ -283,8 +306,11 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
283306
* ses to the new server.
284307
*/
285308

309+
spin_lock(&ses->chan_lock);
286310
ses->chan_count++;
287311
atomic_set(&ses->chan_seq, 0);
312+
spin_unlock(&ses->chan_lock);
313+
288314
out:
289315
ses->binding = false;
290316
ses->binding_chan = NULL;

fs/cifs/transport.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,14 +1044,17 @@ struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses)
10441044
if (!ses)
10451045
return NULL;
10461046

1047+
spin_lock(&ses->chan_lock);
10471048
if (!ses->binding) {
10481049
/* round robin */
10491050
if (ses->chan_count > 1) {
10501051
index = (uint)atomic_inc_return(&ses->chan_seq);
10511052
index %= ses->chan_count;
10521053
}
1054+
spin_unlock(&ses->chan_lock);
10531055
return ses->chans[index].server;
10541056
} else {
1057+
spin_unlock(&ses->chan_lock);
10551058
return cifs_ses_server(ses);
10561059
}
10571060
}

0 commit comments

Comments
 (0)