mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Jonathan Flynn <jonathan.flynn@hammerspace.com>
To: NeilBrown <neil@brown.name>,
	Alexander Viro <viro@zeniv.linux.org.uk>,
	Christian Brauner <brauner@kernel.org>,
	Trond Myklebust <trondmy@kernel.org>,
	Anna Schumaker <anna@kernel.org>
Cc: Jeff Layton <jlayton@kernel.org>, Chuck Lever <cel@kernel.org>,
	Jan Kara <jack@suse.cz>, Jonathan Corbet <corbet@lwn.net>,
	linux-doc@vger.kernel.org, linux-nfs@vger.kernel.org,
	linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH RFC 03/12] nfs: tell the VFS when two names can be one entry on the server
Date: Thu, 29 Oct 2026 12:09:48 -0600	[thread overview]
Message-ID: <20261029180957.387138-4-jonathan.flynn@hammerspace.com> (raw)
In-Reply-To: <20261029180957.387138-1-jonathan.flynn@hammerspace.com>

A server that reports itself case-insensitive, or as not preserving
case, can make one entry of two names that the client caches apart,
because the client compares names byte for byte.  Mark the filesystem
on such a server with SB_I_NAMES_COLLIDE, from "vfs: allow the parent
lock to be shared for O_CREAT", so that the VFS keeps the exclusive
directory lock for creates there: the per-name exclusion that replaces
the lock for a create made with it held shared covers one byte string,
not the names the server treats as equal.  The client learns this when
it reads the server's capabilities, which it does again on remount, on
migration and when a later mount reuses the filesystem, and it updates
the mark each time.

This is a conservative fallback.  A server that declares itself
case-insensitive should itself treat the two names as one, but one that
did not was seen making two files of one name when such creates arrived
together.  No filesystem relies on the mark yet, so nothing changes.

Signed-off-by: Jonathan Flynn <jonathan.flynn@hammerspace.com>
---
 fs/nfs/client.c   |  1 +
 fs/nfs/internal.h | 35 +++++++++++++++++++++++++++++++++++
 fs/nfs/nfs4proc.c |  1 +
 fs/nfs/super.c    |  2 ++
 4 files changed, 39 insertions(+)

diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index ea99b3c105..30206b309c 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -975,6 +975,7 @@ static int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, str
 			server->caps &= ~(NFS_CAP_CASE_INSENSITIVE |
 					  NFS_CAP_CASE_NONPRESERVING);
 		}
+		nfs_server_publish_name_caps(server);
 	}
 
 	if (clp->rpc_ops->discover_trunking != NULL &&
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 5af6a032f1..34b8aff4e0 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -224,6 +224,41 @@ int nfs_init_server_rpcclient(struct nfs_server *, const struct rpc_timeout *t,
 struct nfs_server *nfs_alloc_server(void);
 void nfs_server_copy_userdata(struct nfs_server *, struct nfs_server *);
 
+/*
+ * Tell the VFS whether this server can make one directory entry of two names
+ * that differ as byte strings.  NFS installs no ->d_hash or ->d_compare, so
+ * the dcache compares names byte for byte, while a server that is
+ * case-insensitive, or does not preserve case, makes "foo" and "FOO" one
+ * entry.  On such a server SB_I_NAMES_COLLIDE keeps the parent of an O_CREAT
+ * open locked exclusive; see atomic_open_create_shared() in fs/namei.c.
+ *
+ * server->caps is derived again on every probe of the server: at mount, at
+ * remount, on migration, and when a later mount reuses this superblock
+ * (nfs4_proc_get_root()).  So this is called wherever the case bits of
+ * server->caps are written, once they have their final value, and when the
+ * superblock is set up, because the first probe runs before it exists.  The
+ * value is what the probed file handle reported, normally the mount root's;
+ * the homogeneous attribute is not fetched, so a server whose answer differs
+ * below the root is described by the root.  Migration runs the probe from
+ * the state manager without ->s_umount, so the update is atomic, and it is
+ * made only when the value changes.  Two probes of one server at once can
+ * leave the bit as the earlier of them derived it; that needs the server's
+ * answer to change between them, and server->caps has the same race.
+ */
+static inline void nfs_server_publish_name_caps(struct nfs_server *server)
+{
+	struct super_block *sb = server->super;
+	unsigned long bits = 0;
+
+	if (!sb)
+		return;
+	if (READ_ONCE(server->caps) & (NFS_CAP_CASE_INSENSITIVE |
+				       NFS_CAP_CASE_NONPRESERVING))
+		bits = SB_I_NAMES_COLLIDE;
+	if ((READ_ONCE(sb->s_iflags) & SB_I_NAMES_COLLIDE) != bits)
+		set_mask_bits(&sb->s_iflags, SB_I_NAMES_COLLIDE, bits);
+}
+
 extern void nfs_put_client(struct nfs_client *);
 extern void nfs_free_client(struct nfs_client *);
 void nfs_cb_idr_remove(struct nfs_client *clp);
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 113c199a6f..fac8450b65 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -4020,6 +4020,7 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f
 		if (nfs4_server_delegtime_capable(&res))
 			caps |= NFS_CAP_DELEGTIME;
 		WRITE_ONCE(server->caps, caps);
+		nfs_server_publish_name_caps(server);
 
 		memcpy(server->cache_consistency_bitmask, res.attr_bitmask, sizeof(server->cache_consistency_bitmask));
 		server->cache_consistency_bitmask[0] &= FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE;
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 23292680ad..e70cef88fb 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -1082,6 +1082,8 @@ static void nfs_fill_super(struct super_block *sb, struct nfs_fs_context *ctx)
 			sb->s_export_op = &nfs_export_ops;
 		break;
 	}
+	/* The first probe of the caps ran before this superblock existed */
+	nfs_server_publish_name_caps(server);
 
 	sb->s_magic = NFS_SUPER_MAGIC;
 
-- 
2.43.0


  parent reply	other threads:[~2026-10-29 18:10 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-28  4:06 [PATCH v4 0/9] nfs: prepare for locking changes NeilBrown
2026-10-28  4:06 ` [PATCH v4 1/9] nfs: fix open-blocking with d_fsdata NeilBrown
2026-10-28  4:06 ` [PATCH v4 2/9] nfs: fix nfs_call_unlink() NeilBrown
2026-10-29 18:09   ` [PATCH 0/3] nfs: fix the silly-delete hand-off that 2/9 makes reachable Jonathan Flynn
2026-10-29 18:09     ` [PATCH 1/3] nfs: do not transfer a sillydelete onto a blocked dentry Jonathan Flynn
2026-10-29 18:09     ` [PATCH 2/3] nfs: answer a handed-off silly-delete on a removal target with -EBUSY Jonathan Flynn
2026-10-29 18:09     ` [PATCH 3/3] nfs: take rmdir_sem for a silly-delete only when its REMOVE is sent Jonathan Flynn
2026-10-29 21:58     ` [PATCH 0/3] nfs: fix the silly-delete hand-off that 2/9 makes reachable NeilBrown
2026-10-28  4:06 ` [PATCH v4 3/9] nfs: remove d_drop()/d_alloc_parallel() from nfs_atomic_open() NeilBrown
2026-10-28  4:06 ` [PATCH v4 4/9] nfs: use d_splice_alias() in nfs_link() NeilBrown
2026-10-28  4:06 ` [PATCH v4 5/9] nfs: don't d_drop() before d_splice_alias() NeilBrown
2026-10-28  4:06 ` [PATCH v4 6/9] nfs: don't d_drop() before d_splice_alias() in atomic_create NeilBrown
2026-10-28  4:06 ` [PATCH v4 7/9] nfs: Use d_alloc_trylock() in nfs_prime_dcache() NeilBrown
2026-10-28  4:06 ` [PATCH v4 8/9] nfs: use d_alloc_trylock() in silly-rename NeilBrown
2026-10-28  4:06 ` [PATCH v4 9/9] nfs: use d_duplicate() NeilBrown
2026-10-28 17:21 ` [PATCH v4 0/9] nfs: prepare for locking changes Anna Schumaker
2026-10-29 18:09 ` [PATCH RFC 00/12] vfs, nfs: concurrent create and unlink in one directory Jonathan Flynn
2026-10-29 18:09   ` [PATCH RFC 01/12] nfs: update a server's capabilities in one step Jonathan Flynn
2026-10-29 18:09   ` [PATCH RFC 02/12] vfs: allow the parent lock to be shared for O_CREAT Jonathan Flynn
2026-10-29 18:09   ` Jonathan Flynn [this message]
2026-10-29 22:23     ` [PATCH RFC 03/12] nfs: tell the VFS when two names can be one entry on the server NeilBrown
2026-10-29 18:09   ` [PATCH RFC 04/12] nfs: concurrent O_CREAT in one NFSv4 directory Jonathan Flynn
2026-10-29 18:09   ` [PATCH RFC 05/12] vfs: factor file removal into a helper, no functional change Jonathan Flynn
2026-10-29 18:09   ` [PATCH RFC 06/12] vfs: let a filesystem remove files with the parent directory locked shared Jonathan Flynn
2026-10-29 18:09   ` [PATCH RFC 07/12] nfs: add a second mark for a removal made with the parent " Jonathan Flynn
2026-10-29 18:09   ` [PATCH RFC 08/12] nfs: keep the dentry of a shared-parent removal hashed Jonathan Flynn
2026-10-29 18:09   ` [PATCH RFC 09/12] nfs: do not cache a name from a listing that a removal overlapped Jonathan Flynn
2026-10-29 18:09   ` [PATCH RFC 10/12] nfs: add the removal method for a parent directory locked shared Jonathan Flynn
2026-10-29 18:09   ` [PATCH RFC 11/12] nfs: look a name up again once its removal has unhashed it Jonathan Flynn
2026-10-29 18:09   ` [PATCH RFC 12/12] nfs: remove files with the parent directory locked shared on NFSv4 Jonathan Flynn
2026-10-29 22:18   ` [PATCH RFC 00/12] vfs, nfs: concurrent create and unlink in one directory NeilBrown

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261029180957.387138-4-jonathan.flynn@hammerspace.com \
    --to=jonathan.flynn@hammerspace.com \
    --cc=anna@kernel.org \
    --cc=brauner@kernel.org \
    --cc=cel@kernel.org \
    --cc=corbet@lwn.net \
    --cc=jack@suse.cz \
    --cc=jlayton@kernel.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-nfs@vger.kernel.org \
    --cc=neil@brown.name \
    --cc=trondmy@kernel.org \
    --cc=viro@zeniv.linux.org.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®