History

cifs: change iface_list from array to sorted linked list

A server's published interface list can change over time, and needs to be updated. We've storing iface_list as a simple array, which makes it difficult to manipulate an existing list. With this change, iface_list is modified into a linked list of interfaces, which is kept sorted by speed. Also added a reference counter for an iface entry, so that each channel can maintain a backpointer to the iface and drop it easily when needed.

Shyam Prasad N committed 4 years ago

Commit aa45dad1 parent 9de74996 files · +201 −129Browse files

Unified · Split

6 files
  1. fs/cifs/cifs_debug.c +7 −5
  2. fs/cifs/cifsglob.h +53 −1
  3. fs/cifs/connect.c +4 −2
  4. fs/cifs/misc.c +8 −1
  5. fs/cifs/sess.c +40 −38
  6. fs/cifs/smb2ops.c +89 −82
fs/cifs/cifs_debug.c+7−5
@@ -162,6 +162,8 @@ cifs_dump_iface(struct seq_file *m, struct cifs_server_iface *iface) · 161 unmodified lines
162 seq_printf(m, "\t\tIPv4: %pI4\n", &ipv4->sin_addr);162 seq_printf(m, "\t\tIPv4: %pI4\n", &ipv4->sin_addr);
163 else if (iface->sockaddr.ss_family == AF_INET6)163 else if (iface->sockaddr.ss_family == AF_INET6)
164 seq_printf(m, "\t\tIPv6: %pI6\n", &ipv6->sin6_addr);164 seq_printf(m, "\t\tIPv6: %pI6\n", &ipv6->sin6_addr);
165 if (!iface->is_active)
166 seq_puts(m, "\t\t[for-cleanup]\n");
165}167}
166 168
167static int cifs_debug_files_proc_show(struct seq_file *m, void *v)169static int cifs_debug_files_proc_show(struct seq_file *m, void *v)
@@ -221,6 +223,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) · 53 unmodified lines
221 struct TCP_Server_Info *server;223 struct TCP_Server_Info *server;
222 struct cifs_ses *ses;224 struct cifs_ses *ses;
223 struct cifs_tcon *tcon;225 struct cifs_tcon *tcon;
226 struct cifs_server_iface *iface;
224 int c, i, j;227 int c, i, j;
225 228
226 seq_puts(m,229 seq_puts(m,
@@ -456,11 +459,10 @@ skip_rdma: · 229 unmodified lines
456 if (ses->iface_count)459 if (ses->iface_count)
457 seq_printf(m, "\n\n\tServer interfaces: %zu",460 seq_printf(m, "\n\n\tServer interfaces: %zu",
458 ses->iface_count);461 ses->iface_count);
459 for (j = 0; j < ses->iface_count; j++) {462 j = 0;
460 struct cifs_server_iface *iface;463 list_for_each_entry(iface, &ses->iface_list,
461 464 iface_head) {
462 iface = &ses->iface_list[j];465 seq_printf(m, "\n\t%d)", ++j);
463 seq_printf(m, "\n\t%d)", j+1);
464 cifs_dump_iface(m, iface);466 cifs_dump_iface(m, iface);
465 if (is_ses_using_iface(ses, iface))467 if (is_ses_using_iface(ses, iface))
466 seq_puts(m, "\t\t[CONNECTED]\n");468 seq_puts(m, "\t\t[CONNECTED]\n");
fs/cifs/cifsglob.h+53−1
@@ -933,15 +933,67 @@ static inline void cifs_set_net_ns(struct TCP_Server_Info *srv, struct net *net) · 932 unmodified lines
933#endif933#endif
934 934
935struct cifs_server_iface {935struct cifs_server_iface {
936 struct list_head iface_head;
937 struct kref refcount;
936 size_t speed;938 size_t speed;
937 unsigned int rdma_capable : 1;939 unsigned int rdma_capable : 1;
938 unsigned int rss_capable : 1;940 unsigned int rss_capable : 1;
941 unsigned int is_active : 1; /* unset if non existent */
939 struct sockaddr_storage sockaddr;942 struct sockaddr_storage sockaddr;
940};943};
941 944
945/* release iface when last ref is dropped */
946static inline void
947release_iface(struct kref *ref)
948{
949 struct cifs_server_iface *iface = container_of(ref,
950 struct cifs_server_iface,
951 refcount);
952 list_del_init(&iface->iface_head);
953 kfree(iface);
954}
955
956/*
957 * compare two interfaces a and b
958 * return 0 if everything matches.
959 * return 1 if a has higher link speed, or rdma capable, or rss capable
960 * return -1 otherwise.
961 */
962static inline int
963iface_cmp(struct cifs_server_iface *a, struct cifs_server_iface *b)
964{
965 int cmp_ret = 0;
966
967 WARN_ON(!a || !b);
968 if (a->speed == b->speed) {
969 if (a->rdma_capable == b->rdma_capable) {
970 if (a->rss_capable == b->rss_capable) {
971 cmp_ret = memcmp(&a->sockaddr, &b->sockaddr,
972 sizeof(a->sockaddr));
973 if (!cmp_ret)
974 return 0;
975 else if (cmp_ret > 0)
976 return 1;
977 else
978 return -1;
979 } else if (a->rss_capable > b->rss_capable)
980 return 1;
981 else
982 return -1;
983 } else if (a->rdma_capable > b->rdma_capable)
984 return 1;
985 else
986 return -1;
987 } else if (a->speed > b->speed)
988 return 1;
989 else
990 return -1;
991}
992
942struct cifs_chan {993struct cifs_chan {
943 unsigned int in_reconnect : 1; /* if session setup in progress for this channel */994 unsigned int in_reconnect : 1; /* if session setup in progress for this channel */
944 struct TCP_Server_Info *server;995 struct TCP_Server_Info *server;
996 struct cifs_server_iface *iface; /* interface in use */
945 __u8 signkey[SMB3_SIGN_KEY_SIZE];997 __u8 signkey[SMB3_SIGN_KEY_SIZE];
946};998};
947 999
@@ -993,7 +1045,7 @@ struct cifs_ses { · 45 unmodified lines
993 */1045 */
994 spinlock_t iface_lock;1046 spinlock_t iface_lock;
995 /* ========= begin: protected by iface_lock ======== */1047 /* ========= begin: protected by iface_lock ======== */
996 struct cifs_server_iface *iface_list;1048 struct list_head iface_list;
997 size_t iface_count;1049 size_t iface_count;
998 unsigned long iface_last_update; /* jiffies */1050 unsigned long iface_last_update; /* jiffies */
999 /* ========= end: protected by iface_lock ======== */1051 /* ========= end: protected by iface_lock ======== */
fs/cifs/connect.c+4−2
@@ -1894,9 +1894,11 @@ void cifs_put_smb_ses(struct cifs_ses *ses) · 1893 unmodified lines
1894 int i;1894 int i;
1895 1895
1896 for (i = 1; i < chan_count; i++) {1896 for (i = 1; i < chan_count; i++) {
1897 spin_unlock(&ses->chan_lock);1897 if (ses->chans[i].iface) {
1898 kref_put(&ses->chans[i].iface->refcount, release_iface);
1899 ses->chans[i].iface = NULL;
1900 }
1898 cifs_put_tcp_session(ses->chans[i].server, 0);1901 cifs_put_tcp_session(ses->chans[i].server, 0);
1899 spin_lock(&ses->chan_lock);
1900 ses->chans[i].server = NULL;1902 ses->chans[i].server = NULL;
1901 }1903 }
1902 }1904 }
fs/cifs/misc.c+8−1
@@ -75,6 +75,7 @@ sesInfoAlloc(void) · 74 unmodified lines
75 INIT_LIST_HEAD(&ret_buf->tcon_list);75 INIT_LIST_HEAD(&ret_buf->tcon_list);
76 mutex_init(&ret_buf->session_mutex);76 mutex_init(&ret_buf->session_mutex);
77 spin_lock_init(&ret_buf->iface_lock);77 spin_lock_init(&ret_buf->iface_lock);
78 INIT_LIST_HEAD(&ret_buf->iface_list);
78 spin_lock_init(&ret_buf->chan_lock);79 spin_lock_init(&ret_buf->chan_lock);
79 }80 }
80 return ret_buf;81 return ret_buf;
@@ -83,6 +84,8 @@ sesInfoAlloc(void) · 2 unmodified lines
83void84void
84sesInfoFree(struct cifs_ses *buf_to_free)85sesInfoFree(struct cifs_ses *buf_to_free)
85{86{
87 struct cifs_server_iface *iface = NULL, *niface = NULL;
88
86 if (buf_to_free == NULL) {89 if (buf_to_free == NULL) {
87 cifs_dbg(FYI, "Null buffer passed to sesInfoFree\n");90 cifs_dbg(FYI, "Null buffer passed to sesInfoFree\n");
88 return;91 return;
@@ -96,7 +99,11 @@ sesInfoFree(struct cifs_ses *buf_to_free) · 7 unmodified lines
96 kfree(buf_to_free->user_name);99 kfree(buf_to_free->user_name);
97 kfree(buf_to_free->domainName);100 kfree(buf_to_free->domainName);
98 kfree_sensitive(buf_to_free->auth_key.response);101 kfree_sensitive(buf_to_free->auth_key.response);
99 kfree(buf_to_free->iface_list);102 spin_lock(&buf_to_free->iface_lock);
103 list_for_each_entry_safe(iface, niface, &buf_to_free->iface_list,
104 iface_head)
105 kref_put(&iface->refcount, release_iface);
106 spin_unlock(&buf_to_free->iface_lock);
100 kfree_sensitive(buf_to_free);107 kfree_sensitive(buf_to_free);
101}108}
102 109
fs/cifs/sess.c+40−38
@@ -58,7 +58,7 @@ bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface) · 57 unmodified lines
58 58
59 spin_lock(&ses->chan_lock);59 spin_lock(&ses->chan_lock);
60 for (i = 0; i < ses->chan_count; i++) {60 for (i = 0; i < ses->chan_count; i++) {
61 if (is_server_using_iface(ses->chans[i].server, iface)) {61 if (ses->chans[i].iface == iface) {
62 spin_unlock(&ses->chan_lock);62 spin_unlock(&ses->chan_lock);
63 return true;63 return true;
64 }64 }
@@ -151,11 +151,9 @@ int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses) · 86 unmodified lines
151{151{
152 int old_chan_count, new_chan_count;152 int old_chan_count, new_chan_count;
153 int left;153 int left;
154 int i = 0;
155 int rc = 0;154 int rc = 0;
156 int tries = 0;155 int tries = 0;
157 struct cifs_server_iface *ifaces = NULL;156 struct cifs_server_iface *iface = NULL, *niface = NULL;
158 size_t iface_count;
159 157
160 spin_lock(&ses->chan_lock);158 spin_lock(&ses->chan_lock);
161 159
@@ -184,33 +182,17 @@ int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses) · 22 unmodified lines
184 }182 }
185 spin_unlock(&ses->chan_lock);183 spin_unlock(&ses->chan_lock);
186 184
187 /*
188 * Make a copy of the iface list at the time and use that
189 * instead so as to not hold the iface spinlock for opening
190 * channels
191 */
192 spin_lock(&ses->iface_lock);
193 iface_count = ses->iface_count;
194 if (iface_count <= 0) {
195 spin_unlock(&ses->iface_lock);
196 cifs_dbg(VFS, "no iface list available to open channels\n");
197 return 0;
198 }
199 ifaces = kmemdup(ses->iface_list, iface_count*sizeof(*ifaces),
200 GFP_ATOMIC);
201 if (!ifaces) {
202 spin_unlock(&ses->iface_lock);
203 return 0;
204 }
205 spin_unlock(&ses->iface_lock);
206
207 /*185 /*
208 * Keep connecting to same, fastest, iface for all channels as186 * Keep connecting to same, fastest, iface for all channels as
209 * long as its RSS. Try next fastest one if not RSS or channel187 * long as its RSS. Try next fastest one if not RSS or channel
210 * creation fails.188 * creation fails.
211 */189 */
190 spin_lock(&ses->iface_lock);
191 iface = list_first_entry(&ses->iface_list, struct cifs_server_iface,
192 iface_head);
193 spin_unlock(&ses->iface_lock);
194
212 while (left > 0) {195 while (left > 0) {
213 struct cifs_server_iface *iface;
214 196
215 tries++;197 tries++;
216 if (tries > 3*ses->chan_max) {198 if (tries > 3*ses->chan_max) {
@@ -219,27 +201,46 @@ int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses) · 2 unmodified lines
219 break;201 break;
220 }202 }
221 203
222 iface = &ifaces[i];204 spin_lock(&ses->iface_lock);
223 if (is_ses_using_iface(ses, iface) && !iface->rss_capable) {205 if (!ses->iface_count) {
224 i = (i+1) % iface_count;206 spin_unlock(&ses->iface_lock);
225 continue;207 break;
226 }208 }
227 209
228 rc = cifs_ses_add_channel(cifs_sb, ses, iface);210 list_for_each_entry_safe_from(iface, niface, &ses->iface_list,
229 if (rc) {211 iface_head) {
230 cifs_dbg(FYI, "failed to open extra channel on iface#%d rc=%d\n",212 /* skip ifaces that are unusable */
231 i, rc);213 if (!iface->is_active ||
232 i = (i+1) % iface_count;214 (is_ses_using_iface(ses, iface) &&
233 continue;215 !iface->rss_capable)) {
216 continue;
217 }
218
219 /* take ref before unlock */
220 kref_get(&iface->refcount);
221
222 spin_unlock(&ses->iface_lock);
223 rc = cifs_ses_add_channel(cifs_sb, ses, iface);
224 spin_lock(&ses->iface_lock);
225
226 if (rc) {
227 cifs_dbg(VFS, "failed to open extra channel on iface:%pIS rc=%d\n",
228 &iface->sockaddr,
229 rc);
230 kref_put(&iface->refcount, release_iface);
231 continue;
232 }
233
234 cifs_dbg(FYI, "successfully opened new channel on iface:%pIS\n",
235 &iface->sockaddr);
236 break;
234 }237 }
238 spin_unlock(&ses->iface_lock);
235 239
236 cifs_dbg(FYI, "successfully opened new channel on iface#%d\n",
237 i);
238 left--;240 left--;
239 new_chan_count++;241 new_chan_count++;
240 }242 }
241 243
242 kfree(ifaces);
243 return new_chan_count - old_chan_count;244 return new_chan_count - old_chan_count;
244}245}
245 246
@@ -355,6 +356,7 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, · 109 unmodified lines
355 spin_unlock(&ses->chan_lock);356 spin_unlock(&ses->chan_lock);
356 goto out;357 goto out;
357 }358 }
359 chan->iface = iface;
358 ses->chan_count++;360 ses->chan_count++;
359 atomic_set(&ses->chan_seq, 0);361 atomic_set(&ses->chan_seq, 0);
360 362
fs/cifs/smb2ops.c+89−82
@@ -512,73 +512,41 @@ smb3_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) · 511 unmodified lines
512static int512static int
513parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,513parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
514 size_t buf_len,514 size_t buf_len,
515 struct cifs_server_iface **iface_list,515 struct cifs_ses *ses)
516 size_t *iface_count)
517{516{
518 struct network_interface_info_ioctl_rsp *p;517 struct network_interface_info_ioctl_rsp *p;
519 struct sockaddr_in *addr4;518 struct sockaddr_in *addr4;
520 struct sockaddr_in6 *addr6;519 struct sockaddr_in6 *addr6;
521 struct iface_info_ipv4 *p4;520 struct iface_info_ipv4 *p4;
522 struct iface_info_ipv6 *p6;521 struct iface_info_ipv6 *p6;
523 struct cifs_server_iface *info;522 struct cifs_server_iface *info = NULL, *iface = NULL, *niface = NULL;
523 struct cifs_server_iface tmp_iface;
524 ssize_t bytes_left;524 ssize_t bytes_left;
525 size_t next = 0;525 size_t next = 0;
526 int nb_iface = 0;526 int nb_iface = 0;
527 int rc = 0;527 int rc = 0, ret = 0;
528
529 *iface_list = NULL;
530 *iface_count = 0;
531
532 /*
533 * Fist pass: count and sanity check
534 */
535 528
536 bytes_left = buf_len;529 bytes_left = buf_len;
537 p = buf;530 p = buf;
538 while (bytes_left >= sizeof(*p)) {
539 nb_iface++;
540 next = le32_to_cpu(p->Next);
541 if (!next) {
542 bytes_left -= sizeof(*p);
543 break;
544 }
545 p = (struct network_interface_info_ioctl_rsp *)((u8 *)p+next);
546 bytes_left -= next;
547 }
548
549 if (!nb_iface) {
550 cifs_dbg(VFS, "%s: malformed interface info\n", __func__);
551 rc = -EINVAL;
552 goto out;
553 }
554
555 /* Azure rounds the buffer size up 8, to a 16 byte boundary */
556 if ((bytes_left > 8) || p->Next)
557 cifs_dbg(VFS, "%s: incomplete interface info\n", __func__);
558
559 531
532 spin_lock(&ses->iface_lock);
560 /*533 /*
561 * Second pass: extract info to internal structure534 * Go through iface_list and do kref_put to remove
535 * any unused ifaces. ifaces in use will be removed
536 * when the last user calls a kref_put on it
562 */537 */
563 538 list_for_each_entry_safe(iface, niface, &ses->iface_list,
564 *iface_list = kcalloc(nb_iface, sizeof(**iface_list), GFP_KERNEL);539 iface_head) {
565 if (!*iface_list) {540 iface->is_active = 0;
566 rc = -ENOMEM;541 kref_put(&iface->refcount, release_iface);
567 goto out;
568 }542 }
543 spin_unlock(&ses->iface_lock);
569 544
570 info = *iface_list;
571 bytes_left = buf_len;
572 p = buf;
573 while (bytes_left >= sizeof(*p)) {545 while (bytes_left >= sizeof(*p)) {
574 info->speed = le64_to_cpu(p->LinkSpeed);546 memset(&tmp_iface, 0, sizeof(tmp_iface));
575 info->rdma_capable = le32_to_cpu(p->Capability & RDMA_CAPABLE) ? 1 : 0;547 tmp_iface.speed = le64_to_cpu(p->LinkSpeed);
576 info->rss_capable = le32_to_cpu(p->Capability & RSS_CAPABLE) ? 1 : 0;548 tmp_iface.rdma_capable = le32_to_cpu(p->Capability & RDMA_CAPABLE) ? 1 : 0;
577 549 tmp_iface.rss_capable = le32_to_cpu(p->Capability & RSS_CAPABLE) ? 1 : 0;
578 cifs_dbg(FYI, "%s: adding iface %zu\n", __func__, *iface_count);
579 cifs_dbg(FYI, "%s: speed %zu bps\n", __func__, info->speed);
580 cifs_dbg(FYI, "%s: capabilities 0x%08x\n", __func__,
581 le32_to_cpu(p->Capability));
582 550
583 switch (p->Family) {551 switch (p->Family) {
584 /*552 /*
@@ -587,7 +555,7 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf, · 2 unmodified lines
587 * conversion explicit in case either one changes.555 * conversion explicit in case either one changes.
588 */556 */
589 case INTERNETWORK:557 case INTERNETWORK:
590 addr4 = (struct sockaddr_in *)&info->sockaddr;558 addr4 = (struct sockaddr_in *)&tmp_iface.sockaddr;
591 p4 = (struct iface_info_ipv4 *)p->Buffer;559 p4 = (struct iface_info_ipv4 *)p->Buffer;
592 addr4->sin_family = AF_INET;560 addr4->sin_family = AF_INET;
593 memcpy(&addr4->sin_addr, &p4->IPv4Address, 4);561 memcpy(&addr4->sin_addr, &p4->IPv4Address, 4);
@@ -599,7 +567,7 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf, · 5 unmodified lines
599 &addr4->sin_addr);567 &addr4->sin_addr);
600 break;568 break;
601 case INTERNETWORKV6:569 case INTERNETWORKV6:
602 addr6 = (struct sockaddr_in6 *)&info->sockaddr;570 addr6 = (struct sockaddr_in6 *)&tmp_iface.sockaddr;
603 p6 = (struct iface_info_ipv6 *)p->Buffer;571 p6 = (struct iface_info_ipv6 *)p->Buffer;
604 addr6->sin6_family = AF_INET6;572 addr6->sin6_family = AF_INET6;
605 memcpy(&addr6->sin6_addr, &p6->IPv6Address, 16);573 memcpy(&addr6->sin6_addr, &p6->IPv6Address, 16);
@@ -619,36 +587,88 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf, · 13 unmodified lines
619 goto next_iface;587 goto next_iface;
620 }588 }
621 589
622 (*iface_count)++;590 /*
623 info++;591 * The iface_list is assumed to be sorted by speed.
592 * Check if the new interface exists in that list.
593 * NEVER change iface. it could be in use.
594 * Add a new one instead
595 */
596 spin_lock(&ses->iface_lock);
597 iface = niface = NULL;
598 list_for_each_entry_safe(iface, niface, &ses->iface_list,
599 iface_head) {
600 ret = iface_cmp(iface, &tmp_iface);
601 if (!ret) {
602 /* just get a ref so that it doesn't get picked/freed */
603 iface->is_active = 1;
604 kref_get(&iface->refcount);
605 spin_unlock(&ses->iface_lock);
606 goto next_iface;
607 } else if (ret < 0) {
608 /* all remaining ifaces are slower */
609 kref_get(&iface->refcount);
610 break;
611 }
612 }
613 spin_unlock(&ses->iface_lock);
614
615 /* no match. insert the entry in the list */
616 info = kmalloc(sizeof(struct cifs_server_iface),
617 GFP_KERNEL);
618 if (!info) {
619 rc = -ENOMEM;
620 goto out;
621 }
622 memcpy(info, &tmp_iface, sizeof(tmp_iface));
623
624 /* add this new entry to the list */
625 kref_init(&info->refcount);
626 info->is_active = 1;
627
628 cifs_dbg(FYI, "%s: adding iface %zu\n", __func__, ses->iface_count);
629 cifs_dbg(FYI, "%s: speed %zu bps\n", __func__, info->speed);
630 cifs_dbg(FYI, "%s: capabilities 0x%08x\n", __func__,
631 le32_to_cpu(p->Capability));
632
633 spin_lock(&ses->iface_lock);
634 if (!list_entry_is_head(iface, &ses->iface_list, iface_head)) {
635 list_add_tail(&info->iface_head, &iface->iface_head);
636 kref_put(&iface->refcount, release_iface);
637 } else
638 list_add_tail(&info->iface_head, &ses->iface_list);
639 spin_unlock(&ses->iface_lock);
640
641 ses->iface_count++;
642 ses->iface_last_update = jiffies;
624next_iface:643next_iface:
644 nb_iface++;
625 next = le32_to_cpu(p->Next);645 next = le32_to_cpu(p->Next);
626 if (!next)646 if (!next) {
647 bytes_left -= sizeof(*p);
627 break;648 break;
649 }
628 p = (struct network_interface_info_ioctl_rsp *)((u8 *)p+next);650 p = (struct network_interface_info_ioctl_rsp *)((u8 *)p+next);
629 bytes_left -= next;651 bytes_left -= next;
630 }652 }
631 653
632 if (!*iface_count) {654 if (!nb_iface) {
655 cifs_dbg(VFS, "%s: malformed interface info\n", __func__);
633 rc = -EINVAL;656 rc = -EINVAL;
634 goto out;657 goto out;
635 }658 }
636 659
637out:660 /* Azure rounds the buffer size up 8, to a 16 byte boundary */
638 if (rc) {661 if ((bytes_left > 8) || p->Next)
639 kfree(*iface_list);662 cifs_dbg(VFS, "%s: incomplete interface info\n", __func__);
640 *iface_count = 0;
641 *iface_list = NULL;
642 }
643 return rc;
644}
645 663
646static int compare_iface(const void *ia, const void *ib)
647{
648 const struct cifs_server_iface *a = (struct cifs_server_iface *)ia;
649 const struct cifs_server_iface *b = (struct cifs_server_iface *)ib;
650 664
651 return a->speed == b->speed ? 0 : (a->speed > b->speed ? -1 : 1);665 if (!ses->iface_count) {
666 rc = -EINVAL;
667 goto out;
668 }
669
670out:
671 return rc;
652}672}
653 673
654static int674static int
@@ -657,8 +677,6 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon) · 2 unmodified lines
657 int rc;677 int rc;
658 unsigned int ret_data_len = 0;678 unsigned int ret_data_len = 0;
659 struct network_interface_info_ioctl_rsp *out_buf = NULL;679 struct network_interface_info_ioctl_rsp *out_buf = NULL;
660 struct cifs_server_iface *iface_list;
661 size_t iface_count;
662 struct cifs_ses *ses = tcon->ses;680 struct cifs_ses *ses = tcon->ses;
663 681
664 rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,682 rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
@@ -674,21 +692,10 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon) · 9 unmodified lines
674 goto out;692 goto out;
675 }693 }
676 694
677 rc = parse_server_interfaces(out_buf, ret_data_len,695 rc = parse_server_interfaces(out_buf, ret_data_len, ses);
678 &iface_list, &iface_count);
679 if (rc)696 if (rc)
680 goto out;697 goto out;
681 698
682 /* sort interfaces from fastest to slowest */
683 sort(iface_list, iface_count, sizeof(*iface_list), compare_iface, NULL);
684
685 spin_lock(&ses->iface_lock);
686 kfree(ses->iface_list);
687 ses->iface_list = iface_list;
688 ses->iface_count = iface_count;
689 ses->iface_last_update = jiffies;
690 spin_unlock(&ses->iface_lock);
691
692out:699out:
693 kfree(out_buf);700 kfree(out_buf);
694 return rc;701 return rc;