Commit 3eca8945 authored by Linus Torvalds's avatar Linus Torvalds

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
parents 1f874787 09eeb072
......@@ -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);
}
......
......@@ -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))
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);
}
spin_lock(&ses->chan_lock);
if (!mark_smb_session && cifs_chan_needs_reconnect(ses, server)) {
spin_unlock(&ses->chan_lock);
continue;
......
......@@ -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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment