Linux CIFS filesystem development
 help / color / mirror / Atom feed
* [PATCH v2 0/8] Remount patches v2
@ 2026-05-07 13:44 rajasimandalos
  2026-05-07 13:44 ` [PATCH v2 1/8] smb: client: block non-reconfigurable option changes on remount rajasimandalos
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: rajasimandalos @ 2026-05-07 13:44 UTC (permalink / raw)
  To: linux-cifs; +Cc: smfrench, pc, sprasad, bharathsm, enzo

From: Rajasi Mandal <rajasimandal@microsoft.com>

This series fixes several long-standing problems with how the SMB
client handles mount options at remount time:

  - Options that affect runtime behaviour were silently ignored on
    remount because smb3_reconfigure() only updated cifs_sb->ctx and
    never propagated the new values to the live tcon / TCP_Server_Info
    structures (retrans, echo_interval, the per-tcon options, rasize).

  - Options that have no meaningful semantics on a live mount were
    accepted by the parser, leading to confusing behaviour where the
    user thinks the change took effect (vers, sec, multiuser, UNC,
    username, ...).  These are now rejected with a clear error.

  - Toggling 'lease' / 'nolease' on remount used to leave behind
    deferred file handles and cached directory entries that were
    obtained under the previous lease setting.  This series closes
    deferred files and invalidates cached dirs across the superblock
    when transitioning to nolease.

  - cache=ro and cache=singleclient are special-cased to be rejected
    on remount because changing them at runtime would be unsafe.

Patch 5 is a small refactor that moves struct tcon_list out of misc.c
into cifsglob.h so it can be shared between the new
invalidate_all_cached_dirs_sb() helper in cached_dir.c and the
existing cifs_close_all_deferred_files_sb() in misc.c.  Patch 6 then
uses the shared struct.

Testing
=======

  - Ran xfstests on the patch series.
  - A targeted nolease/cached_dir stress test (lease<->nolease toggle
    cycles, parallel remounters, concurrent I/O, GET/PUT trace pairing,
    deadlock/starvation bounds, hung-task scan).
  - A general remount test covering both reconfigurable and rejected
    option changes plus repeated mount/umount/remount cycles.

All runs clean, no WARN/BUG/UAF/hung-task in dmesg.

Rajasi Mandal (8):
  smb: client: block non-reconfigurable option changes on remount
  smb: client: sync tcon-level options on remount
  smb: client: sync retrans on remount
  smb: client: sync echo_interval on remount
  smb: client: move struct tcon_list to cifsglob.h
  smb: client: allow nolease option to be reconfigured on remount
  smb: client: block cache=ro and cache=singleclient on remount
  smb: client: apply rasize on remount

 fs/smb/client/cached_dir.c |  44 +++++
 fs/smb/client/cached_dir.h |   1 +
 fs/smb/client/cifsglob.h   |   5 +
 fs/smb/client/fs_context.c | 355 ++++++++++++++++++++++++++++++++++++-
 fs/smb/client/misc.c       |   5 -
 fs/smb/client/trace.h      |   2 +
 6 files changed, 403 insertions(+), 9 deletions(-)

-- 
2.43.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v2 1/8] smb: client: block non-reconfigurable option changes on remount
  2026-05-07 13:44 [PATCH v2 0/8] Remount patches v2 rajasimandalos
@ 2026-05-07 13:44 ` rajasimandalos
  2026-05-07 13:44 ` [PATCH v2 2/8] smb: client: sync tcon-level options " rajasimandalos
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: rajasimandalos @ 2026-05-07 13:44 UTC (permalink / raw)
  To: linux-cifs; +Cc: smfrench, pc, sprasad, bharathsm, enzo

From: Rajasi Mandal <rajasimandal@microsoft.com>

Several mount options (seal, sign, vers, ip, rdma, nosharesock,
persistent/resilient handles, etc.) require tearing down the
connection to take effect, but smb3_verify_reconfigure_ctx()
does not reject them.  A remount that changes any of these silently
ignores the new value, confusing users.

Adding simple != checks to smb3_verify_reconfigure_ctx() does not
work with the current code because smb3_init_fs_context() always
creates a fresh context with init defaults on remount.  Since
cifs_show_options() reads many fields from tcon/server/ses rather
than from ctx, the init defaults differ from the runtime state
and every != check would spuriously fire.

Fix this by detecting FS_CONTEXT_FOR_RECONFIGURE in
smb3_init_fs_context() and duplicating the existing cifs_sb->ctx
instead of building one from scratch.  Before duplicating, sync
ctx with runtime state via new helper smb3_sync_ctx_from_tcon()
so the baseline matches what cifs_show_options() displays.  With
this in place, add comprehensive checks in
smb3_verify_reconfigure_ctx() to reject non-reconfigurable option
changes with clear error messages.

Also preserve inherited multichannel/max_channels values in
smb3_handle_conflicting_options() during reconfigure when neither
option is explicitly specified.

Signed-off-by: Rajasi Mandal <rajasimandal@microsoft.com>
---
 fs/smb/client/fs_context.c | 254 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 252 insertions(+), 2 deletions(-)

diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c
index b63ec7ab6e51..d90430e7a648 100644
--- a/fs/smb/client/fs_context.c
+++ b/fs/smb/client/fs_context.c
@@ -717,13 +717,13 @@ static int smb3_handle_conflicting_options(struct fs_context *fc)
 				ctx->multichannel = true;
 			else
 				ctx->multichannel = false;
-		} else {
+		} else if (fc->purpose != FS_CONTEXT_FOR_RECONFIGURE) {
 			ctx->multichannel = false;
 			ctx->max_channels = 1;
 		}
+		/* For reconfigure with neither specified, keep inherited values */
 	}
 
-	//resetting default values as remount doesn't initialize fs_context again
 	ctx->multichannel_specified = false;
 	ctx->max_channels_specified = false;
 
@@ -921,6 +921,85 @@ static void smb3_fs_context_free(struct fs_context *fc)
 	smb3_cleanup_fs_context(ctx);
 }
 
+/*
+ * Sync cifs_sb->ctx with the runtime state visible through /proc/mounts.
+ * cifs_show_options() reads many fields from tcon/server/ses rather than
+ * from ctx.  On remount, libmount reads /proc/mounts and feeds those
+ * values back as mount options.  To avoid false mismatches between the
+ * old ctx and the newly parsed options, we must ensure ctx reflects
+ * the current runtime state before it is duplicated into the new
+ * remount context.
+ *
+ * Note: sb->s_umount is not yet held when VFS calls init_fs_context()
+ * for reconfigure, so in theory concurrent I/O paths could read
+ * cifs_sb->ctx fields (e.g. cifs_symlink_type()) while we write them.
+ * This is safe because all fields written here are word-sized
+ * (bool/int/pointer), so stores are architecturally atomic.  The same
+ * unsynchronized-read pattern already exists in cifs_show_options().
+ */
+static void smb3_sync_ctx_from_tcon(struct cifs_sb_info *cifs_sb)
+{
+	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
+	struct TCP_Server_Info *server = tcon->ses->server;
+	struct cifs_ses *ses = tcon->ses;
+	struct smb3_fs_context *ctx = cifs_sb->ctx;
+	const char *domain;
+	int unicode;
+
+	/*
+	 * Server fields: ops/vals can change during reconnect
+	 * (renegotiation may upgrade the dialect).  nosharesock can
+	 * transition false->true if the server sets
+	 * SMB2_SHAREFLAG_ISOLATED_TRANSPORT during tree connect.
+	 * Read all under srv_lock to get a consistent snapshot.
+	 */
+	spin_lock(&server->srv_lock);
+	ctx->ops = server->ops;
+	ctx->vals = server->vals;
+	ctx->nosharesock = server->nosharesock;
+	spin_unlock(&server->srv_lock);
+
+	/*
+	 * Tcon fields: posix_extensions, unix_ext, use_persistent and
+	 * use_resilient are set during tree connect and do not change
+	 * after that, but read under tc_lock for consistency with the
+	 * convention that tc_lock protects tcon state.
+	 */
+	spin_lock(&tcon->tc_lock);
+	if (tcon->posix_extensions) {
+		ctx->linux_ext = 1;
+		ctx->no_linux_ext = 0;
+	} else if (tcon->unix_ext) {
+		ctx->linux_ext = 1;
+		ctx->no_linux_ext = 0;
+	} else {
+		ctx->linux_ext = 0;
+		ctx->no_linux_ext = 1;
+	}
+	if (tcon->use_persistent) {
+		ctx->persistent = true;
+		ctx->nopersistent = false;
+	}
+	ctx->resilient = tcon->use_resilient;
+	spin_unlock(&tcon->tc_lock);
+
+	/*
+	 * Session fields: domainName and unicode are effectively
+	 * write-once (set during session setup, never freed/replaced
+	 * while the session exists).  Snapshot them under ses_lock
+	 * and kstrdup the domain outside the lock.
+	 */
+	spin_lock(&ses->ses_lock);
+	domain = ses->domainName;
+	unicode = ses->unicode;
+	spin_unlock(&ses->ses_lock);
+
+	if (domain && !ctx->domainname)
+		ctx->domainname = kstrdup(domain, GFP_KERNEL);
+	if (unicode >= 0)
+		ctx->unicode = unicode;
+}
+
 /*
  * Compare the old and new proposed context during reconfigure
  * and check if the changes are compatible.
@@ -990,6 +1069,143 @@ static int smb3_verify_reconfigure_ctx(struct fs_context *fc,
 		cifs_errorf(fc, "can not change nbsessinit during remount\n");
 		return -EINVAL;
 	}
+	if (new_ctx->compress != old_ctx->compress) {
+		cifs_errorf(fc, "can not change compress during remount\n");
+		return -EINVAL;
+	}
+	if (new_ctx->noblocksnd != old_ctx->noblocksnd) {
+		cifs_errorf(fc, "can not change noblocksend during remount\n");
+		return -EINVAL;
+	}
+	if (new_ctx->noautotune != old_ctx->noautotune) {
+		cifs_errorf(fc, "can not change noautotune during remount\n");
+		return -EINVAL;
+	}
+	if (new_ctx->no_sparse != old_ctx->no_sparse) {
+		cifs_errorf(fc, "can not change nosparse during remount\n");
+		return -EINVAL;
+	}
+	if (new_ctx->nodelete != old_ctx->nodelete) {
+		cifs_errorf(fc, "can not change nodelete during remount\n");
+		return -EINVAL;
+	}
+	if (new_ctx->cruid_specified &&
+	    !uid_eq(new_ctx->cred_uid, old_ctx->cred_uid)) {
+		cifs_errorf(fc, "can not change cruid during remount\n");
+		return -EINVAL;
+	}
+	if (new_ctx->port != old_ctx->port) {
+		cifs_errorf(fc, "can not change port during remount\n");
+		return -EINVAL;
+	}
+	if (new_ctx->min_offload != old_ctx->min_offload) {
+		cifs_errorf(fc, "can not change min_enc_offload during remount\n");
+		return -EINVAL;
+	}
+	if (new_ctx->snapshot_time != old_ctx->snapshot_time) {
+		cifs_errorf(fc, "can not change snapshot during remount\n");
+		return -EINVAL;
+	}
+	if (new_ctx->max_credits != old_ctx->max_credits) {
+		cifs_errorf(fc, "can not change max_credits during remount\n");
+		return -EINVAL;
+	}
+	if (new_ctx->handle_timeout != old_ctx->handle_timeout) {
+		cifs_errorf(fc, "can not change handletimeout during remount\n");
+		return -EINVAL;
+	}
+	if (new_ctx->got_ip &&
+	    !cifs_match_ipaddr((struct sockaddr *)&new_ctx->dstaddr,
+			       (struct sockaddr *)&old_ctx->dstaddr)) {
+		cifs_errorf(fc, "can not change ip during remount\n");
+		return -EINVAL;
+	}
+	if (((struct sockaddr *)&new_ctx->srcaddr)->sa_family != AF_UNSPEC &&
+	    memcmp(&new_ctx->srcaddr, &old_ctx->srcaddr, sizeof(new_ctx->srcaddr))) {
+		cifs_errorf(fc, "can not change srcaddr during remount\n");
+		return -EINVAL;
+	}
+	if (memcmp(new_ctx->source_rfc1001_name, old_ctx->source_rfc1001_name,
+		   RFC1001_NAME_LEN)) {
+		cifs_errorf(fc, "can not change netbiosname during remount\n");
+		return -EINVAL;
+	}
+	if (memcmp(new_ctx->target_rfc1001_name, old_ctx->target_rfc1001_name,
+		   RFC1001_NAME_LEN)) {
+		cifs_errorf(fc, "can not change servern during remount\n");
+		return -EINVAL;
+	}
+	if (new_ctx->got_version &&
+	    (new_ctx->ops != old_ctx->ops || new_ctx->vals != old_ctx->vals)) {
+		cifs_errorf(fc, "can not change vers during remount\n");
+		return -EINVAL;
+	}
+	if (new_ctx->witness != old_ctx->witness) {
+		cifs_errorf(fc, "can not change witness during remount\n");
+		return -EINVAL;
+	}
+	if (new_ctx->rootfs != old_ctx->rootfs) {
+		cifs_errorf(fc, "can not change rootfs during remount\n");
+		return -EINVAL;
+	}
+	if (new_ctx->linux_ext != old_ctx->linux_ext ||
+	    new_ctx->no_linux_ext != old_ctx->no_linux_ext) {
+		cifs_errorf(fc, "can not change unix during remount\n");
+		return -EINVAL;
+	}
+	if (new_ctx->nocase != old_ctx->nocase) {
+		cifs_errorf(fc, "can not change nocase during remount\n");
+		return -EINVAL;
+	}
+	if (new_ctx->intr != old_ctx->intr) {
+		cifs_errorf(fc, "can not change intr during remount\n");
+		return -EINVAL;
+	}
+	if (new_ctx->no_psx_acl != old_ctx->no_psx_acl) {
+		cifs_errorf(fc, "can not change acl during remount\n");
+		return -EINVAL;
+	}
+	if (new_ctx->local_lease != old_ctx->local_lease) {
+		cifs_errorf(fc, "can not change locallease during remount\n");
+		return -EINVAL;
+	}
+	if (new_ctx->sign != old_ctx->sign) {
+		cifs_errorf(fc, "can not change sign during remount\n");
+		return -EINVAL;
+	}
+	if (new_ctx->ignore_signature != old_ctx->ignore_signature) {
+		cifs_errorf(fc, "can not change ignore_signature during remount\n");
+		return -EINVAL;
+	}
+	if (new_ctx->seal != old_ctx->seal) {
+		cifs_errorf(fc, "can not change seal during remount\n");
+		return -EINVAL;
+	}
+	if (new_ctx->nosharesock != old_ctx->nosharesock) {
+		cifs_errorf(fc, "can not change nosharesock during remount\n");
+		return -EINVAL;
+	}
+	if (new_ctx->persistent != old_ctx->persistent ||
+	    new_ctx->nopersistent != old_ctx->nopersistent) {
+		cifs_errorf(fc, "can not change persistenthandles during remount\n");
+		return -EINVAL;
+	}
+	if (new_ctx->resilient != old_ctx->resilient) {
+		cifs_errorf(fc, "can not change resilienthandles during remount\n");
+		return -EINVAL;
+	}
+	if (new_ctx->sockopt_tcp_nodelay != old_ctx->sockopt_tcp_nodelay) {
+		cifs_errorf(fc, "can not change tcpnodelay during remount\n");
+		return -EINVAL;
+	}
+	if (new_ctx->domainauto != old_ctx->domainauto) {
+		cifs_errorf(fc, "can not change domainauto during remount\n");
+		return -EINVAL;
+	}
+	if (new_ctx->rdma != old_ctx->rdma) {
+		cifs_errorf(fc, "can not change rdma during remount\n");
+		return -EINVAL;
+	}
 
 	return 0;
 }
@@ -1903,6 +2119,40 @@ int smb3_init_fs_context(struct fs_context *fc)
 	char *nodename = utsname()->nodename;
 	int i;
 
+	/*
+	 * For reconfigure (remount), duplicate the existing mount context
+	 * instead of building one from scratch with init defaults.
+	 *
+	 * VFS sets fc->root before calling init_fs_context for reconfigure,
+	 * so we can access the existing superblock's context.  We first sync
+	 * cifs_sb->ctx with runtime state (tcon/server/ses) so that ctx
+	 * matches what cifs_show_options() displays.  Then we dup old_ctx
+	 * into new_ctx.  The parser will overwrite only the options
+	 * explicitly passed on remount, so any difference between new_ctx
+	 * and old_ctx in smb3_verify_reconfigure_ctx() represents a real,
+	 * intentional change by the user.
+	 */
+	if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
+		struct cifs_sb_info *cifs_sb = CIFS_SB(fc->root->d_sb);
+		int rc;
+
+		smb3_sync_ctx_from_tcon(cifs_sb);
+
+		ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+		if (!ctx)
+			return -ENOMEM;
+
+		rc = smb3_fs_context_dup(ctx, cifs_sb->ctx);
+		if (rc) {
+			kfree(ctx);
+			return rc;
+		}
+
+		fc->fs_private = ctx;
+		fc->ops = &smb3_fs_context_ops;
+		return 0;
+	}
+
 	ctx = kzalloc_obj(struct smb3_fs_context);
 	if (unlikely(!ctx))
 		return -ENOMEM;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 2/8] smb: client: sync tcon-level options on remount
  2026-05-07 13:44 [PATCH v2 0/8] Remount patches v2 rajasimandalos
  2026-05-07 13:44 ` [PATCH v2 1/8] smb: client: block non-reconfigurable option changes on remount rajasimandalos
@ 2026-05-07 13:44 ` rajasimandalos
  2026-05-07 13:44 ` [PATCH v2 3/8] smb: client: sync retrans " rajasimandalos
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: rajasimandalos @ 2026-05-07 13:44 UTC (permalink / raw)
  To: linux-cifs; +Cc: smfrench, pc, sprasad, bharathsm, enzo

From: Rajasi Mandal <rajasimandal@microsoft.com>

Several mount options are stored on each cifs_tcon at mount time but
smb3_reconfigure() only updates cifs_sb->ctx. A remount that changes
any of these options silently has no effect because the runtime code
reads from the tcon fields, not from the superblock context.

The affected options and their tcon fields are:
  retry           -> tcon->retry
  max_cached_dirs -> tcon->max_cached_dirs

Add smb3_sync_tcon_opts() which walks the tlink_tree under
tlink_tree_lock and propagates these values from ctx to every tcon
under tc_lock. Call it from smb3_reconfigure() after the context has
been updated.

Signed-off-by: Rajasi Mandal <rajasimandal@microsoft.com>
---
 fs/smb/client/fs_context.c | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c
index d90430e7a648..20c17f7cbbf8 100644
--- a/fs/smb/client/fs_context.c
+++ b/fs/smb/client/fs_context.c
@@ -1264,6 +1264,38 @@ static void smb3_sync_ses_chan_max(struct cifs_ses *ses, unsigned int max_channe
 	spin_unlock(&ses->chan_lock);
 }
 
+/*
+ * Synchronize tcon options that are derived from ctx across all tcons
+ * associated with this superblock.  These fields are consulted at runtime
+ * (reconnect, I/O, unlink/rmdir) so remount needs to update the live
+ * tcons in addition to cifs_sb->ctx.
+ */
+static void smb3_sync_tcon_opts(struct cifs_sb_info *cifs_sb,
+				struct smb3_fs_context *ctx)
+{
+	struct tcon_link *tlink;
+	struct cifs_tcon *tcon;
+	struct rb_node *node;
+
+	spin_lock(&cifs_sb->tlink_tree_lock);
+	for (node = rb_first(&cifs_sb->tlink_tree); node; node = rb_next(node)) {
+		tlink = rb_entry(node, struct tcon_link, tl_rbnode);
+		tcon = tlink_tcon(tlink);
+		if (IS_ERR(tcon))
+			continue;
+
+		spin_lock(&tcon->tc_lock);
+		tcon->retry = ctx->retry;
+		/*
+		 * Note: this updates the limit for new cached dir opens
+		 * but does not resize or evict existing cached dirents.
+		 */
+		tcon->max_cached_dirs = ctx->max_cached_dirs;
+		spin_unlock(&tcon->tc_lock);
+	}
+	spin_unlock(&cifs_sb->tlink_tree_lock);
+}
+
 static int smb3_reconfigure(struct fs_context *fc)
 {
 	struct smb3_fs_context *ctx = smb3_fc2context(fc);
@@ -1397,6 +1429,8 @@ static int smb3_reconfigure(struct fs_context *fc)
 	if (!rc)
 		rc = dfs_cache_remount_fs(cifs_sb);
 #endif
+	if (!rc)
+		smb3_sync_tcon_opts(cifs_sb, cifs_sb->ctx);
 
 	return rc;
 }
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 3/8] smb: client: sync retrans on remount
  2026-05-07 13:44 [PATCH v2 0/8] Remount patches v2 rajasimandalos
  2026-05-07 13:44 ` [PATCH v2 1/8] smb: client: block non-reconfigurable option changes on remount rajasimandalos
  2026-05-07 13:44 ` [PATCH v2 2/8] smb: client: sync tcon-level options " rajasimandalos
