Skip to content

Commit ef529f6

Browse files
rajasi3010smfrench
authored andcommitted
cifs: client: allow changing multichannel mount options on remount
Previously, the client did not update a session's channel state when multichannel or max_channels mount options were changed via remount. This led to inconsistent behavior and prevented enabling or disabling multichannel support without a full unmount/remount cycle. Enable dynamic reconfiguration of multichannel and max_channels during remount by: - Introducing smb3_sync_ses_chan_max(), a centralized function for channel updates which synchronizes the session's channels with the updated configuration. - Replacing cifs_disable_secondary_channels() with cifs_decrease_secondary_channels(), which accepts a disable_mchan flag to support multichannel disable when the server stops supporting multichannel. - Updating remount logic to detect changes in multichannel or max_channels and trigger appropriate session/channel updates. Current limitation: - The query_interfaces worker runs even when max_channels=1 so that multichannel can be enabled later via remount without requiring an unmount. This is a temporary approach and may be refined in the future. Users can safely modify multichannel and max_channels on an existing mount. The client will correctly adjust the session's channel state to match the new configuration, preserving durability where possible and avoiding unnecessary disconnects. Reviewed-by: Shyam Prasad N <sprasad@microsoft.com> Signed-off-by: Rajasi Mandal <rajasimandal@microsoft.com> Signed-off-by: Steve French <stfrench@microsoft.com>
1 parent 32a6086 commit ef529f6

5 files changed

Lines changed: 128 additions & 26 deletions

File tree

fs/smb/client/cifsproto.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,8 @@ int cifs_alloc_hash(const char *name, struct shash_desc **sdesc);
635635
void cifs_free_hash(struct shash_desc **sdesc);
636636

637637
int cifs_try_adding_channels(struct cifs_ses *ses);
638+
int smb3_update_ses_channels(struct cifs_ses *ses, struct TCP_Server_Info *server,
639+
bool from_reconnect, bool disable_mchan);
638640
bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface);
639641
void cifs_ses_mark_for_reconnect(struct cifs_ses *ses);
640642

@@ -660,7 +662,7 @@ bool
660662
cifs_chan_is_iface_active(struct cifs_ses *ses,
661663
struct TCP_Server_Info *server);
662664
void
663-
cifs_disable_secondary_channels(struct cifs_ses *ses);
665+
cifs_decrease_secondary_channels(struct cifs_ses *ses, bool disable_mchan);
664666
void
665667
cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server);
666668
int

fs/smb/client/connect.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3926,7 +3926,9 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
39263926
ctx->prepath = NULL;
39273927

39283928
out:
3929-
cifs_try_adding_channels(mnt_ctx.ses);
3929+
smb3_update_ses_channels(mnt_ctx.ses, mnt_ctx.server,
3930+
false /* from_reconnect */,
3931+
false /* disable_mchan */);
39303932
rc = mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon);
39313933
if (rc)
39323934
goto error;

fs/smb/client/fs_context.c

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,7 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
758758
static int smb3_fs_context_parse_monolithic(struct fs_context *fc,
759759
void *data);
760760
static int smb3_get_tree(struct fs_context *fc);
761+
static void smb3_sync_ses_chan_max(struct cifs_ses *ses, unsigned int max_channels);
761762
static int smb3_reconfigure(struct fs_context *fc);
762763

