Skip to content

Commit 5eef12c

Browse files
sprasad-microsoftsmfrench
authored andcommitted
cifs: fix lock ordering while disabling multichannel
The code to handle the case of server disabling multichannel was picking iface_lock with chan_lock held. This goes against the lock ordering rules, as iface_lock is a higher order lock (even if it isn't so obvious). This change fixes the lock ordering by doing the following in that order for each secondary channel: 1. store iface and server pointers in local variable 2. remove references to iface and server in channels 3. unlock chan_lock 4. lock iface_lock 5. dec ref count for iface 6. unlock iface_lock 7. dec ref count for server 8. lock chan_lock again Since this function can only be called in smb2_reconnect, and that cannot be called by two parallel processes, we should not have races due to dropping chan_lock between steps 3 and 8. Fixes: ee1d217 ("cifs: handle when server stops supporting multichannel") Reported-by: Paulo Alcantara <[email protected]> Signed-off-by: Shyam Prasad N <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 29954d5 commit 5eef12c

File tree

1 file changed

+13
-9
lines changed

1 file changed

+13
-9
lines changed

fs/smb/client/sess.c

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -322,28 +322,32 @@ cifs_disable_secondary_channels(struct cifs_ses *ses)
322322
iface = ses->chans[i].iface;
323323
server = ses->chans[i].server;
324324

325+
/*
326+
* remove these references first, since we need to unlock
327+
* the chan_lock here, since iface_lock is a higher lock
328+
*/
329+
ses->chans[i].iface = NULL;
330+
ses->chans[i].server = NULL;
331+
spin_unlock(&ses->chan_lock);
332+
325333
if (iface) {
326334
spin_lock(&ses->iface_lock);
327335
kref_put(&iface->refcount, release_iface);
328-
ses->chans[i].iface = NULL;
329336
iface->num_channels--;
330337
if (iface->weight_fulfilled)
331338
iface->weight_fulfilled--;
332339
spin_unlock(&ses->iface_lock);
333340
}
334341

335-
spin_unlock(&ses->chan_lock);
336-
if (server && !server->terminate) {
337-
server->terminate = true;
338-
cifs_signal_cifsd_for_reconnect(server, false);
339-
}
340-
spin_lock(&ses->chan_lock);
341-
342342
if (server) {
343-
ses->chans[i].server = NULL;
343+
if (!server->terminate) {
344+
server->terminate = true;
345+
cifs_signal_cifsd_for_reconnect(server, false);
346+
}
344347
cifs_put_tcp_session(server, false);
345348
}
346349

350+
spin_lock(&ses->chan_lock);
347351
}
348352

349353
done:

0 commit comments

Comments
 (0)