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

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
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -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);
}

+17 −10
Original line number Diff line number Diff line
@@ -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;


	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 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)
			break;
	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) {
		/* 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);
		}

		if (!mark_smb_session && cifs_chan_needs_reconnect(ses, server)) {
			spin_unlock(&ses->chan_lock);
			continue;
+23 −11
Original line number Diff line number Diff line
@@ -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);