Skip to content

Commit 8da33fd

Browse files
sprasad-microsoftSteve French
authored andcommitted
cifs: avoid deadlocks while updating iface
We use cifs_tcp_ses_lock to protect a lot of things. Not only does it protect the lists of connections, sessions, tree connects, open file lists, etc., we also use it to protect some fields in each of it's entries. In this case, cifs_mark_ses_for_reconnect takes the cifs_tcp_ses_lock to traverse the lists, and then calls cifs_update_iface. However, that can end up calling cifs_put_tcp_session, which picks up the same lock again. Avoid this by taking a ref for the session, drop the lock, and then call update iface. Also, in cifs_update_iface, avoid nested locking of iface_lock and chan_lock, as much as possible. When unavoidable, we need to pick iface_lock first. Signed-off-by: Shyam Prasad N <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 6e1c1c0 commit 8da33fd

File tree

2 files changed

+33
-15
lines changed

2 files changed

+33
-15
lines changed

fs/cifs/connect.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server,
236236
bool mark_smb_session)
237237
{
238238
struct TCP_Server_Info *pserver;
239-
struct cifs_ses *ses;
239+
struct cifs_ses *ses, *nses;
240240
struct cifs_tcon *tcon;
241241

242242
/*
@@ -250,10 +250,19 @@ cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server,
250250

251251

252252
spin_lock(&cifs_tcp_ses_lock);
253-
list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
253+
list_for_each_entry_safe(ses, nses, &pserver->smb_ses_list, smb_ses_list) {
254254
/* check if iface is still active */
255-
if (!cifs_chan_is_iface_active(ses, server))
255+
if (!cifs_chan_is_iface_active(ses, server)) {
256+
/*
257+
* HACK: drop the lock before calling
258+
* cifs_chan_update_iface to avoid deadlock
259+
*/
260+
ses->ses_count++;
261+
spin_unlock(&cifs_tcp_ses_lock);
256262
cifs_chan_update_iface(ses, server);
263+
spin_lock(&cifs_tcp_ses_lock);
264+
ses->ses_count--;
265+
}
257266

258267
spin_lock(&ses->chan_lock);
259268
if (!mark_smb_session && cifs_chan_needs_reconnect(ses, server))

fs/cifs/sess.c

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -256,29 +256,34 @@ int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
256256

257257
/*
258258
* update the iface for the channel if necessary.
259-
* will return 0 when iface is updated. 1 otherwise
259+
* will return 0 when iface is updated, 1 if removed, 2 otherwise
260260
* Must be called with chan_lock held.
261261
*/
262262
int
263263
cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
264264
{
265-
unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
265+
unsigned int chan_index;
266266
struct cifs_server_iface *iface = NULL;
267267
struct cifs_server_iface *old_iface = NULL;
268268
int rc = 0;
269269

270-
/* primary channel. This can never go away */
271-
if (!chan_index)
270+
spin_lock(&ses->chan_lock);
271+
chan_index = cifs_ses_get_chan_index(ses, server);
272+
if (!chan_index) {
273+
spin_unlock(&ses->chan_lock);
272274
return 0;
275+
}
273276

274277
if (ses->chans[chan_index].iface) {
275278
old_iface = ses->chans[chan_index].iface;
276-
if (old_iface->is_active)
279+
if (old_iface->is_active) {
280+
spin_unlock(&ses->chan_lock);
277281
return 1;
282+
}
278283
}
284+
spin_unlock(&ses->chan_lock);
279285

280286
spin_lock(&ses->iface_lock);
281-
282287
/* then look for a new one */
283288
list_for_each_entry(iface, &ses->iface_list, iface_head) {
284289
if (!iface->is_active ||
@@ -295,8 +300,6 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
295300
cifs_dbg(FYI, "unable to find a suitable iface\n");
296301
}
297302

298-
ses->chans[chan_index].iface = iface;
299-
300303
/* now drop the ref to the current iface */
301304
if (old_iface && iface) {
302305
kref_put(&old_iface->refcount, release_iface);
@@ -311,14 +314,20 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
311314
WARN_ON(!iface);
312315
cifs_dbg(FYI, "adding new iface: %pIS\n", &iface->sockaddr);
313316
}
314-
315317
spin_unlock(&ses->iface_lock);
316318

319+
spin_lock(&ses->chan_lock);
320+
chan_index = cifs_ses_get_chan_index(ses, server);
321+
ses->chans[chan_index].iface = iface;
322+
317323
/* No iface is found. if secondary chan, drop connection */
318-
if (!iface && CIFS_SERVER_IS_CHAN(server)) {
319-
cifs_put_tcp_session(server, false);
324+
if (!iface && CIFS_SERVER_IS_CHAN(server))
320325
ses->chans[chan_index].server = NULL;
321-
}
326+
327+
spin_unlock(&ses->chan_lock);
328+
329+
if (!iface && CIFS_SERVER_IS_CHAN(server))
330+
cifs_put_tcp_session(server, false);
322331

323332
return rc;
324333
}

0 commit comments

Comments
 (0)