Skip to content

Commit f591062

Browse files
sprasad-microsoftSteve French
authored andcommitted
cifs: handle servers that still advertise multichannel after disabling
Some servers like Azure SMB servers always advertise multichannel capability in server capabilities list. Such servers return error STATUS_NOT_IMPLEMENTED for ioctl calls to query server interfaces, and expect clients to consider that as a sign that they do not support multichannel. We already handled this at mount time. Soon after the tree connect, we query server interfaces. And when server returned STATUS_NOT_IMPLEMENTED, we kept interface list as empty. When cifs_try_adding_channels gets called, it would not find any interfaces, so will not add channels. For the case where an active multichannel mount exists, and multichannel is disabled by such a server, this change will now allow the client to disable secondary channels on the mount. It will check the return status of query server interfaces call soon after a tree reconnect. If the return status is EOPNOTSUPP, then instead of the check to add more channels, we'll disable the secondary channels instead. For better code reuse, this change also moves the common code for disabling multichannel to a helper function. Signed-off-by: Shyam Prasad N <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent ce09f8d commit f591062

File tree

2 files changed

+69
-46
lines changed

2 files changed

+69
-46
lines changed

fs/smb/client/smb2ops.c

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -614,7 +614,7 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
614614
"multichannel not available\n"
615615
"Empty network interface list returned by server %s\n",
616616
ses->server->hostname);
617-
rc = -EINVAL;
617+
rc = -EOPNOTSUPP;
618618
goto out;
619619
}
620620

@@ -734,12 +734,6 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
734734
if ((bytes_left > 8) || p->Next)
735735
cifs_dbg(VFS, "%s: incomplete interface info\n", __func__);
736736

737-
738-
if (!ses->iface_count) {
739-
rc = -EINVAL;
740-
goto out;
741-
}
742-
743737
out:
744738
/*
745739
* Go through the list again and put the inactive entries

fs/smb/client/smb2pdu.c

Lines changed: 68 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,57 @@ smb2_hdr_assemble(struct smb2_hdr *shdr, __le16 smb2_cmd,
156156
return;
157157
}
158158

159+
/* helper function for code reuse */
160+
static int
161+
cifs_chan_skip_or_disable(struct cifs_ses *ses,
162+
struct TCP_Server_Info *server,
163+
bool from_reconnect)
164+
{
165+
struct TCP_Server_Info *pserver;
166+
unsigned int chan_index;
167+
168+
if (SERVER_IS_CHAN(server)) {
169+
cifs_dbg(VFS,
170+
"server %s does not support multichannel anymore. Skip secondary channel\n",
171+
ses->server->hostname);
172+
173+
spin_lock(&ses->chan_lock);
174+
chan_index = cifs_ses_get_chan_index(ses, server);
175+
if (chan_index == CIFS_INVAL_CHAN_INDEX) {
176+
spin_unlock(&ses->chan_lock);
177+
goto skip_terminate;
178+
}
179+
180+
ses->chans[chan_index].server = NULL;
181+
spin_unlock(&ses->chan_lock);
182+
183+
/*
184+
* the above reference of server by channel
185+
* needs to be dropped without holding chan_lock
186+
* as cifs_put_tcp_session takes a higher lock
187+
* i.e. cifs_tcp_ses_lock
188+
*/
189+
cifs_put_tcp_session(server, from_reconnect);
190+
191+
server->terminate = true;
192+
cifs_signal_cifsd_for_reconnect(server, false);
193+
194+
/* mark primary server as needing reconnect */
195+
pserver = server->primary_server;
196+
cifs_signal_cifsd_for_reconnect(pserver, false);
197+
skip_terminate:
198+
mutex_unlock(&ses->session_mutex);
199+
return -EHOSTDOWN;
200+
}
201+
202+
cifs_server_dbg(VFS,
203+
"server does not support multichannel anymore. Disable all other channels\n");
204+
cifs_disable_secondary_channels(ses);
205+
206+
207+
return 0;
208+
}
209+
159210
static int
160211
smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
161212
struct TCP_Server_Info *server, bool from_reconnect)
@@ -164,8 +215,6 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
164215
struct nls_table *nls_codepage = NULL;
165216
struct cifs_ses *ses;
166217
int xid;
167-
struct TCP_Server_Info *pserver;
168-
unsigned int chan_index;
169218

170219
/*
171220
* SMB2s NegProt, SessSetup, Logoff do not have tcon yet so
@@ -310,44 +359,11 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
310359
*/
311360
if (ses->chan_count > 1 &&
312361
!(server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) {
313-
if (SERVER_IS_CHAN(server)) {
314-
cifs_dbg(VFS, "server %s does not support " \
315-
"multichannel anymore. skipping secondary channel\n",
316-
ses->server->hostname);
317-
318-
spin_lock(&ses->chan_lock);
319-
chan_index = cifs_ses_get_chan_index(ses, server);
320-
if (chan_index == CIFS_INVAL_CHAN_INDEX) {
321-
spin_unlock(&ses->chan_lock);
322-
goto skip_terminate;
323-
}
324-
325-
ses->chans[chan_index].server = NULL;
326-
spin_unlock(&ses->chan_lock);
327-
328-
/*
329-
* the above reference of server by channel
330-
* needs to be dropped without holding chan_lock
331-
* as cifs_put_tcp_session takes a higher lock
332-
* i.e. cifs_tcp_ses_lock
333-
*/
334-
cifs_put_tcp_session(server, from_reconnect);
335-
336-
server->terminate = true;
337-
cifs_signal_cifsd_for_reconnect(server, false);
338-
339-
/* mark primary server as needing reconnect */
340-
pserver = server->primary_server;
341-
cifs_signal_cifsd_for_reconnect(pserver, false);
342-
343-
skip_terminate:
362+
rc = cifs_chan_skip_or_disable(ses, server,
363+
from_reconnect);
364+
if (rc) {
344365
mutex_unlock(&ses->session_mutex);
345-
rc = -EHOSTDOWN;
346366
goto out;
347-
} else {
348-
cifs_server_dbg(VFS, "does not support " \
349-
"multichannel anymore. disabling all other channels\n");
350-
cifs_disable_secondary_channels(ses);
351367
}
352368
}
353369

@@ -395,11 +411,23 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
395411
rc = SMB3_request_interfaces(xid, tcon, false);
396412
free_xid(xid);
397413

398-
if (rc)
414+
if (rc == -EOPNOTSUPP) {
415+
/*
416+
* some servers like Azure SMB server do not advertise
417+
* that multichannel has been disabled with server
418+
* capabilities, rather return STATUS_NOT_IMPLEMENTED.
419+
* treat this as server not supporting multichannel
420+
*/
421+
422+
rc = cifs_chan_skip_or_disable(ses, server,
423+
from_reconnect);
424+
goto skip_add_channels;
425+
} else if (rc)
399426
cifs_dbg(FYI, "%s: failed to query server interfaces: %d\n",
400427
__func__, rc);
401428

402429
if (ses->chan_max > ses->chan_count &&
430+
ses->iface_count &&
403431
!SERVER_IS_CHAN(server)) {
404432
if (ses->chan_count == 1)
405433
cifs_server_dbg(VFS, "supports multichannel now\n");
@@ -411,6 +439,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
411439
} else {
412440
mutex_unlock(&ses->session_mutex);
413441
}
442+
skip_add_channels:
414443

415444
if (smb2_command != SMB2_INTERNAL_CMD)
416445
mod_delayed_work(cifsiod_wq, &server->reconnect, 0);

0 commit comments

Comments
 (0)