lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-ID: <vwwwvo2ehhlvq64vzp3j62asaryc6kkw34rjnkf7ozssv3hzyd@vwmxraotzwo6>
Date: Tue, 23 Sep 2025 14:32:24 -0300
From: Enzo Matsumiya <ematsumiya@...e.de>
To: RAJASI MANDAL <rajasimandalos@...il.com>
Cc: linux-cifs@...r.kernel.org, sfrench@...ba.org, pc@...guebit.org, 
	ronniesahlberg@...il.com, sprasad@...rosoft.com, tom@...pey.com, bharathsm@...rosoft.com, 
	linux-kernel@...r.kernel.org, Rajasi Mandal <rajasimandal@...rosoft.com>
Subject: Re: [PATCH 2/2] cifs: client: allow changing multichannel mount
 options on remount

On 09/23, RAJASI MANDAL wrote:
>> (for all hunks above) Can we stick to /* */ comments instead of //
>please?
>
>Yes sure I will do that.
>
>> This effectively makes query_interfaces worker run even with
>max_channels=1 -- just a heads up because it didn't look like your
>original intention.
>
>This change ensures that the multichannel option can be enabled during
>remounts, even if it wasn't specified during the initial mount.
>During the initial mount, if the user does not pass the multichannel
>option, the client does not advertise multichannel capability to the
>server.
>Since remounts do not trigger a fresh negotiation with the server, it's not
>possible to enable multichannel at that stage if it was previously omitted.
>
>To address this, the client is modified to *advertise multichannel
>capability by default*, regardless of whether the option was explicitly
>provided during the initial mount.

Yes, and I agree with that change.

