Skip to content

Commit

Permalink
cifs: do not depend on release_iface for maintaining iface_list
Browse files Browse the repository at this point in the history
commit 09eeb07 upstream.

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: aa45dad ("cifs: change iface_list from array to sorted linked list")
Cc: stable@vger.kernel.org
Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
sprasad-microsoft authored and gregkh committed Jan 10, 2024
1 parent 5982a62 commit 3152a7d
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 11 deletions.
1 change: 0 additions & 1 deletion fs/smb/client/cifsglob.h
Original file line number Diff line number Diff line change
Expand Up @@ -972,7 +972,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);
}

Expand Down
27 changes: 17 additions & 10 deletions fs/smb/client/smb2ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -588,16 +588,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);

/*
Expand Down Expand Up @@ -672,10 +668,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) {
Expand Down Expand Up @@ -742,6 +735,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;
}

Expand Down

0 comments on commit 3152a7d

Please sign in to comment.