@ 2026-05-07 13:44 ` rajasimandalos
  2026-05-07 13:44 ` [PATCH v2 4/8] smb: client: sync echo_interval " rajasimandalos
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: rajasimandalos @ 2026-05-07 13:44 UTC (permalink / raw)
  To: linux-cifs; +Cc: smfrench, pc, sprasad, bharathsm, enzo

From: Rajasi Mandal <rajasimandal@microsoft.com>

The retrans mount option controls how many times the client retries
sending a request before giving up.  Although remount already stored
the new value in cifs_sb->ctx, it was never propagated to
server->retrans, so the running connection kept using the old count.

Introduce smb3_sync_server_opts() to push ctx options that live on
TCP_Server_Info into the live server struct after a successful
remount.  For now it handles retrans; subsequent patches will extend
it to other server-level knobs.

The assignment is guarded (if ctx->retrans) so that a bare
'mount -o remount' (which re-parses /proc/mounts and gets
retrans=0 because cifs_show_options does not print it) does not
accidentally reset the value to zero.

Signed-off-by: Rajasi Mandal <rajasimandal@microsoft.com>
---
 fs/smb/client/fs_context.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c
index 20c17f7cbbf8..de682a5d2b68 100644
--- a/fs/smb/client/fs_context.c
+++ b/fs/smb/client/fs_context.c
@@ -1296,6 +1296,23 @@ static void smb3_sync_tcon_opts(struct cifs_sb_info *cifs_sb,
 	spin_unlock(&cifs_sb->tlink_tree_lock);
 }
 
