mirror of
https://github.com/torvalds/linux.git
synced 2024-12-28 22:02:28 +00:00
CIFS: parse and store info on iface queries
Signed-off-by: Aurelien Aptel <aaptel@suse.com> Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
parent
b6f0dd5d75
commit
fe856be475
@ -294,34 +294,176 @@ smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
|
||||
return rsize;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CIFS_STATS2
|
||||
|
||||
static int
|
||||
parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
|
||||
size_t buf_len,
|
||||
struct cifs_server_iface **iface_list,
|
||||
size_t *iface_count)
|
||||
{
|
||||
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;
|
||||
ssize_t bytes_left;
|
||||
size_t next = 0;
|
||||
int nb_iface = 0;
|
||||
int rc = 0;
|
||||
|
||||
*iface_list = NULL;
|
||||
*iface_count = 0;
|
||||
|
||||
/*
|
||||
* Fist pass: count and sanity check
|
||||
*/
|
||||
|
||||
bytes_left = buf_len;
|
||||
p = buf;
|
||||
while (bytes_left >= sizeof(*p)) {
|
||||
nb_iface++;
|
||||
next = le32_to_cpu(p->Next);
|
||||
if (!next) {
|
||||
bytes_left -= sizeof(*p);
|
||||
break;
|
||||
}
|
||||
p = (struct network_interface_info_ioctl_rsp *)((u8 *)p+next);
|
||||
bytes_left -= next;
|
||||
}
|
||||
|
||||
if (!nb_iface) {
|
||||
cifs_dbg(VFS, "%s: malformed interface info\n", __func__);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (bytes_left || p->Next)
|
||||
cifs_dbg(VFS, "%s: incomplete interface info\n", __func__);
|
||||
|
||||
|
||||
/*
|
||||
* Second pass: extract info to internal structure
|
||||
*/
|
||||
|
||||
*iface_list = kcalloc(nb_iface, sizeof(**iface_list), GFP_KERNEL);
|
||||
if (!*iface_list) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
info = *iface_list;
|
||||
bytes_left = buf_len;
|
||||
p = buf;
|
||||
while (bytes_left >= sizeof(*p)) {
|
||||
info->speed = le64_to_cpu(p->LinkSpeed);
|
||||
info->rdma_capable = le32_to_cpu(p->Capability & RDMA_CAPABLE);
|
||||
info->rss_capable = le32_to_cpu(p->Capability & RSS_CAPABLE);
|
||||
|
||||
cifs_dbg(FYI, "%s: adding iface %zu\n", __func__, *iface_count);
|
||||
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));
|
||||
|
||||
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 *)&info->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 *)&info->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_count)++;
|
||||
info++;
|
||||
next_iface:
|
||||
next = le32_to_cpu(p->Next);
|
||||
if (!next)
|
||||
break;
|
||||
p = (struct network_interface_info_ioctl_rsp *)((u8 *)p+next);
|
||||
bytes_left -= next;
|
||||
}
|
||||
|
||||
if (!*iface_count) {
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
if (rc) {
|
||||
kfree(*iface_list);
|
||||
*iface_count = 0;
|
||||
*iface_list = NULL;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
|
||||
{
|
||||
int rc;
|
||||
unsigned int ret_data_len = 0;
|
||||
struct network_interface_info_ioctl_rsp *out_buf;
|
||||
struct network_interface_info_ioctl_rsp *out_buf = NULL;
|
||||
struct cifs_server_iface *iface_list;
|
||||
size_t iface_count;
|
||||
struct cifs_ses *ses = tcon->ses;
|
||||
|
||||
rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
|
||||
FSCTL_QUERY_NETWORK_INTERFACE_INFO, true /* is_fsctl */,
|
||||
NULL /* no data input */, 0 /* no data input */,
|
||||
(char **)&out_buf, &ret_data_len);
|
||||
if (rc != 0)
|
||||
if (rc != 0) {
|
||||
cifs_dbg(VFS, "error %d on ioctl to get interface list\n", rc);
|
||||
else if (ret_data_len < sizeof(struct network_interface_info_ioctl_rsp)) {
|
||||
cifs_dbg(VFS, "server returned bad net interface info buf\n");
|
||||
rc = -EINVAL;
|
||||
} else {
|
||||
/* Dump info on first interface */
|
||||
cifs_dbg(FYI, "Adapter Capability 0x%x\t",
|
||||
le32_to_cpu(out_buf->Capability));
|
||||
cifs_dbg(FYI, "Link Speed %lld\n",
|
||||
le64_to_cpu(out_buf->LinkSpeed));
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = parse_server_interfaces(out_buf, ret_data_len,
|
||||
&iface_list, &iface_count);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
spin_lock(&ses->iface_lock);
|
||||
kfree(ses->iface_list);
|
||||
ses->iface_list = iface_list;
|
||||
ses->iface_count = iface_count;
|
||||
ses->iface_last_update = jiffies;
|
||||
spin_unlock(&ses->iface_lock);
|
||||
|
||||
out:
|
||||
kfree(out_buf);
|
||||
return rc;
|
||||
}
|
||||
#endif /* STATS2 */
|
||||
|
||||
void
|
||||
smb2_cached_lease_break(struct work_struct *work)
|
||||
@ -399,9 +541,7 @@ smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon)
|
||||
if (rc)
|
||||
return;
|
||||
|
||||
#ifdef CONFIG_CIFS_STATS2
|
||||
SMB3_request_interfaces(xid, tcon);
|
||||
#endif /* STATS2 */
|
||||
|
||||
SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid,
|
||||
FS_ATTRIBUTE_INFORMATION);
|
||||
|
Loading…
Reference in New Issue
Block a user