diff options
Diffstat (limited to 'fs/smb/client/smb2ops.c')
| -rw-r--r-- | fs/smb/client/smb2ops.c | 133 |
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); |