+/*
+ * Synchronize server-level options that are stored on TCP_Server_Info
+ * at mount time.  These fields are consulted at runtime (retry logic)
+ * so remount needs to update the live server struct in addition to
+ * cifs_sb->ctx.
+ */
+static void smb3_sync_server_opts(struct cifs_sb_info *cifs_sb)
+{
+	struct TCP_Server_Info *server = cifs_sb_master_tcon(cifs_sb)->ses->server;
+	struct smb3_fs_context *ctx = cifs_sb->ctx;
+
+	spin_lock(&server->srv_lock);
+	if (ctx->retrans)
+		server->retrans = ctx->retrans;
+	spin_unlock(&server->srv_lock);
+}
+
 static int smb3_reconfigure(struct fs_context *fc)
 {
 	struct smb3_fs_context *ctx = smb3_fc2context(fc);
@@ -1431,6 +1448,8 @@ static int smb3_reconfigure(struct fs_context *fc)
 #endif
 	if (!rc)
 		smb3_sync_tcon_opts(cifs_sb, cifs_sb->ctx);
+	if (!rc)
+		smb3_sync_server_opts(cifs_sb);
 
 	return rc;
 }
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 4/8] smb: client: sync echo_interval on remount
  2026-05-07 13:44 [PATCH v2 0/8] Remount patches v2 rajasimandalos
                   ` (2 preceding siblings ...)
  2026-05-07 13:44 ` [PATCH v2 3/8] smb: client: sync retrans " rajasimandalos
@ 2026-05-07 13:44 ` rajasimandalos
  2026-05-07 13:44 ` [PATCH v2 5/8] smb: client: move struct tcon_list to cifsglob.h rajasimandalos
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: rajasimandalos @ 2026-05-07 13:44 UTC (permalink / raw)
  To: linux-cifs; +Cc: smfrench, pc, sprasad, bharathsm, enzo

From: Rajasi Mandal <rajasimandal@microsoft.com>

echo_interval is accepted during remount parsing but
server->echo_interval is only set in cifs_get_tcp_session() at
connection setup. A remount with a new echo_interval value silently
has no effect on the echo keepalive timer.

Add echo_interval to smb3_sync_server_opts() under srv_lock so the
new interval is pushed to the live server struct. Reschedule the echo
delayed work via mod_delayed_work() so the new interval takes effect
immediately rather than after the current timer fires.

Signed-off-by: Rajasi Mandal <rajasimandal@microsoft.com>
---
 fs/smb/client/fs_context.c | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c
index de682a5d2b68..972d3969f7e7 100644
--- a/fs/smb/client/fs_context.c
+++ b/fs/smb/client/fs_context.c
@@ -1298,9 +1298,9 @@ static void smb3_sync_tcon_opts(struct cifs_sb_info *cifs_sb,
 
 /*
  * Synchronize server-level options that are stored on TCP_Server_Info
- * at mount time.  These fields are consulted at runtime (retry logic)
- * so remount needs to update the live server struct in addition to
- * cifs_sb->ctx.
+ * at mount time.  These fields are consulted at runtime (echo work,
+ * retry logic) so remount needs to update the live server struct in
+ * addition to cifs_sb->ctx.
  */
 static void smb3_sync_server_opts(struct cifs_sb_info *cifs_sb)
 {
@@ -1310,7 +1310,13 @@ static void smb3_sync_server_opts(struct cifs_sb_info *cifs_sb)
 	spin_lock(&server->srv_lock);
 	if (ctx->retrans)
 		server->retrans = ctx->retrans;
+	if (ctx->echo_interval)
+		server->echo_interval = ctx->echo_interval * HZ;
 	spin_unlock(&server->srv_lock);
+
+	if (ctx->echo_interval)
+		mod_delayed_work(cifsiod_wq, &server->echo,
+				 server->echo_interval);
 }
 
 static int smb3_reconfigure(struct fs_context *fc)
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 5/8] smb: client: move struct tcon_list to cifsglob.h
  2026-05-07 13:44 [PATCH v2 0/8] Remount patches v2 rajasimandalos
                   ` (3 preceding siblings ...)
  2026-05-07 13:44 ` [PATCH v2 4/8] smb: client: sync echo_interval " rajasimandalos
@ 2026-05-07 13:44 ` rajasimandalos
  2026-05-07 13:44 ` [PATCH v2 6/8] smb: client: allow nolease option to be reconfigured on remount rajasimandalos
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: rajasimandalos @ 2026-05-07 13:44 UTC (permalink / raw)
  To: linux-cifs; +Cc: smfrench, pc, sprasad, bharathsm, enzo

From: Rajasi Mandal <rajasimandal@microsoft.com>

struct tcon_list is a small list-node wrapper that pairs a list_head
with a cifs_tcon pointer, used to safely iterate tcons under a
superblock outside tlink_tree_lock.

It is currently file-static in misc.c, used only by
cifs_close_all_deferred_files_sb().  Move it next to the similar
struct file_list in cifsglob.h so that other _sb() iteration helpers
can reuse it instead of redefining the same shape locally.

No functional change.

Signed-off-by: Rajasi Mandal <rajasimandal@microsoft.com>
---
 fs/smb/client/cifsglob.h | 5 +++++
 fs/smb/client/misc.c     | 5 -----
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index 82e0adc1dabd..2a8b955638e7 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -1807,6 +1807,11 @@ struct file_list {
 	struct cifsFileInfo *cfile;
 };
 
