Skip to content

Commit 918c30d

Browse files
committed
Merge tag '5.19-rc3-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6
Pull cifs client fixes from Steve French: "Fixes addressing important multichannel, and reconnect issues. Multichannel mounts when the server network interfaces changed, or ip addresses changed, uncovered problems, especially in reconnect, but the patches for this were held up until recently due to some lock conflicts that are now addressed. Included in this set of fixes: - three fixes relating to multichannel reconnect, dynamically adjusting the list of server interfaces to avoid problems during reconnect - a lock conflict fix related to the above - two important fixes for negotiate on secondary channels (null netname can unintentionally cause multichannel to be disabled to some servers) - a reconnect fix (reporting incorrect IP address in some cases)" * tag '5.19-rc3-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6: cifs: update cifs_ses::ip_addr after failover cifs: avoid deadlocks while updating iface cifs: periodically query network interfaces from server cifs: during reconnect, update interface if necessary cifs: change iface_list from array to sorted linked list smb3: use netname when available on secondary channels smb3: fix empty netname context on secondary channels
2 parents 0840a79 + af3a6d1 commit 918c30d

File tree

8 files changed

+366
-139
lines changed

8 files changed

+366
-139
lines changed

fs/cifs/cifs_debug.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ cifs_dump_iface(struct seq_file *m, struct cifs_server_iface *iface)
162162
seq_printf(m, "\t\tIPv4: %pI4\n", &ipv4->sin_addr);
163163
else if (iface->sockaddr.ss_family == AF_INET6)
164164
seq_printf(m, "\t\tIPv6: %pI6\n", &ipv6->sin6_addr);
165+
if (!iface->is_active)
166+
seq_puts(m, "\t\t[for-cleanup]\n");
165167
}
166168

167169
static 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)
221223
struct TCP_Server_Info *server;
222224
struct cifs_ses *ses;
223225
struct cifs_tcon *tcon;
226+
struct cifs_server_iface *iface;
224227
int c, i, j;
225228

