summaryrefslogtreecommitdiff
path: root/fs/smb/client/smb2ops.c
diff options
context:
space:
mode:
authorEnzo Matsumiya <ematsumiya@suse.de>2025-05-21 16:25:53 +0200
committerEnzo Matsumiya <ematsumiya@suse.de>2025-05-22 14:02:48 +0200
commit192a0f9ef43af2d520ed4ac016595992660d24e0 (patch)
treec9cc854c84eecf159b8605d9dfc6ad269c89cbea /fs/smb/client/smb2ops.c
parenta87cf769d6b0be1a0e733e33945f3d6ba468240b (diff)
downloadlinux-multichannel-fixes-v2.tar.gz
linux-multichannel-fixes-v2.tar.bz2
linux-multichannel-fixes-v2.zip
smb: client: set interface for primary channelmultichannel-fixes-v2
On multichannel mounts, 2 sockets are created for the primary channel, as the interface it's connected to is received and parsed/added again. So compare ifaces IP with primary channel server IP (server->dstaddr) and assign the matching iface to ses->chans[0].iface. Add a ->is_primary flag to cifs_server_iface to allow skipping session setup on the primary channel, and for pretty-printing on cifs_debug.c as well. Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de>
Diffstat (limited to 'fs/smb/client/smb2ops.c')
-rw-r--r--fs/smb/client/smb2ops.c133
1 files changed, 73 insertions, 60 deletions
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index f3efafa23fa4..827cdec61b12 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -593,9 +593,12 @@ static int __iface_cmp(struct cifs_server_iface *a, struct cifs_server_iface *b)
return -1;
if (a->rdma_capable == b->rdma_capable) {
if (a->rss_capable == b->rss_capable) {
- if (a->speed == b->speed)
- return cifs_ipaddr_cmp((struct sockaddr *)&a->sockaddr,
- (struct sockaddr *)&b->sockaddr);
+ if (a->speed == b->speed) {
+ if (cifs_ipaddr_cmp((struct sockaddr *)&a->sockaddr,
+ (struct sockaddr *)&b->sockaddr))
+ return -1;
+ return 0;
+ }
if (a->speed > b->speed)
return -1;
return 1;
@@ -623,17 +626,70 @@ static inline bool ifaces_need_update(unsigned long last_update)
time_before(jiffies, last_update + (SMB_INTERFACE_POLL_INTERVAL * HZ)));
}
-static int
-parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf, size_t buf_len,
- struct TCP_Server_Info *pserver, struct list_head *ifaces)
+static inline bool check_iface(struct network_interface_info_ioctl_rsp *info, bool server_rdma,
+ struct cifs_server_iface *tmp)
{
- struct network_interface_info_ioctl_rsp *p;
struct sockaddr_in *addr4;
struct sockaddr_in6 *addr6;
struct iface_info_ipv4 *p4;
struct iface_info_ipv6 *p6;
- struct cifs_server_iface *info = NULL, *iface = NULL, *niface = NULL;
- struct cifs_server_iface tmp_iface;
+
+ memset(tmp, 0, sizeof(*tmp));
+
+ /* default to 1Gbps when link speed is unset */
+ tmp->speed = le64_to_cpu(info->LinkSpeed) ?: 1000000000;
+ tmp->rdma_capable = le32_to_cpu(info->Capability & RDMA_CAPABLE) ? 1 : 0;
+ tmp->rss_capable = le32_to_cpu(info->Capability & RSS_CAPABLE) ? 1 : 0;
+
+ if (!tmp->rss_capable)
+ return false;
+
+ if (tmp->rdma_capable != server_rdma)
+ return false;
+
+ /*
+ * The kernel and wire socket structures have the same
+ * layout and use network byte order but make the
+ * conversion explicit in case either one changes.
+ */
+ if (info->Family == INTERNETWORK) {
+ addr4 = (struct sockaddr_in *)&tmp->sockaddr;
+ p4 = (struct iface_info_ipv4 *)info->Buffer;
+ addr4->sin_family = AF_INET;
+ memcpy(&addr4->sin_addr, &p4->IPv4Address, 4);
+
+ /* [MS-SMB2] 2.2.32.5.1.1 Clients MUST ignore these */
+ addr4->sin_port = cpu_to_be16(CIFS_PORT);
+
+ cifs_dbg(FYI, "%s: ipv4 %pI4\n", __func__, &addr4->sin_addr);
+ return true;
+ }
+
+ if (info->Family == INTERNETWORKV6) {
+ addr6 = (struct sockaddr_in6 *)&tmp->sockaddr;
+ p6 = (struct iface_info_ipv6 *)info->Buffer;
+ addr6->sin6_family = AF_INET6;
+ memcpy(&addr6->sin6_addr, &p6->IPv6Address, 16);
+
+ /* [MS-SMB2] 2.2.32.5.1.2 Clients MUST ignore these */
+ addr6->sin6_flowinfo = 0;
+ addr6->sin6_scope_id = 0;
+ addr6->sin6_port = cpu_to_be16(CIFS_PORT);
+
+ cifs_dbg(FYI, "%s: ipv6 %pI6\n", __func__, &addr6->sin6_addr);
+ return true;
+ }
+
+ cifs_dbg(VFS, "%s: skipping unsupported socket family\n", __func__);
+ return false;
+}
+
+static int
+parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf, size_t buf_len,
+ struct TCP_Server_Info *pserver, struct list_head *ifaces)
+{
+ struct network_interface_info_ioctl_rsp *p;
+ struct cifs_server_iface *info = NULL, *iface = NULL, *niface = NULL, tmp_iface = {};
ssize_t bytes_left;
size_t next = 0;
int nb_iface = 0;
@@ -653,57 +709,9 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf, size_t buf
}
while (bytes_left >= (ssize_t)sizeof(*p)) {
- memset(&tmp_iface, 0, sizeof(tmp_iface));
- /* default to 1Gbps when link speed is unset */
- tmp_iface.speed = le64_to_cpu(p->LinkSpeed) ?: 1000000000;
- tmp_iface.rdma_capable = le32_to_cpu(p->Capability & RDMA_CAPABLE) ? 1 : 0;
- tmp_iface.rss_capable = le32_to_cpu(p->Capability & RSS_CAPABLE) ? 1 : 0;
-
- if (!tmp_iface.rss_capable)
- goto next_iface;
-
- if (tmp_iface.rdma_capable != pserver->rdma)
+ if (!check_iface(p, pserver->rdma, &tmp_iface))
goto next_iface;
- switch (p->Family) {
- /*
- * The kernel and wire socket structures have the same
- * layout and use network byte order but make the
- * conversion explicit in case either one changes.
- */
- case INTERNETWORK:
- addr4 = (struct sockaddr_in *)&tmp_iface.sockaddr;
- p4 = (struct iface_info_ipv4 *)p->Buffer;
- addr4->sin_family = AF_INET;
- memcpy(&addr4->sin_addr, &p4->IPv4Address, 4);
-
- /* [MS-SMB2] 2.2.32.5.1.1 Clients MUST ignore these */
- addr4->sin_port = cpu_to_be16(CIFS_PORT);
-
- cifs_dbg(FYI, "%s: ipv4 %pI4\n", __func__,
- &addr4->sin_addr);
- break;
- case INTERNETWORKV6:
- addr6 = (struct sockaddr_in6 *)&tmp_iface.sockaddr;
- p6 = (struct iface_info_ipv6 *)p->Buffer;
- addr6->sin6_family = AF_INET6;
- memcpy(&addr6->sin6_addr, &p6->IPv6Address, 16);
-
- /* [MS-SMB2] 2.2.32.5.1.2 Clients MUST ignore these */
- addr6->sin6_flowinfo = 0;
- addr6->sin6_scope_id = 0;
- addr6->sin6_port = cpu_to_be16(CIFS_PORT);
-
- cifs_dbg(FYI, "%s: ipv6 %pI6\n", __func__,
- &addr6->sin6_addr);
- break;
- default:
- cifs_dbg(VFS,
- "%s: skipping unsupported socket family\n",
- __func__);
- goto next_iface;
- }
-
/*
* iface_list is sorted by speed.
* Check if the new interface exists in that list.
@@ -737,13 +745,18 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf, size_t buf
/* add this new entry to the list */
kref_init(&info->refcount);
- cifs_dbg(FYI, "%s: adding iface[%zu]: %pIS\n", __func__, pserver->iface_count,
- &info->sockaddr);
+ cifs_dbg(FYI, "%s: adding iface: %pIS\n", __func__, &info->sockaddr);
cifs_dbg(FYI, "%s: speed %zu bps\n", __func__, info->speed);
cifs_dbg(FYI, "%s: capabilities 0x%08x\n", __func__,
le32_to_cpu(p->Capability));
+ /* mark primary channel to avoid duplicate sockets */
+ if (unlikely(!cifs_ipaddr_cmp((struct sockaddr *)&info->sockaddr,
+ (struct sockaddr *)&pserver->dstaddr)))
+ info->is_primary = 1;
+
list_add_tail(&info->iface_head, ifaces);
+
next_iface:
nb_iface++;
next = le32_to_cpu(p->Next);