+struct tcon_list {
+	struct list_head entry;
+	struct cifs_tcon *tcon;
+};
+
 struct cifs_mount_ctx {
 	struct cifs_sb_info *cifs_sb;
 	struct smb3_fs_context *fs_ctx;
diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c
index 0c54b9b79a2c..7ef135dc8268 100644
--- a/fs/smb/client/misc.c
+++ b/fs/smb/client/misc.c
@@ -28,11 +28,6 @@
 #include "fs_context.h"
 #include "cached_dir.h"
 
-struct tcon_list {
-	struct list_head entry;
-	struct cifs_tcon *tcon;
-};
-
 /* The xid serves as a useful identifier for each incoming vfs request,
    in a similar way to the mid which is useful to track each sent smb,
    and CurrentXid can also provide a running counter (although it
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 6/8] smb: client: allow nolease option to be reconfigured on remount
  2026-05-07 13:44 [PATCH v2 0/8] Remount patches v2 rajasimandalos
                   ` (4 preceding siblings ...)
  2026-05-07 13:44 ` [PATCH v2 5/8] smb: client: move struct tcon_list to cifsglob.h rajasimandalos
@ 2026-05-07 13:44 ` rajasimandalos
  2026-05-07 13:44 ` [PATCH v2 7/8] smb: client: block cache=ro and cache=singleclient " rajasimandalos
  2026-05-07 13:44 ` [PATCH v2 8/8] smb: client: apply rasize " rajasimandalos
  7 siblings, 0 replies; 9+ messages in thread
From: rajasimandalos @ 2026-05-07 13:44 UTC (permalink / raw)
  To: linux-cifs; +Cc: smfrench, pc, sprasad, bharathsm, enzo

From: Rajasi Mandal <rajasimandal@microsoft.com>

Changing nolease via remount is silently accepted but has no effect:
the value is not propagated to the live tcon, and stale lease-bearing
state from before the switch keeps using the old behavior.

Make nolease take effect on remount:

 - Propagate ctx->no_lease to tcon->no_lease in smb3_sync_tcon_opts()
   so future opens honor the new setting.

 - On switch to nolease, drop deferred file handles via
   cifs_close_all_deferred_files_sb() (each holds an active lease).

 - On switch to nolease, evict cached directory fids via the new
   invalidate_all_cached_dirs_sb() helper (each holds a directory
   lease).

invalidate_all_cached_dirs_sb() mirrors cifs_close_all_deferred_files_sb():
take tc_count refs on every tcon under tlink_tree_lock, drop the lock,
then call invalidate_all_cached_dirs() per tcon (it can sleep).  Two
new tcon_ref trace tags are added for the audit trail.

Existing open handles keep their leases until the server breaks them
or userspace closes the file -- nolease only governs new opens.

Signed-off-by: Rajasi Mandal <rajasimandal@microsoft.com>
---
 fs/smb/client/cached_dir.c | 44 ++++++++++++++++++++++++++++++++++++++
 fs/smb/client/cached_dir.h |  1 +
 fs/smb/client/fs_context.c | 22 +++++++++++++++++++
 fs/smb/client/trace.h      |  2 ++
 4 files changed, 69 insertions(+)

diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c
index 88d5e9a32f28..58d68a8f4d41 100644
--- a/fs/smb/client/cached_dir.c
+++ b/fs/smb/client/cached_dir.c
@@ -637,6 +637,50 @@ void invalidate_all_cached_dirs(struct cifs_tcon *tcon, bool sync)
 		flush_delayed_work(&cfids->laundromat_work);
 }
 
+/*
+ * Invalidate cached directory entries across all tcons under a
+ * superblock.  Collect references on each tcon under tlink_tree_lock,
+ * then call invalidate_all_cached_dirs() outside the spinlock since it
+ * can sleep.  Holding a tc_count reference prevents the tcon from being
+ * freed by tlink_expire_delayed() between dropping the spinlock and
+ * the call.
+ */
+void invalidate_all_cached_dirs_sb(struct cifs_sb_info *cifs_sb)
+{
+	struct rb_root *root = &cifs_sb->tlink_tree;
+	struct rb_node *node;
+	struct cifs_tcon *tcon;
+	struct tcon_link *tlink;
+	struct tcon_list *tmp_list, *q;
+	LIST_HEAD(tcon_head);
+
+	spin_lock(&cifs_sb->tlink_tree_lock);
+	for (node = rb_first(root); node; node = rb_next(node)) {
+		tlink = rb_entry(node, struct tcon_link, tl_rbnode);
+		tcon = tlink_tcon(tlink);
+		if (IS_ERR(tcon))
+			continue;
+		tmp_list = kmalloc_obj(struct tcon_list, GFP_ATOMIC);
+		if (!tmp_list)
+			break;
+		tmp_list->tcon = tcon;
+		spin_lock(&tcon->tc_lock);
+		++tcon->tc_count;
+		trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
+				    netfs_trace_tcon_ref_get_cached_inval_sb);
+		spin_unlock(&tcon->tc_lock);
+		list_add_tail(&tmp_list->entry, &tcon_head);
+	}
+	spin_unlock(&cifs_sb->tlink_tree_lock);
+
+	list_for_each_entry_safe(tmp_list, q, &tcon_head, entry) {
+		invalidate_all_cached_dirs(tmp_list->tcon, true);
+		list_del(&tmp_list->entry);
+		cifs_put_tcon(tmp_list->tcon, netfs_trace_tcon_ref_put_cached_inval_sb);
+		kfree(tmp_list);
+	}
+}
+
 static void
 cached_dir_offload_close(struct work_struct *work)
 {
diff --git a/fs/smb/client/cached_dir.h b/fs/smb/client/cached_dir.h
index fc756836da95..606ba2a0b64f 100644
--- a/fs/smb/client/cached_dir.h
+++ b/fs/smb/client/cached_dir.h
@@ -90,6 +90,7 @@ void close_cached_dir(struct cached_fid *cfid);
 void drop_cached_dir_by_name(const unsigned int xid, struct cifs_tcon *tcon,
 			     const char *name, struct cifs_sb_info *cifs_sb);
 void close_all_cached_dirs(struct cifs_sb_info *cifs_sb);
+void invalidate_all_cached_dirs_sb(struct cifs_sb_info *cifs_sb);
 void invalidate_all_cached_dirs(struct cifs_tcon *tcon, bool sync);
 bool cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]);
 
diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c
index 972d3969f7e7..01c57adb0ef1 100644
--- a/fs/smb/client/fs_context.c
+++ b/fs/smb/client/fs_context.c
@@ -35,6 +35,7 @@
 #include "nterr.h"
 #include "rfc1002pdu.h"
 #include "fs_context.h"
+#include "cached_dir.h"
 
 DEFINE_MUTEX(cifs_mount_mutex);
 
@@ -1286,6 +1287,7 @@ static void smb3_sync_tcon_opts(struct cifs_sb_info *cifs_sb,
 
 		spin_lock(&tcon->tc_lock);
 		tcon->retry = ctx->retry;
+		tcon->no_lease = ctx->no_lease;
 		/*
 		 * Note: this updates the limit for new cached dir opens
 		 * but does not resize or evict existing cached dirents.
@@ -1294,6 +1296,26 @@ static void smb3_sync_tcon_opts(struct cifs_sb_info *cifs_sb,
 		spin_unlock(&tcon->tc_lock);
 	}
 	spin_unlock(&cifs_sb->tlink_tree_lock);
+
+	/*
+	 * When switching to nolease, close deferred file handles and
+	 * invalidate cached directory entries.  Both hold leases from
+	 * before the switch; without cleaning them up, those handles
+	 * continue using lease-based caching despite nolease being set.
+	 *
+	 * Note: files already open with leases (e.g. RWH) by applications
+	 * are not affected -- nolease only governs new opens.  Existing
+	 * handles retain their leases until the server sends a lease break
+	 * or the application closes the handle.
+	 *
+	 * Both _sb() helpers iterate all tcons internally and handle
+	 * their own locking.  They can sleep, so they must be called
+	 * outside tlink_tree_lock.
+	 */
+	if (ctx->no_lease) {
+		cifs_close_all_deferred_files_sb(cifs_sb);
+		invalidate_all_cached_dirs_sb(cifs_sb);
+	}
 }
 
 /*
diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h
index b99ec5a417fa..cec34e69e191 100644
--- a/fs/smb/client/trace.h
+++ b/fs/smb/client/trace.h
@@ -173,6 +173,7 @@
 	EM(netfs_trace_tcon_ref_free_ipc,		"FRE Ipc   ") \
 	EM(netfs_trace_tcon_ref_free_ipc_fail,		"FRE Ipc-F ") \
 	EM(netfs_trace_tcon_ref_free_reconnect_server,	"FRE Reconn") \
+	EM(netfs_trace_tcon_ref_get_cached_inval_sb,	"GET Ch-IvS") \
 	EM(netfs_trace_tcon_ref_get_cached_laundromat,	"GET Ch-Lau") \
 	EM(netfs_trace_tcon_ref_get_cached_lease_break,	"GET Ch-Lea") \
 	EM(netfs_trace_tcon_ref_get_cancelled_close,	"GET Cn-Cls") \
@@ -185,6 +186,7 @@
 	EM(netfs_trace_tcon_ref_new_ipc,		"NEW Ipc   ") \
 	EM(netfs_trace_tcon_ref_new_reconnect_server,	"NEW Reconn") \
 	EM(netfs_trace_tcon_ref_put_cached_close,	"PUT Ch-Cls") \
+	EM(netfs_trace_tcon_ref_put_cached_inval_sb,	"PUT Ch-IvS") \
 	EM(netfs_trace_tcon_ref_put_cancelled_close,	"PUT Cn-Cls") \
 	EM(netfs_trace_tcon_ref_put_cancelled_close_fid, "PUT Cn-Fid") \
 	EM(netfs_trace_tcon_ref_put_cancelled_mid,	"PUT Cn-Mid") \
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 7/8] smb: client: block cache=ro and cache=singleclient on remount
  2026-05-07 13:44 [PATCH v2 0/8] Remount patches v2 rajasimandalos
                   ` (5 preceding siblings ...)
  2026-05-07 13:44 ` [PATCH v2 6/8] smb: client: allow nolease option to be reconfigured on remount rajasimandalos
@ 2026-05-07 13:44 ` rajasimandalos
  2026-05-07 13:44 ` [PATCH v2 8/8] smb: client: apply rasize " rajasimandalos
  7 siblings, 0 replies; 9+ messages in thread
From: rajasimandalos @ 2026-05-07 13:44 UTC (permalink / raw)
  To: linux-cifs; +Cc: smfrench, pc, sprasad, bharathsm, enzo

From: Rajasi Mandal <rajasimandal@microsoft.com>

cache=ro and cache=singleclient are mount-time environment declarations
where the admin promises that the share is read-only or exclusively
accessed.  The client bypasses server-based coherency (oplocks/leases)
and caches aggressively based on this promise.

These modes were intentionally excluded from smb3_update_mnt_flags()
when it was introduced in commit 2d39f50c2b15 ("cifs: move update of
flags into a separate function") — only cache=strict, cache=none and
cache=loose were made reconfigurable.  However, remount currently
silently accepts cache=ro and cache=singleclient without actually
applying them, which is confusing.

Add explicit checks in smb3_verify_reconfigure_ctx() to reject
attempts to change these options during remount with a clear error
message.

Signed-off-by: Rajasi Mandal <rajasimandal@microsoft.com>
---
 fs/smb/client/fs_context.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c
index 01c57adb0ef1..009b9480b5cc 100644
--- a/fs/smb/client/fs_context.c
+++ b/fs/smb/client/fs_context.c
@@ -1207,6 +1207,15 @@ static int smb3_verify_reconfigure_ctx(struct fs_context *fc,
 		cifs_errorf(fc, "can not change rdma during remount\n");
 		return -EINVAL;
 	}
+	/* init default: cache_ro = false, cache_rw = false (i.e. cache=strict) */
+	if (new_ctx->cache_ro != old_ctx->cache_ro) {
+		cifs_errorf(fc, "can not change cache=ro during remount\n");
+		return -EINVAL;
+	}
+	if (new_ctx->cache_rw != old_ctx->cache_rw) {
+		cifs_errorf(fc, "can not change cache=singleclient during remount\n");
+		return -EINVAL;
+	}
 
 	return 0;
 }
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 8/8] smb: client: apply rasize on remount
  2026-05-07 13:44 [PATCH v2 0/8] Remount patches v2 rajasimandalos
                   ` (6 preceding siblings ...)
  2026-05-07 13:44 ` [PATCH v2 7/8] smb: client: block cache=ro and cache=singleclient " rajasimandalos
