diff options
| author | Enzo Matsumiya <ematsumiya@suse.de> | 2025-05-21 16:25:53 +0200 |
|---|---|---|
| committer | Enzo Matsumiya <ematsumiya@suse.de> | 2025-05-22 14:02:48 +0200 |
| commit | 192a0f9ef43af2d520ed4ac016595992660d24e0 (patch) | |
| tree | c9cc854c84eecf159b8605d9dfc6ad269c89cbea | |
| parent | a87cf769d6b0be1a0e733e33945f3d6ba468240b (diff) | |
| download | linux-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>
| -rw-r--r-- | fs/smb/client/cifs_debug.c | 17 | ||||
| -rw-r--r-- | fs/smb/client/cifsglob.h | 1 | ||||
| -rw-r--r-- | fs/smb/client/sess.c | 14 | ||||
| -rw-r--r-- | fs/smb/client/smb2ops.c | 133 |
4 files changed, 90 insertions, 75 deletions
diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c index 0c2acc4e2cbb..34166040569c 100644 --- a/fs/smb/client/cifs_debug.c +++ b/fs/smb/client/cifs_debug.c @@ -212,22 +212,16 @@ static inline const char *smb_speed_to_str(size_t bps) static void cifs_dump_iface(struct seq_file *m, struct cifs_server_iface *iface) { - struct sockaddr_in *ipv4 = (struct sockaddr_in *)&iface->sockaddr; - struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&iface->sockaddr; - seq_printf(m, "\tSpeed: %s\n", smb_speed_to_str(iface->speed)); seq_puts(m, "\t\tCapabilities: "); if (iface->rdma_capable) - seq_puts(m, "rdma "); + seq_puts(m, "RDMA "); if (iface->rss_capable) - seq_puts(m, "rss "); + seq_puts(m, "RSS "); if (!iface->rdma_capable && !iface->rss_capable) - seq_puts(m, "None"); + seq_puts(m, "NONE"); seq_putc(m, '\n'); - if (iface->sockaddr.ss_family == AF_INET) - seq_printf(m, "\t\tIPv4: %pI4\n", &ipv4->sin_addr); - else if (iface->sockaddr.ss_family == AF_INET6) - seq_printf(m, "\t\tIPv6: %pI6\n", &ipv6->sin6_addr); + seq_printf(m, "\t\tIP: %pIS\n", (struct sockaddr *)&iface->sockaddr); if (!iface->is_active) seq_puts(m, "\t\t[for-cleanup]\n"); } @@ -602,7 +596,8 @@ skip_rdma: iface->num_channels); if (is_ses_using_iface(ses, iface)) - seq_puts(m, "\t\t[CONNECTED]\n"); + seq_printf(m, "\t\t[CONNECTED]%s\n", + iface->is_primary ? " (PRIMARY)" : ""); } spin_unlock(&server->iface_lock); diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h index 8631f3b9eaf7..282684316b87 100644 --- a/fs/smb/client/cifsglob.h +++ b/fs/smb/client/cifsglob.h @@ -1083,6 +1083,7 @@ struct cifs_server_iface { unsigned int rdma_capable : 1; unsigned int rss_capable : 1; unsigned int is_active : 1; /* unset if non existent */ + unsigned int is_primary : 1; struct sockaddr_storage sockaddr; }; diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c index af39c819822f..5c2725eee933 100644 --- a/fs/smb/client/sess.c +++ b/fs/smb/client/sess.c @@ -170,8 +170,6 @@ static int try_adding_channels(struct cifs_ses *ses, struct list_head *ifaces) spin_unlock(&pserver->iface_lock); list_for_each_entry_safe(iface, niface, ifaces, iface_head) { - rc = 0; - /* do not mix rdma and non-rdma interfaces */ if (iface->rdma_capable != ses->server->rdma) continue; @@ -185,7 +183,15 @@ static int try_adding_channels(struct cifs_ses *ses, struct list_head *ifaces) if (iface->weight_fulfilled >= weight) continue; - rc = cifs_ses_add_channel(ses, iface); + rc = 0; + if (!iface->is_primary) { + rc = cifs_ses_add_channel(ses, iface); + } else { + spin_lock(&ses->chan_lock); + ses->chans[0].iface = iface; + spin_unlock(&ses->chan_lock); + } + if (!rc) { cifs_info("successfully opened new channel on iface:%pIS\n", &iface->sockaddr); iface->is_active = 1; @@ -195,7 +201,7 @@ static int try_adding_channels(struct cifs_ses *ses, struct list_head *ifaces) spin_lock(&pserver->iface_lock); pserver->iface_count++; - list_move(&iface->iface_head, &pserver->iface_list); + list_move_tail(&iface->iface_head, &pserver->iface_list); pserver->iface_last_update = jiffies; spin_unlock(&pserver->iface_lock); 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); |