226229
seq_puts(m,
@@ -456,11 +459,10 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
456459
if (ses->iface_count)
457460
seq_printf(m, "\n\n\tServer interfaces: %zu",
458461
ses->iface_count);
459-
for (j = 0; j < ses->iface_count; j++) {
460-
struct cifs_server_iface *iface;
461-
462-
iface = &ses->iface_list[j];
463-
seq_printf(m, "\n\t%d)", j+1);
462+
j = 0;
463+
list_for_each_entry(iface, &ses->iface_list,
464+
iface_head) {
465+
seq_printf(m, "\n\t%d)", ++j);
464466
cifs_dump_iface(m, iface);
465467
if (is_ses_using_iface(ses, iface))
466468
seq_puts(m, "\t\t[CONNECTED]\n");

fs/cifs/cifsglob.h

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@
8080
#define SMB_DNS_RESOLVE_INTERVAL_MIN 120
8181
#define SMB_DNS_RESOLVE_INTERVAL_DEFAULT 600
8282

83+
/* smb multichannel query server interfaces interval in seconds */
84+
#define SMB_INTERFACE_POLL_INTERVAL 600
85+
8386
/* maximum number of PDUs in one compound */
8487
#define MAX_COMPOUND 5
8588

@@ -933,15 +936,67 @@ static inline void cifs_set_net_ns(struct TCP_Server_Info *srv, struct net *net)
933936
#endif
934937

935938
struct cifs_server_iface {
939+
struct list_head iface_head;
940+
struct kref refcount;
936941
size_t speed;
937942
unsigned int rdma_capable : 1;
938943
unsigned int rss_capable : 1;
944+
unsigned int is_active : 1; /* unset if non existent */
939945
struct sockaddr_storage sockaddr;
940946
};
941947

948+
/* release iface when last ref is dropped */
949+
static inline void
950+
release_iface(struct kref *ref)
951+
{
952+
struct cifs_server_iface *iface = container_of(ref,
953+
struct cifs_server_iface,
954+
refcount);
955+
list_del_init(&iface->iface_head);
956+
kfree(iface);
957+
}
958+
959+
/*
960+
* compare two interfaces a and b
961+
* return 0 if everything matches.
962+
* return 1 if a has higher link speed, or rdma capable, or rss capable
963+
* return -1 otherwise.
964+
*/
965+
static inline int
966+
iface_cmp(struct cifs_server_iface *a, struct cifs_server_iface *b)
967+
{
968+
int cmp_ret = 0;
969+
970+
WARN_ON(!a || !b);
971+
if (a->speed == b->speed) {
972+
if (a->rdma_capable == b->rdma_capable) {
973+
if (a->rss_capable == b->rss_capable) {
974+
cmp_ret = memcmp(&a->sockaddr, &b->sockaddr,
975+
sizeof(a->sockaddr));
976+
if (!cmp_ret)
977+
return 0;
978+
else if (cmp_ret > 0)
979+
return 1;
980+
else
981+
return -1;
982+
} else if (a->rss_capable > b->rss_capable)
983+
return 1;
984+
else
985+
return -1;
986+
} else if (a->rdma_capable > b->rdma_capable)
987+
return 1;
988+
else
989+
return -1;
990+
} else if (a->speed > b->speed)
991+
return 1;
992+
else
993+
return -1;
994+
}
995+
942996
struct cifs_chan {
943997
unsigned int in_reconnect : 1; /* if session setup in progress for this channel */
944998
struct TCP_Server_Info *server;
999+
struct cifs_server_iface *iface; /* interface in use */
9451000
__u8 signkey[SMB3_SIGN_KEY_SIZE];
9461001
};
9471002

@@ -993,7 +1048,7 @@ struct cifs_ses {
9931048
*/
9941049
spinlock_t iface_lock;
9951050
/* ========= begin: protected by iface_lock ======== */
996-
struct cifs_server_iface *iface_list;
1051+
struct list_head iface_list;
9971052
size_t iface_count;
9981053
unsigned long iface_last_update; /* jiffies */
9991054
/* ========= end: protected by iface_lock ======== */
@@ -1203,6 +1258,7 @@ struct cifs_tcon {
12031258
#ifdef CONFIG_CIFS_DFS_UPCALL
12041259
struct list_head ulist; /* cache update list */
12051260
#endif
1261+
struct delayed_work query_interfaces; /* query interfaces workqueue job */
12061262
};
12071263

12081264
/*

fs/cifs/cifsproto.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,13 @@ cifs_chan_clear_need_reconnect(struct cifs_ses *ses,
636636
bool
637637
cifs_chan_needs_reconnect(struct cifs_ses *ses,
638638
struct TCP_Server_Info *server);
639+
bool
640+
cifs_chan_is_iface_active(struct cifs_ses *ses,
641+
struct TCP_Server_Info *server);
642+
int
643+
cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server);
644+
int
645+
SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon);
639646

640647
void extract_unc_hostname(const char *unc, const char **h, size_t *len);
641648
int copy_path_name(char *dst, const char *src);

fs/cifs/connect.c

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,25 @@ static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
145145
return rc;
146146
}
147147

148+
static void smb2_query_server_interfaces(struct work_struct *work)
149+
{
150+
int rc;
151+
struct cifs_tcon *tcon = container_of(work,
152+
struct cifs_tcon,
153+
query_interfaces.work);
154+
155+
/*
156+
* query server network interfaces, in case they change
157+
*/
158+
rc = SMB3_request_interfaces(0, tcon);
159+
if (rc) {
160+
cifs_dbg(FYI, "%s: failed to query server interfaces: %d\n",
161+
__func__, rc);
162+
}
163+
164+
queue_delayed_work(cifsiod_wq, &tcon->query_interfaces,
165+
(SMB_INTERFACE_POLL_INTERVAL * HZ));
166+
}
148167

149168
static void cifs_resolve_server(struct work_struct *work)
150169
{
@@ -217,7 +236,7 @@ cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server,
217236
bool mark_smb_session)
218237
{
219238
struct TCP_Server_Info *pserver;
220-
struct cifs_ses *ses;
239+
struct cifs_ses *ses, *nses;
221240
struct cifs_tcon *tcon;
222241

223242
/*
@@ -231,7 +250,20 @@ cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server,
231250

232251

233252
spin_lock(&cifs_tcp_ses_lock);
234-
list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
253+
list_for_each_entry_safe(ses, nses, &pserver->smb_ses_list, smb_ses_list) {
254+
/* check if iface is still active */
255+
if (!cifs_chan_is_iface_active(ses, server)) {
256+
/*
257+
* HACK: drop the lock before calling
258+
* cifs_chan_update_iface to avoid deadlock
259+
*/
260+
ses->ses_count++;
261+
spin_unlock(&cifs_tcp_ses_lock);
262+
cifs_chan_update_iface(ses, server);
263+
spin_lock(&cifs_tcp_ses_lock);
264+
ses->ses_count--;
265+
}
266+
235267
spin_lock(&ses->chan_lock);
236268
if (!mark_smb_session && cifs_chan_needs_reconnect(ses, server))
237269
goto next_session;
@@ -1894,9 +1926,11 @@ void cifs_put_smb_ses(struct cifs_ses *ses)
18941926
int i;
18951927

18961928
for (i = 1; i < chan_count; i++) {
1897-
spin_unlock(&ses->chan_lock);
1929+
if (ses->chans[i].iface) {
1930+
kref_put(&ses->chans[i].iface->refcount, release_iface);
1931+
ses->chans[i].iface = NULL;
1932+
}
18981933
cifs_put_tcp_session(ses->chans[i].server, 0);
1899-
spin_lock(&ses->chan_lock);
19001934
ses->chans[i].server = NULL;
19011935
}
19021936
}
@@ -2270,6 +2304,9 @@ cifs_put_tcon(struct cifs_tcon *tcon)
22702304
list_del_init(&tcon->tcon_list);
22712305
spin_unlock(&cifs_tcp_ses_lock);
22722306

2307+
/* cancel polling of interfaces */
2308+
cancel_delayed_work_sync(&tcon->query_interfaces);
2309+
22732310
if (tcon->use_witness) {
22742311
int rc;
22752312

@@ -2507,6 +2544,12 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx)
25072544
tcon->local_lease = ctx->local_lease;
25082545
INIT_LIST_HEAD(&tcon->pending_opens);
25092546

2547+
/* schedule query interfaces poll */
2548+
INIT_DELAYED_WORK(&tcon->query_interfaces,
2549+
smb2_query_server_interfaces);
2550+
queue_delayed_work(cifsiod_wq, &tcon->query_interfaces,
2551+
(SMB_INTERFACE_POLL_INTERVAL * HZ));
2552+
25102553
spin_lock(&cifs_tcp_ses_lock);
25112554
list_add(&tcon->tcon_list, &ses->tcon_list);
25122555
spin_unlock(&cifs_tcp_ses_lock);
@@ -3982,10 +4025,16 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
39824025
struct nls_table *nls_info)
39834026
{
39844027
int rc = -ENOSYS;
4028+
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr;
4029+
struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr;
39854030
bool is_binding = false;
39864031

3987-
39884032
spin_lock(&cifs_tcp_ses_lock);
4033+
if (server->dstaddr.ss_family == AF_INET6)
4034+
scnprintf(ses->ip_addr, sizeof(ses->ip_addr), "%pI6", &addr6->sin6_addr);
4035+
else
4036+
scnprintf(ses->ip_addr, sizeof(ses->ip_addr), "%pI4", &addr->sin_addr);
4037+
39894038
if (ses->ses_status != SES_GOOD &&
39904039
ses->ses_status != SES_NEW &&
39914040
ses->ses_status != SES_NEED_RECON) {

fs/cifs/misc.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ sesInfoAlloc(void)
7575
INIT_LIST_HEAD(&ret_buf->tcon_list);
7676
mutex_init(&ret_buf->session_mutex);
7777
spin_lock_init(&ret_buf->iface_lock);
78+
INIT_LIST_HEAD(&ret_buf->iface_list);
7879
spin_lock_init(&ret_buf->chan_lock);
7980
}
8081
return ret_buf;
@@ -83,6 +84,8 @@ sesInfoAlloc(void)
8384
void
8485
sesInfoFree(struct cifs_ses *buf_to_free)
8586
{
87+
struct cifs_server_iface *iface = NULL, *niface = NULL;
88+
8689
if (buf_to_free == NULL) {
8790
cifs_dbg(FYI, "Null buffer passed to sesInfoFree\n");
8891
return;
@@ -96,7 +99,11 @@ sesInfoFree(struct cifs_ses *buf_to_free)
9699
kfree(buf_to_free->user_name);
97100
kfree(buf_to_free->domainName);
98101
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);
100107
kfree_sensitive(buf_to_free);
101108
}
102109

0 commit comments

Comments
 (0)