I was just mentioning that because cifs_get_tcon() starts the
query_interface worker based on that flag (and not on ses->chan_max, for
example):

         if (ses->server->dialect >= SMB30_PROT_ID &&
             (ses->server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) {
                 /* schedule query interfaces poll */
                 queue_delayed_work(cifsiod_wq, &tcon->query_interfaces,
                                    (SMB_INTERFACE_POLL_INTERVAL * HZ));
	}

IMO, like I replied to Shyam, is that the worker should only run if the
user requested multichannel.

I'd suggest changing it to "if (ses->chan_max > 1) ...", and then also
add a call to queue_delayed_work() on reconfigure when chan_max changes.


Cheers,

Enzo

>On Mon, Sep 22, 2025 at 8:43 PM Enzo Matsumiya <ematsumiya@...e.de> wrote:
>
>> On 09/22, rajasimandalos@...il.com wrote:
>> >From: Rajasi Mandal <rajasimandal@...rosoft.com>
>> >
>> >Previously, the client did not properly update the session's channel
>> >state when multichannel or max_channels mount options were changed
>> >during remount. This led to inconsistent behavior and prevented
>> >enabling or disabling multichannel support without a full
>> >unmount/remount.
>> >
>> >Enable dynamic reconfiguration of multichannel and max_channels
>> >options during remount by introducing smb3_sync_ses_chan_max() to
>> >safely update the session's chan_max field, and smb3_sync_ses_channels()
>> >to synchronize the session's channels with the new configuration.
>> >Replace cifs_disable_secondary_channels() with
>> >cifs_decrease_secondary_channels(), which now takes a from_reconfigure
>> >argument for more flexible channel cleanup. Update the remount logic
>> >to detect changes in multichannel or max_channels and trigger the
>> >appropriate session/channel updates.
>> >
>> >With this change, users can safely change multichannel and
>> >max_channels options on remount, and the client will correctly adjust
>> >the session's channel state to match the new configuration.
>> >
>> >Signed-off-by: Rajasi Mandal <rajasimandal@...rosoft.com>
>> >---
>> > fs/smb/client/cifsproto.h  |  2 +-
>> > fs/smb/client/fs_context.c | 29 ++++++++++++++++++
>> > fs/smb/client/fs_context.h |  2 +-
>> > fs/smb/client/sess.c       | 35 +++++++++++++++-------
>> > fs/smb/client/smb2pdu.c    | 60 ++++++++++++++++++++++++++++++--------
>> > fs/smb/client/smb2pdu.h    |  2 ++
>> > 6 files changed, 105 insertions(+), 25 deletions(-)
>>
>> I think the fix is necessary, and the implementation works (at least
>> with the simple case I tested).  I just think we now have too many
>> functions related to channel adding/removing/updating and they're all
>> too similar.  IMHO they could all be merged into a single "update
>> channels" one.
>>
>> Do you think it's possible to do that?  Probably would require a bit
>> more work, but at least we would end up with a centralized place to deal
>> with channel management.
>>
>> >diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h
>> >index e8fba98690ce..ec3118457b26 100644
>> >--- a/fs/smb/client/cifsproto.h
>> >+++ b/fs/smb/client/cifsproto.h
>> >@@ -667,7 +667,7 @@ bool
>> > cifs_chan_is_iface_active(struct cifs_ses *ses,
>> >                         struct TCP_Server_Info *server);
>> > void
>> >-cifs_disable_secondary_channels(struct cifs_ses *ses);
>> >+cifs_decrease_secondary_channels(struct cifs_ses *ses, bool
>> from_reconfigure);
>> > void
>> > cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info
>> *server);
>> > int
>> >diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c
>> >index 43552b44f613..96e80c70f25d 100644
>> >--- a/fs/smb/client/fs_context.c
>> >+++ b/fs/smb/client/fs_context.c
>> >@@ -1015,6 +1015,22 @@ int smb3_sync_session_ctx_passwords(struct
>> cifs_sb_info *cifs_sb, struct cifs_se
>> >       return 0;
>> > }
>> >
>> >+/**
>> >+ * smb3_sync_ses_chan_max - Synchronize the session's maximum channel
>> count
>> >+ * @ses: pointer to the old CIFS session structure
>> >+ * @max_channels: new maximum number of channels to allow
>> >+ *
>> >+ * Updates the session's chan_max field to the new value, protecting the
>> update
>> >+ * with the session's channel lock. This should be called whenever the
>> maximum
>> >+ * allowed channels for a session changes (e.g., after a remount or
>> reconfigure).
>> >+ */
>> >+void smb3_sync_ses_chan_max(struct cifs_ses *ses, unsigned int
>> max_channels)
>> >+{
>> >+      spin_lock(&ses->chan_lock);
>> >+      ses->chan_max = max_channels;
>> >+      spin_unlock(&ses->chan_lock);
>> >+}
>> >+
>> > static int smb3_reconfigure(struct fs_context *fc)
>> > {
>> >       struct smb3_fs_context *ctx = smb3_fc2context(fc);
>> >@@ -1097,6 +1113,18 @@ static int smb3_reconfigure(struct fs_context *fc)
>> >               ses->password2 = new_password2;
>> >       }
>> >
>> >+      /*
>> >+       * If multichannel or max_channels has changed, update the
>> session's channels accordingly.
>> >+       * This may add or remove channels to match the new configuration.
>> >+       */
>> >+      if ((ctx->multichannel != cifs_sb->ctx->multichannel) ||
>> >+              (ctx->max_channels != cifs_sb->ctx->max_channels)) {
>> >+              //Synchronize ses->chan_max with the new mount context
>> >+              smb3_sync_ses_chan_max(ses, ctx->max_channels);
>> >+              //Now update the session's channels to match the new
>> configuration
>> >+              rc = smb3_sync_ses_channels(cifs_sb);
>> >+      }
>> >+
>> >       mutex_unlock(&ses->session_mutex);
>> >
>> >       STEAL_STRING(cifs_sb, ctx, domainname);
>> >@@ -1110,6 +1138,7 @@ static int smb3_reconfigure(struct fs_context *fc)
>> >       smb3_cleanup_fs_context_contents(cifs_sb->ctx);
>> >       rc = smb3_fs_context_dup(cifs_sb->ctx, ctx);
>> >       smb3_update_mnt_flags(cifs_sb);
>> >+
>> > #ifdef CONFIG_CIFS_DFS_UPCALL
>> >       if (!rc)
>> >               rc = dfs_cache_remount_fs(cifs_sb);
>> >diff --git a/fs/smb/client/fs_context.h b/fs/smb/client/fs_context.h
>> >index b0fec6b9a23b..a75185858285 100644
>> >--- a/fs/smb/client/fs_context.h
>> >+++ b/fs/smb/client/fs_context.h
>> >@@ -371,7 +371,7 @@ static inline struct smb3_fs_context
>> *smb3_fc2context(const struct fs_context *f
>> > extern int smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct
>> smb3_fs_context *ctx);
>> > extern int smb3_sync_session_ctx_passwords(struct cifs_sb_info *cifs_sb,
>> struct cifs_ses *ses);
>> > extern void smb3_update_mnt_flags(struct cifs_sb_info *cifs_sb);
>> >-
>> >+extern void smb3_sync_ses_chan_max(struct cifs_ses *ses, unsigned int
>> max_channels);
>> > /*
>> >  * max deferred close timeout (jiffies) - 2^30
>> >  */
>> >diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c
>> >index 0a8c2fcc9ded..42b5481c884a 100644
>> >--- a/fs/smb/client/sess.c
>> >+++ b/fs/smb/client/sess.c
>> >@@ -264,13 +264,16 @@ int cifs_try_adding_channels(struct cifs_ses *ses)
>> >       return new_chan_count - old_chan_count;
>> > }
>> >
>> >-/*
>> >- * called when multichannel is disabled by the server.
>> >- * this always gets called from smb2_reconnect
>> >- * and cannot get called in parallel threads.
>> >+/**
>> >+ * cifs_decrease_secondary_channels - Reduce the number of active
>> secondary channels
>> >+ * @ses: pointer to the CIFS session structure
>> >+ * @from_reconfigure: if true, only reduce to chan_max; if false, reduce
>> to a single channel
>> >+ *
>> >+ * This function disables and cleans up extra secondary channels for a
>> CIFS session.
>> >+ * If called during reconfiguration, it reduces the channel count to the
>> new maximum (chan_max).
>> >+ * Otherwise, it disables all but the primary channel.
>> >  */
>> >-void
>> >-cifs_disable_secondary_channels(struct cifs_ses *ses)
>> >+void cifs_decrease_secondary_channels(struct cifs_ses *ses, bool
>> from_reconfigure)
>> > {
>> >       int i, chan_count;
>> >       struct TCP_Server_Info *server;
>> >@@ -281,12 +284,13 @@ cifs_disable_secondary_channels(struct cifs_ses
>> *ses)
>> >       if (chan_count == 1)
>> >               goto done;
>> >
>> >-      ses->chan_count = 1;
>> >-
>> >-      /* for all secondary channels reset the need reconnect bit */
>> >-      ses->chans_need_reconnect &= 1;
>> >+      // Update the chan_count to the new maximum
>> >+      if (from_reconfigure)
>> >+              ses->chan_count = ses->chan_max;
>> >+      else
>> >+              ses->chan_count = 1;
>> >
>> >-      for (i = 1; i < chan_count; i++) {
>> >+      for (i = ses->chan_max ; i < chan_count; i++) {
>> >               iface = ses->chans[i].iface;
>> >               server = ses->chans[i].server;
>> >
>> >@@ -318,6 +322,15 @@ cifs_disable_secondary_channels(struct cifs_ses *ses)
>> >               spin_lock(&ses->chan_lock);
>> >       }
>> >
>> >+      /* For extra secondary channels, reset the need reconnect bit */
>> >+      if (ses->chan_count == 1) {
>> >+              cifs_server_dbg(VFS, "server does not support multichannel
>> anymore. Disable all other channels\n");
>> >+              ses->chans_need_reconnect &= 1;
>> >+      } else {
>> >+              cifs_server_dbg(VFS, "Disable extra secondary channels\n");
>> >+              ses->chans_need_reconnect &= ((1UL << ses->chan_max) - 1);
>> >+      }
>> >+
>> > done:
>> >       spin_unlock(&ses->chan_lock);
>> > }
>> >diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
>> >index c3b9d3f6210f..bf9a8dc0e8fc 100644
>> >--- a/fs/smb/client/smb2pdu.c
>> >+++ b/fs/smb/client/smb2pdu.c
>> >@@ -168,7 +168,7 @@ smb2_hdr_assemble(struct smb2_hdr *shdr, __le16
>> smb2_cmd,
>> > static int
>> > cifs_chan_skip_or_disable(struct cifs_ses *ses,
>> >                         struct TCP_Server_Info *server,
>> >-                        bool from_reconnect)
>> >+                        bool from_reconnect, bool from_reconfigure)
>> > {
>> >       struct TCP_Server_Info *pserver;
>> >       unsigned int chan_index;
>> >@@ -206,10 +206,49 @@ cifs_chan_skip_or_disable(struct cifs_ses *ses,
>> >               return -EHOSTDOWN;
>> >       }
>> >
>> >-      cifs_server_dbg(VFS,
>> >-              "server does not support multichannel anymore. Disable all
>> other channels\n");
>> >-      cifs_disable_secondary_channels(ses);
>> >+      cifs_decrease_secondary_channels(ses, from_reconfigure);
>> >
>> >+      return 0;
>> >+}
>> >+
>> >+/**
>> >+ * smb3_sync_ses_channels - Synchronize session channels
>> >+ * with new configuration (cifs_sb_info version)
>> >+ * @cifs_sb: pointer to the CIFS superblock info structure
>> >+ * Returns 0 on success or -EINVAL if scaling is already in progress.
>> >+ */
>> >+int smb3_sync_ses_channels(struct cifs_sb_info *cifs_sb)
>> >+{
>> >+      struct cifs_ses *ses = cifs_sb_master_tcon(cifs_sb)->ses;
>> >+      struct smb3_fs_context *ctx = cifs_sb->ctx;
>> >+      bool from_reconnect = false;
>> >+
>> >+      /* Prevent concurrent scaling operations */
>> >+      spin_lock(&ses->ses_lock);
>> >+      if (ses->flags & CIFS_SES_FLAG_SCALE_CHANNELS) {
>> >+              spin_unlock(&ses->ses_lock);
>> >+              return -EINVAL;
>> >+      }
>> >+      ses->flags |= CIFS_SES_FLAG_SCALE_CHANNELS;
>> >+      spin_unlock(&ses->ses_lock);
>> >+
>> >+      /*
>> >+       * If the old max_channels is less than the new chan_max,
>> >+       * try to add channels to reach the new maximum.
>> >+       * Otherwise, disable or skip extra channels to match the new
>> configuration.
>> >+       */
>> >+      if (ctx->max_channels < ses->chan_max) {
>> >+              mutex_unlock(&ses->session_mutex);
>> >+              cifs_try_adding_channels(ses);
>> >+              mutex_lock(&ses->session_mutex);
>> >+      } else {
>> >+              cifs_chan_skip_or_disable(ses, ses->server,
>> from_reconnect, true);
>> >+      }
>> >+
>> >+      /* Clear scaling flag after operation */
>> >+      spin_lock(&ses->ses_lock);
>> >+      ses->flags &= ~CIFS_SES_FLAG_SCALE_CHANNELS;
>> >+      spin_unlock(&ses->ses_lock);
>> >
>> >       return 0;
>> > }
>> >@@ -356,7 +395,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon
>> *tcon,
>> >       if (ses->chan_count > 1 &&
>> >           !(server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) {
>> >               rc = cifs_chan_skip_or_disable(ses, server,
>> >-                                             from_reconnect);
>> >+                                             from_reconnect, false);
>> >               if (rc) {
>> >                       mutex_unlock(&ses->session_mutex);
>> >                       goto out;
>> >@@ -439,7 +478,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon
>> *tcon,
>> >                        */
>> >
>> >                       rc = cifs_chan_skip_or_disable(ses, server,
>> >-                                                     from_reconnect);
>> >+                                                     from_reconnect,
>> false);
>> >                       goto skip_add_channels;
>> >               } else if (rc)
>> >                       cifs_dbg(FYI, "%s: failed to query server
>> interfaces: %d\n",
>>
>> (for all hunks above) Can we stick to /* */ comments instead of //
>> please?
>>
>> >@@ -1105,8 +1144,7 @@ SMB2_negotiate(const unsigned int xid,
>> >               req->SecurityMode = 0;
>> >
>> >       req->Capabilities = cpu_to_le32(server->vals->req_capabilities);
>> >-      if (ses->chan_max > 1)
>> >-              req->Capabilities |=
>> cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL);
>> >+      req->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL);
>> >
>> >       /* ClientGUID must be zero for SMB2.02 dialect */
>> >       if (server->vals->protocol_id == SMB20_PROT_ID)
>> >@@ -1310,10 +1348,8 @@ int smb3_validate_negotiate(const unsigned int
>> xid, struct cifs_tcon *tcon)
>> >       if (!pneg_inbuf)
>> >               return -ENOMEM;
>> >
>> >-      pneg_inbuf->Capabilities =
>> >-                      cpu_to_le32(server->vals->req_capabilities);
>> >-      if (tcon->ses->chan_max > 1)
>> >-              pneg_inbuf->Capabilities |=
>> cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL);
>> >+      pneg_inbuf->Capabilities =
>> cpu_to_le32(server->vals->req_capabilities);
>> >+      pneg_inbuf->Capabilities |=
>> cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL);
>>
>> This effectively makes query_interfaces worker run even with
>> max_channels=1 -- just a heads up because it didn't look like your
>> original intention.
>>
>>
>> Cheers,
>>
>> Enzo
>>

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