Commit 09eeb072 authored by Shyam Prasad N's avatar Shyam Prasad N Committed by Steve French
Browse files

cifs: do not depend on release_iface for maintaining iface_list



parse_server_interfaces should be in complete charge of maintaining
the iface_list linked list. Today, iface entries are removed
from the list only when the last refcount is dropped.
i.e. in release_iface. However, this can result in undercounting
of refcount if the server stops advertising interfaces (which
Azure SMB server does).

This change puts parse_server_interfaces in full charge of
maintaining the iface_list. So if an empty list is returned
by the server, the entries in the list will immediately be
removed. This way, a following call to the same function will
not find entries in the list.

Fixes: aa45dadd ("cifs: change iface_list from array to sorted linked list")
Cc: stable@vger.kernel.org
Signed-off-by: default avatarShyam Prasad N <sprasad@microsoft.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 7257bcf3
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
@@ -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;
}