763764
static const struct fs_context_operations smb3_fs_context_ops = {
@@ -1055,6 +1056,22 @@ int smb3_sync_session_ctx_passwords(struct cifs_sb_info *cifs_sb, struct cifs_se
10551056
return 0;
10561057
}
10571058

1059+
/*
1060+
* smb3_sync_ses_chan_max - Synchronize the session's maximum channel count
1061+
* @ses: pointer to the old CIFS session structure
1062+
* @max_channels: new maximum number of channels to allow
1063+
*
1064+
* Updates the session's chan_max field to the new value, protecting the update
1065+
* with the session's channel lock. This should be called whenever the maximum
1066+
* allowed channels for a session changes (e.g., after a remount or reconfigure).
1067+
*/
1068+
static void smb3_sync_ses_chan_max(struct cifs_ses *ses, unsigned int max_channels)
1069+
{
1070+
spin_lock(&ses->chan_lock);
1071+
ses->chan_max = max_channels;
1072+
spin_unlock(&ses->chan_lock);
1073+
}
1074+
10581075
static int smb3_reconfigure(struct fs_context *fc)
10591076
{
10601077
struct smb3_fs_context *ctx = smb3_fc2context(fc);
@@ -1137,7 +1154,39 @@ static int smb3_reconfigure(struct fs_context *fc)
11371154
ses->password2 = new_password2;
11381155
}
11391156

1140-
mutex_unlock(&ses->session_mutex);
1157+
/*
1158+
* If multichannel or max_channels has changed, update the session's channels accordingly.
1159+
* This may add or remove channels to match the new configuration.
1160+
*/
1161+
if ((ctx->multichannel != cifs_sb->ctx->multichannel) ||
1162+
(ctx->max_channels != cifs_sb->ctx->max_channels)) {
1163+
1164+
/* Synchronize ses->chan_max with the new mount context */
1165+
smb3_sync_ses_chan_max(ses, ctx->max_channels);
1166+
/* Now update the session's channels to match the new configuration */
1167+
/* Prevent concurrent scaling operations */
1168+
spin_lock(&ses->ses_lock);
1169+
if (ses->flags & CIFS_SES_FLAG_SCALE_CHANNELS) {
1170+
spin_unlock(&ses->ses_lock);
1171+
mutex_unlock(&ses->session_mutex);
1172+
return -EINVAL;
1173+
}
1174+
ses->flags |= CIFS_SES_FLAG_SCALE_CHANNELS;
1175+
spin_unlock(&ses->ses_lock);
1176+
1177+
mutex_unlock(&ses->session_mutex);
1178+
1179+
rc = smb3_update_ses_channels(ses, ses->server,
1180+
false /* from_reconnect */,
1181+
false /* disable_mchan */);
1182+
1183+
/* Clear scaling flag after operation */
1184+
spin_lock(&ses->ses_lock);
1185+
ses->flags &= ~CIFS_SES_FLAG_SCALE_CHANNELS;
1186+
spin_unlock(&ses->ses_lock);
1187+
} else {
1188+
mutex_unlock(&ses->session_mutex);
1189+
}
11411190

11421191
STEAL_STRING(cifs_sb, ctx, domainname);
11431192
STEAL_STRING(cifs_sb, ctx, nodename);

fs/smb/client/sess.c

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -265,12 +265,16 @@ int cifs_try_adding_channels(struct cifs_ses *ses)
265265
}
266266

