Three important multichannel smb3 client fixes

-----BEGIN PGP SIGNATURE-----
 
 iQGyBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmWWRDMACgkQiiy9cAdy
 T1E+tAv4g15Kh9JeSaO/x1oyujKSDnEALWGvriwj9EC6+Z+MzQQyyPxB4uUyRuSN
 uts/QgvpK4LW2jIhyTaBfU9xRfhJYup1B+vaBtij6NwKfnxYo1PJCdAj8yelAfCW
 7mgqSiIGMRbRFz3jnFbb5vQS3H32Hpje0h32Rh3LiKrS6q+9exCJDHA6jqQ5JwvP
 /BrDeJ0vykyUjvjxcYQaeWZDkXEy7bysVVq+3qOEr9HSwX7Jv1f6JOLLpUoTTWx9
 LPkBus02JOAWX6FUWewVdx1ar9cTafA54rzS3hrpT2lxjuMhSmL6v9Br9MCsAMnI
 oWMWfjyefAM6ss4m/pAD178WZ61f1urIRrTRJJUwcWA+XjXTF6dXXZvMFdzqjYW5
 k6nMhDfFXzSzywwSFmlQwh/00NPVzX7DuGjgd3ZdJ7z6bvFvltwZtkRPMVK7zvHc
 X8LWAWYuaLku4yj3PJa3FiBrSYPsztTX3NLqQq+5vcxxTDCOcFduXComvvEosSiq
 4UbhxHU=
 =0P5G
 -----END PGP SIGNATURE-----

Merge tag '6.7-rc8-smb3-mchan-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull smb client fixes from Steve French:
 "Three important multichannel smb3 client fixes found in recent
  testing:

   - fix oops due to incorrect refcounting of interfaces after
     disabling multichannel

   - fix possible unrecoverable session state after disabling
     multichannel with active sessions

   - fix two places that were missing use of chan_lock"

* tag '6.7-rc8-smb3-mchan-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  cifs: do not depend on release_iface for maintaining iface_list
  cifs: cifs_chan_is_iface_active should be called with chan_lock held
  cifs: after disabling multichannel, mark tcon for reconnect
This commit is contained in:
Linus Torvalds 2024-01-05 08:52:25 -08:00
commit 3eca89454a
3 changed files with 41 additions and 23 deletions

View file

@ -994,7 +994,6 @@ release_iface(struct kref *ref)
struct cifs_server_iface *iface = container_of(ref,
struct cifs_server_iface,
refcount);
list_del_init(&iface->iface_head);
kfree(iface);
}

View file

@ -216,22 +216,29 @@ cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server,
/* If server is a channel, select the primary channel */
pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
/*
* if the server has been marked for termination, there is a
* chance that the remaining channels all need reconnect. To be
* on the safer side, mark the session and trees for reconnect
* for this scenario. This might cause a few redundant session
* setup and tree connect requests, but it is better than not doing
* a tree connect when needed, and all following requests failing
*/
if (server->terminate) {
mark_smb_session = true;
server = pserver;
}
spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry_safe(ses, nses, &pserver->smb_ses_list, smb_ses_list) {
/*
* if channel has been marked for termination, nothing to do
* for the channel. in fact, we cannot find the channel for the
* server. So safe to exit here
*/
if (server->terminate)
break;
/* check if iface is still active */
if (!cifs_chan_is_iface_active(ses, server))
cifs_chan_update_iface(ses, server);
spin_lock(&ses->chan_lock);
if (!cifs_chan_is_iface_active(ses, server)) {
spin_unlock(&ses->chan_lock);
cifs_chan_update_iface(ses, server);
spin_lock(&ses->chan_lock);
}
if (!mark_smb_session && cifs_chan_needs_reconnect(ses, server)) {
spin_unlock(&ses->chan_lock);
continue;

View file

@ -595,16 +595,12 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
}
/*
* Go through iface_list and do kref_put to remove
* any unused ifaces. ifaces in use will be removed
* when the last user calls a kref_put on it
* Go through iface_list and mark them as inactive
*/
list_for_each_entry_safe(iface, niface, &ses->iface_list,
iface_head) {
iface_head)
iface->is_active = 0;
kref_put(&iface->refcount, release_iface);
ses->iface_count--;
}
spin_unlock(&ses->iface_lock);
/*
@ -678,10 +674,7 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
iface_head) {
ret = iface_cmp(iface, &tmp_iface);
if (!ret) {
/* just get a ref so that it doesn't get picked/freed */
iface->is_active = 1;
kref_get(&iface->refcount);
ses->iface_count++;
spin_unlock(&ses->iface_lock);
goto next_iface;
} else if (ret < 0) {
@ -748,6 +741,20 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
}
out:
/*
* Go through the list again and put the inactive entries
*/
spin_lock(&ses->iface_lock);
list_for_each_entry_safe(iface, niface, &ses->iface_list,
iface_head) {
if (!iface->is_active) {
list_del(&iface->iface_head);
kref_put(&iface->refcount, release_iface);
ses->iface_count--;
}
}
spin_unlock(&ses->iface_lock);
return rc;
}
@ -784,9 +791,14 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon, bool in_
goto out;
/* check if iface is still active */
spin_lock(&ses->chan_lock);
pserver = ses->chans[0].server;
if (pserver && !cifs_chan_is_iface_active(ses, pserver))
if (pserver && !cifs_chan_is_iface_active(ses, pserver)) {
spin_unlock(&ses->chan_lock);
cifs_chan_update_iface(ses, pserver);
spin_lock(&ses->chan_lock);
}
spin_unlock(&ses->chan_lock);
out:
kfree(out_buf);