From: Mike Snitzer <snitzer@hammerspace.com>
To: Trond Myklebust <trondmy@kernel.org>, Anna Schumaker <anna@kernel.org>
Cc: Tom Haynes <loghyr@hammerspace.com>, Chuck Lever <cel@kernel.org>,
linux-nfs@vger.kernel.org
Subject: [PATCH v3 4/6] nfs4.2: add UNCACHEABLE_DIRENT_METADATA attribute support
Date: Wed, 1 Jul 2026 16:43:35 -0400 [thread overview]
Message-ID: <20260701204337.54314-5-snitzer@kernel.org> (raw)
In-Reply-To: <20260701204337.54314-1-snitzer@kernel.org>
Recognize the NFSv4.2 per-directory UNCACHEABLE_DIRENT_METADATA attribute
(attr 88, draft-ietf-nfsv4-uncacheable-directories): decode it via
GETATTR, track per-exported-filesystem support, and record on the inode
whether a directory's directory-entry metadata must not be cached.
Honoring the attribute (refetching directory-entry metadata from the
server on each READDIR) is done by a subsequent change.
If the NFSv4 server reports a directory's UNCACHEABLE_DIRENT_METADATA as
true, it indicates the directory's directory-entry metadata must not be
cached; the client records this in
NFS_I(inode)->uncacheable_dirent_metadata for use by the readdir path.
The UNCACHEABLE_DIRENT_METADATA attribute applies only to directory
objects (NF4DIR) and is independent of the companion
UNCACHEABLE_FILE_DATA attribute (attr 87); the two govern different
aspects of client caching and may be used separately. A subsequent
commit gates the client accordingly so the attribute is requested only
for directories.
Unlike the per-file UNCACHEABLE_FILE_DATA attribute, this directory
attribute is deliberately not tied to the cache_validity / file-delegation
machinery (nfs4_bitmap_copy_adjust()'s delegation block, nfs4_bitmask_set()):
a directory cannot hold an NFSv4 read/write (file) delegation, and per the
draft a server must recall or withhold a directory delegation while the
attribute is set, so there is never an authoritative cached state to
optimize against. The attribute is simply requested on every directory
GETATTR and recorded on receipt.
See: https://datatracker.ietf.org/doc/draft-ietf-nfsv4-uncacheable-directories/
Signed-off-by: Mike Snitzer <snitzer@kernel.org>
Assisted-by: Claude:claude-opus-4-8
---
fs/nfs/inode.c | 8 ++++++++
fs/nfs/nfs4proc.c | 6 +++++-
fs/nfs/nfs4trace.h | 3 ++-
fs/nfs/nfs4xdr.c | 31 ++++++++++++++++++++++++++++++-
include/linux/nfs4.h | 9 +++++++++
include/linux/nfs_fs.h | 1 +
include/linux/nfs_xdr.h | 5 ++++-
7 files changed, 59 insertions(+), 4 deletions(-)
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index bb6e58123341..080a9fed99ba 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -508,6 +508,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
nfsi->write_io = 0;
nfsi->read_io = 0;
nfsi->uncacheable_file_data = false;
+ nfsi->uncacheable_dirent_metadata = false;
nfsi->read_cache_jiffies = fattr->time_start;
nfsi->attr_gencount = fattr->gencount;
@@ -568,6 +569,9 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
else if (S_ISREG(inode->i_mode) &&
(fattr_supported & NFS_ATTR_FATTR_UNCACHEABLE_FILE_DATA))
nfs_set_cache_invalid(inode, NFS_INO_INVALID_UNCACHEABLE_FILE_DATA);
+ if (fattr->valid & NFS_ATTR_FATTR_UNCACHEABLE_DIRENT_METADATA)
+ nfsi->uncacheable_dirent_metadata =
+ fattr->aux_flags & NFS_AUX_UNCACHEABLE_DIRENT_METADATA;
nfs_setsecurity(inode, fattr);
@@ -2479,6 +2483,10 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
nfsi->cache_validity |=
save_cache_validity & NFS_INO_INVALID_UNCACHEABLE_FILE_DATA;
+ if (fattr->valid & NFS_ATTR_FATTR_UNCACHEABLE_DIRENT_METADATA)
+ nfsi->uncacheable_dirent_metadata =
+ fattr->aux_flags & NFS_AUX_UNCACHEABLE_DIRENT_METADATA;
+
/* Update attrtimeo value if we're out of the unstable period */
if (attr_changed) {
nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE);
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 3903d613f3eb..4c8436ac5cfc 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -226,6 +226,7 @@ const u32 nfs4_fattr_bitmap[3] = {
| FATTR4_WORD1_TIME_MODIFY
| FATTR4_WORD1_MOUNTED_ON_FILEID,
FATTR4_WORD2_UNCACHEABLE_FILE_DATA
+ | FATTR4_WORD2_UNCACHEABLE_DIRENT_METADATA
#ifdef CONFIG_NFS_V4_SECURITY_LABEL
| FATTR4_WORD2_SECURITY_LABEL
#endif
@@ -252,6 +253,7 @@ static const u32 nfs4_pnfs_open_bitmap[3] = {
| FATTR4_WORD2_SECURITY_LABEL
#endif
| FATTR4_WORD2_UNCACHEABLE_FILE_DATA
+ | FATTR4_WORD2_UNCACHEABLE_DIRENT_METADATA
};
static const u32 nfs4_open_noattr_bitmap[3] = {
@@ -3881,7 +3883,7 @@ static void nfs4_close_context(struct nfs_open_context *ctx, int is_sync)
#define FATTR4_WORD1_NFS40_MASK (2*FATTR4_WORD1_MOUNTED_ON_FILEID - 1UL)
#define FATTR4_WORD2_NFS41_MASK (2*FATTR4_WORD2_SUPPATTR_EXCLCREAT - 1UL)
-#define FATTR4_WORD2_NFS42_MASK (2*FATTR4_WORD2_UNCACHEABLE_FILE_DATA - 1UL)
+#define FATTR4_WORD2_NFS42_MASK (2*FATTR4_WORD2_UNCACHEABLE_DIRENT_METADATA - 1UL)
#define FATTR4_WORD2_NFS42_TIME_DELEG_MASK \
(FATTR4_WORD2_TIME_DELEG_MODIFY|FATTR4_WORD2_TIME_DELEG_ACCESS)
@@ -4007,6 +4009,8 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f
server->attr_bitmask_nl[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
if (!(res.attr_bitmask[2] & FATTR4_WORD2_UNCACHEABLE_FILE_DATA))
server->fattr_valid &= ~NFS_ATTR_FATTR_UNCACHEABLE_FILE_DATA;
+ if (!(res.attr_bitmask[2] & FATTR4_WORD2_UNCACHEABLE_DIRENT_METADATA))
+ server->fattr_valid &= ~NFS_ATTR_FATTR_UNCACHEABLE_DIRENT_METADATA;
if (res.open_caps.oa_share_access_want[0] &
NFS4_SHARE_WANT_OPEN_XOR_DELEGATION)
diff --git a/fs/nfs/nfs4trace.h b/fs/nfs/nfs4trace.h
index 3298dab34a78..868c201d024f 100644
--- a/fs/nfs/nfs4trace.h
+++ b/fs/nfs/nfs4trace.h
@@ -34,7 +34,8 @@
{ NFS_ATTR_FATTR_OWNER_NAME, "OWNER_NAME" }, \
{ NFS_ATTR_FATTR_GROUP_NAME, "GROUP_NAME" }, \
{ NFS_ATTR_FATTR_BTIME, "BTIME" }, \
- { NFS_ATTR_FATTR_UNCACHEABLE_FILE_DATA, "UNCACHEABLE_FILE_DATA" })
+ { NFS_ATTR_FATTR_UNCACHEABLE_FILE_DATA, "UNCACHEABLE_FILE_DATA" }, \
+ { NFS_ATTR_FATTR_UNCACHEABLE_DIRENT_METADATA, "UNCACHEABLE_DIRENT_METADATA" })
DECLARE_EVENT_CLASS(nfs4_clientid_event,
TP_PROTO(
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index fc049ce4ba8a..8329d5baf90e 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -121,7 +121,7 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
nfs4_owner_maxsz + \
nfs4_group_maxsz + nfs4_label_maxsz + \
decode_mdsthreshold_maxsz + \
- 1)) /* uncacheable_file_data */
+ 1)) /* uncacheable_file_data / dirent_metadata */
#define nfs4_fattr_maxsz (nfs4_fattr_bitmap_maxsz + \
nfs4_fattr_value_maxsz)
#define decode_getattr_maxsz (op_decode_hdr_maxsz + nfs4_fattr_maxsz)
@@ -4405,6 +4405,30 @@ static int decode_attr_uncacheable_file_data(struct xdr_stream *xdr, uint32_t *b
return status;
}
+static int decode_attr_uncacheable_dirent_metadata(struct xdr_stream *xdr, uint32_t *bitmap,
+ uint32_t *res, uint64_t *flags)
+{
+ int status = 0;
+ __be32 *p;
+
+ if (unlikely(bitmap[2] & (FATTR4_WORD2_UNCACHEABLE_DIRENT_METADATA - 1U)))
+ return -EIO;
+ if (likely(bitmap[2] & FATTR4_WORD2_UNCACHEABLE_DIRENT_METADATA)) {
+ p = xdr_inline_decode(xdr, 4);
+ if (unlikely(!p))
+ return -EIO;
+ if (be32_to_cpup(p))
+ *res |= NFS_AUX_UNCACHEABLE_DIRENT_METADATA;
+ else
+ *res &= ~NFS_AUX_UNCACHEABLE_DIRENT_METADATA;
+ bitmap[2] &= ~FATTR4_WORD2_UNCACHEABLE_DIRENT_METADATA;
+ *flags |= NFS_ATTR_FATTR_UNCACHEABLE_DIRENT_METADATA;
+ }
+ dprintk("%s: uncacheable_dirent_metadata: =%s\n", __func__,
+ (*res & NFS_AUX_UNCACHEABLE_DIRENT_METADATA) == 0 ? "false" : "true");
+ return status;
+}
+
static int verify_attr_len(struct xdr_stream *xdr, unsigned int savep, uint32_t attrlen)
{
unsigned int attrwords = XDR_QUADLEN(attrlen);
@@ -4875,6 +4899,11 @@ static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap,
if (status < 0)
goto xdr_error;
+ status = decode_attr_uncacheable_dirent_metadata(xdr, bitmap, &fattr->aux_flags,
+ &fattr->valid);
+ if (status < 0)
+ goto xdr_error;
+
status = 0;
xdr_error:
dprintk("%s: xdr returned %d\n", __func__, -status);
diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index 1a3981c26b23..a30905cb4118 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -397,6 +397,14 @@ enum {
FATTR4_UNCACHEABLE_FILE_DATA = 87,
};
+/*
+ * Symbol name and value are from draft-ietf-nfsv4-uncacheable-directories
+ * Section 8. "XDR for Uncacheable Dirents Attribute"
+ */
+enum {
+ FATTR4_UNCACHEABLE_DIRENT_METADATA = 88,
+};
+
/*
* The following internal definitions enable processing the above
* attribute bits within 32-bit word boundaries.
@@ -484,6 +492,7 @@ enum {
#define FATTR4_WORD2_POSIX_DEFAULT_ACL BIT(FATTR4_POSIX_DEFAULT_ACL - 64)
#define FATTR4_WORD2_POSIX_ACCESS_ACL BIT(FATTR4_POSIX_ACCESS_ACL - 64)
#define FATTR4_WORD2_UNCACHEABLE_FILE_DATA BIT(FATTR4_UNCACHEABLE_FILE_DATA - 64)
+#define FATTR4_WORD2_UNCACHEABLE_DIRENT_METADATA BIT(FATTR4_UNCACHEABLE_DIRENT_METADATA - 64)
/* MDS threshold bitmap bits */
#define THRESHOLD_RD (1UL << 0)
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 48b806aa3a2f..887b76c2a5dd 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -164,6 +164,7 @@ struct nfs_inode {
struct timespec64 btime;
bool uncacheable_file_data : 1;
+ bool uncacheable_dirent_metadata : 1;
/*
* read_cache_jiffies is when we started read-caching this inode.
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 2e1987ac403d..2018cc3c9c31 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -19,6 +19,7 @@
/* aux_flags in nfs_fattr */
#define NFS_AUX_UNCACHEABLE_FILE_DATA BIT(0)
+#define NFS_AUX_UNCACHEABLE_DIRENT_METADATA BIT(1)
struct nfs4_string {
unsigned int len;
@@ -113,6 +114,7 @@ struct nfs_fattr {
#define NFS_ATTR_FATTR_V4_SECURITY_LABEL BIT_ULL(25)
#define NFS_ATTR_FATTR_BTIME BIT_ULL(26)
#define NFS_ATTR_FATTR_UNCACHEABLE_FILE_DATA BIT_ULL(27)
+#define NFS_ATTR_FATTR_UNCACHEABLE_DIRENT_METADATA BIT_ULL(28)
#define NFS_ATTR_FATTR (NFS_ATTR_FATTR_TYPE \
| NFS_ATTR_FATTR_MODE \
@@ -135,7 +137,8 @@ struct nfs_fattr {
| NFS_ATTR_FATTR_SPACE_USED \
| NFS_ATTR_FATTR_BTIME \
| NFS_ATTR_FATTR_V4_SECURITY_LABEL \
- | NFS_ATTR_FATTR_UNCACHEABLE_FILE_DATA)
+ | NFS_ATTR_FATTR_UNCACHEABLE_FILE_DATA \
+ | NFS_ATTR_FATTR_UNCACHEABLE_DIRENT_METADATA)
/*
* Maximal number of supported layout drivers.
--
2.47.3
next prev parent reply other threads:[~2026-07-01 20:43 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-01 20:43 [PATCH v3 0/6] nfs: NFSv4.2 client support for UNCACHEABLE_FILE_DATA and UNCACHEABLE_DIRENT_METADATA Mike Snitzer
2026-07-01 20:43 ` [PATCH v3 1/6] nfs4.2: add UNCACHEABLE_FILE_DATA attribute support Mike Snitzer
2026-07-01 20:43 ` [PATCH v3 2/6] nfs4.2: request UNCACHEABLE_FILE_DATA only for regular files Mike Snitzer
2026-07-01 20:43 ` [PATCH v3 3/6] nfs4.2: open UNCACHEABLE_FILE_DATA files with O_DIRECT Mike Snitzer
2026-07-01 20:43 ` Mike Snitzer [this message]
2026-07-01 20:43 ` [PATCH v3 5/6] nfs4.2: request UNCACHEABLE_DIRENT_METADATA only for directories Mike Snitzer
2026-07-01 20:43 ` [PATCH v3 6/6] nfs4.2: honor UNCACHEABLE_DIRENT_METADATA by refetching readdir Mike Snitzer
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=20260701204337.54314-5-snitzer@kernel.org \
--to=snitzer@hammerspace.com \
--cc=anna@kernel.org \
--cc=cel@kernel.org \
--cc=linux-nfs@vger.kernel.org \
--cc=loghyr@hammerspace.com \
--cc=trondmy@kernel.org \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.