267267
/*
268-
* called when multichannel is disabled by the server.
269-
* this always gets called from smb2_reconnect
270-
* and cannot get called in parallel threads.
268+
* cifs_decrease_secondary_channels - Reduce the number of active secondary channels
269+
* @ses: pointer to the CIFS session structure
270+
* @disable_mchan: if true, reduce to a single channel; if false, reduce to chan_max
271+
*
272+
* This function disables and cleans up extra secondary channels for a CIFS session.
273+
* If called during reconfiguration, it reduces the channel count to the new maximum (chan_max).
274+
* Otherwise, it disables all but the primary channel.
271275
*/
272276
void
273-
cifs_disable_secondary_channels(struct cifs_ses *ses)
277+
cifs_decrease_secondary_channels(struct cifs_ses *ses, bool disable_mchan)
274278
{
275279
int i, chan_count;
276280
struct TCP_Server_Info *server;
@@ -281,12 +285,16 @@ cifs_disable_secondary_channels(struct cifs_ses *ses)
281285
if (chan_count == 1)
282286
goto done;
283287

284-
ses->chan_count = 1;
285-
286-
/* for all secondary channels reset the need reconnect bit */
287-
ses->chans_need_reconnect &= 1;
288+
/* Update the chan_count to the new maximum */
289+
if (disable_mchan) {
290+
cifs_dbg(FYI, "server does not support multichannel anymore.\n");
291+
ses->chan_count = 1;
292+
} else {
293+
ses->chan_count = ses->chan_max;
294+
}
288295

289-
for (i = 1; i < chan_count; i++) {
296+
/* Disable all secondary channels beyond the new chan_count */
297+
for (i = ses->chan_count ; i < chan_count; i++) {
290298
iface = ses->chans[i].iface;
291299
server = ses->chans[i].server;
292300

@@ -318,6 +326,15 @@ cifs_disable_secondary_channels(struct cifs_ses *ses)
318326
spin_lock(&ses->chan_lock);
319327
}
320328

329+
/* For extra secondary channels, reset the need reconnect bit */
330+
if (ses->chan_count == 1) {
331+
cifs_dbg(VFS, "Disable all secondary channels\n");
332+
ses->chans_need_reconnect &= 1;
333+
} else {
334+
cifs_dbg(VFS, "Disable extra secondary channels\n");
335+
ses->chans_need_reconnect &= ((1UL << ses->chan_max) - 1);
336+
}
337+
321338
done:
322339
spin_unlock(&ses->chan_lock);
323340
}

fs/smb/client/smb2pdu.c

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ smb2_hdr_assemble(struct smb2_hdr *shdr, __le16 smb2_cmd,
168168
static int
169169
cifs_chan_skip_or_disable(struct cifs_ses *ses,
170170
struct TCP_Server_Info *server,
171-
bool from_reconnect)
171+
bool from_reconnect, bool disable_mchan)
172172
{
173173
struct TCP_Server_Info *pserver;
174174
unsigned int chan_index;
@@ -206,14 +206,46 @@ cifs_chan_skip_or_disable(struct cifs_ses *ses,
206206
return -EHOSTDOWN;
207207
}
208208

209-
cifs_server_dbg(VFS,
210-
"server does not support multichannel anymore. Disable all other channels\n");
211-
cifs_disable_secondary_channels(ses);
212-
209+
cifs_decrease_secondary_channels(ses, disable_mchan);
213210

214211
return 0;
215212
}
216213

214+
/*
215+
* smb3_update_ses_channels - Synchronize session channels with new configuration
216+
* @ses: pointer to the CIFS session structure
217+
* @server: pointer to the TCP server info structure
218+
* @from_reconnect: indicates if called from reconnect context
219+
* @disable_mchan: indicates if called from reconnect to disable multichannel
220+
*
221+
* Returns 0 on success or error code on failure.
222+
*
223+
* Outside of reconfigure, this function is called from cifs_mount() during mount
224+
* and from reconnect scenarios to adjust channel count when the
225+
* server's multichannel support changes.
226+
*/
227+
int smb3_update_ses_channels(struct cifs_ses *ses, struct TCP_Server_Info *server,
228+
bool from_reconnect, bool disable_mchan)
229+
{
230+
int rc = 0;
231+
/*
232+
* Manage session channels based on current count vs max:
233+
* - If disable requested, skip or disable the channel
234+
* - If below max channels, attempt to add more
235+
* - If above max channels, skip or disable excess channels
236+
*/
237+
if (disable_mchan)
238+
rc = cifs_chan_skip_or_disable(ses, server, from_reconnect, disable_mchan);
239+
else {
240+
if (ses->chan_count < ses->chan_max)
241+
rc = cifs_try_adding_channels(ses);
242+
else if (ses->chan_count > ses->chan_max)
243+
rc = cifs_chan_skip_or_disable(ses, server, from_reconnect, disable_mchan);
244+
}
245+
246+
return rc;
247+
}
248+
217249
static int
218250
smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
219251
struct TCP_Server_Info *server, bool from_reconnect)
@@ -355,8 +387,8 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
355387
*/
356388
if (ses->chan_count > 1 &&
357389
!(server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) {
358-
rc = cifs_chan_skip_or_disable(ses, server,
359-
from_reconnect);
390+
rc = smb3_update_ses_channels(ses, server,
391+
from_reconnect, true /* disable_mchan */);
360392
if (rc) {
361393
mutex_unlock(&ses->session_mutex);
362394
goto out;
@@ -438,8 +470,9 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
438470
* treat this as server not supporting multichannel
439471
*/
440472

441-
rc = cifs_chan_skip_or_disable(ses, server,
442-
from_reconnect);
473+
rc = smb3_update_ses_channels(ses, server,
474+
from_reconnect,
475+
true /* disable_mchan */);
443476
goto skip_add_channels;
444477
} else if (rc)
445478
cifs_tcon_dbg(FYI, "%s: failed to query server interfaces: %d\n",
@@ -451,7 +484,8 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
451484
if (ses->chan_count == 1)
452485
cifs_server_dbg(VFS, "supports multichannel now\n");
453486

454-
cifs_try_adding_channels(ses);
487+
smb3_update_ses_channels(ses, server, from_reconnect,
488+
false /* disable_mchan */);
455489
}
456490
} else {
457491
mutex_unlock(&ses->session_mutex);
@@ -1105,8 +1139,7 @@ SMB2_negotiate(const unsigned int xid,
11051139
req->SecurityMode = 0;
11061140

11071141
req->Capabilities = cpu_to_le32(server->vals->req_capabilities);
1108-
if (ses->chan_max > 1)
1109-
req->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL);
1142+
req->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL);
11101143

11111144
/* ClientGUID must be zero for SMB2.02 dialect */
11121145
if (server->vals->protocol_id == SMB20_PROT_ID)
@@ -1332,8 +1365,7 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
13321365

13331366
pneg_inbuf->Capabilities =
13341367
cpu_to_le32(server->vals->req_capabilities);
1335-
if (tcon->ses->chan_max > 1)
1336-
pneg_inbuf->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL);
1368+
pneg_inbuf->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL);
13371369

13381370
memcpy(pneg_inbuf->Guid, server->client_guid,
13391371
SMB2_CLIENT_GUID_SIZE);

0 commit comments

Comments
 (0)