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:
Aurelien Aptel 2018-06-14 17:04:51 +02:00 коммит произвёл Steve French
Родитель b6f0dd5d75
Коммит fe856be475
1 изменённых файлов: 155 добавлений и 15 удалений

Просмотреть файл

@ -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);