@ 2026-05-07 13:44 ` rajasimandalos
  7 siblings, 0 replies; 9+ messages in thread
From: rajasimandalos @ 2026-05-07 13:44 UTC (permalink / raw)
  To: linux-cifs; +Cc: smfrench, pc, sprasad, bharathsm, enzo

From: Rajasi Mandal <rajasimandal@microsoft.com>

rasize is accepted during remount parsing but sb->s_bdi->ra_pages is
only set in cifs_read_super() at mount time. A remount with a new
rasize value silently has no effect on the readahead window.

Update ra_pages in smb3_reconfigure() after the context has been
duplicated, using the same logic as cifs_read_super(): if rasize is
set, use it directly; otherwise fall back to 2 * rsize.

Signed-off-by: Rajasi Mandal <rajasimandal@microsoft.com>
---
 fs/smb/client/fs_context.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c
index 009b9480b5cc..b10fd05c0af0 100644
--- a/fs/smb/client/fs_context.c
+++ b/fs/smb/client/fs_context.c
@@ -1356,7 +1356,7 @@ static int smb3_reconfigure(struct fs_context *fc)
 	struct dentry *root = fc->root;
 	struct cifs_sb_info *cifs_sb = CIFS_SB(root->d_sb);
 	struct cifs_ses *ses = cifs_sb_master_tcon(cifs_sb)->ses;
-	unsigned int rsize = ctx->rsize, wsize = ctx->wsize;
+	unsigned int rsize = ctx->rsize, wsize = ctx->wsize, rasize = ctx->rasize;
 	char *new_password = NULL, *new_password2 = NULL;
 	bool need_recon = false;
 	int rc;
@@ -1472,13 +1472,20 @@ static int smb3_reconfigure(struct fs_context *fc)
 	STEAL_STRING(cifs_sb, ctx, nodename);
 	STEAL_STRING(cifs_sb, ctx, iocharset);
 
-	/* if rsize or wsize not passed in on remount, use previous values */
+	/* if rsize, wsize, or rasize not passed in on remount, use previous values */
 	ctx->rsize = rsize ? CIFS_ALIGN_RSIZE(fc, rsize) : cifs_sb->ctx->rsize;
 	ctx->wsize = wsize ? CIFS_ALIGN_WSIZE(fc, wsize) : cifs_sb->ctx->wsize;
+	ctx->rasize = rasize ? rasize : cifs_sb->ctx->rasize;
 
 	smb3_cleanup_fs_context_contents(cifs_sb->ctx);
 	rc = smb3_fs_context_dup(cifs_sb->ctx, ctx);
 	smb3_update_mnt_flags(cifs_sb);
+
+	if (cifs_sb->ctx->rasize)
+		root->d_sb->s_bdi->ra_pages = cifs_sb->ctx->rasize / PAGE_SIZE;
+	else
+		root->d_sb->s_bdi->ra_pages = 2 * (cifs_sb->ctx->rsize / PAGE_SIZE);
+
 #ifdef CONFIG_CIFS_DFS_UPCALL
 	if (!rc)
 		rc = dfs_cache_remount_fs(cifs_sb);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-05-07 13:45 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-07 13:44 [PATCH v2 0/8] Remount patches v2 rajasimandalos
2026-05-07 13:44 ` [PATCH v2 1/8] smb: client: block non-reconfigurable option changes on remount rajasimandalos
2026-05-07 13:44 ` [PATCH v2 2/8] smb: client: sync tcon-level options " rajasimandalos
2026-05-07 13:44 ` [PATCH v2 3/8] smb: client: sync retrans " rajasimandalos
2026-05-07 13:44 ` [PATCH v2 4/8] smb: client: sync echo_interval " rajasimandalos
2026-05-07 13:44 ` [PATCH v2 5/8] smb: client: move struct tcon_list to cifsglob.h rajasimandalos
2026-05-07 13:44 ` [PATCH v2 6/8] smb: client: allow nolease option to be reconfigured on remount rajasimandalos
2026-05-07 13:44 ` [PATCH v2 7/8] smb: client: block cache=ro and cache=singleclient " rajasimandalos
2026-05-07 13:44 ` [PATCH v2 8/8] smb: client: apply rasize " rajasimandalos

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox