* [PATCH v3 00/38] vfs, nfsd: implement directory delegations
@ 2025-09-24 18:05 Jeff Layton
2025-09-24 18:05 ` [PATCH v3 01/38] filelock: push the S_ISREG check down to ->setlease handlers Jeff Layton
` (38 more replies)
0 siblings, 39 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:05 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
This patchset is an update to a patchset that I posted in early June
this year [1]. This version should be basically feature-complete, with a
few caveats.
NFSv4.1 adds a GET_DIR_DELEGATION operation, to allow clients
to request a delegation on a directory. If the client holds a directory
delegation, then it knows that nothing will change the dentries in it
until it has been recalled (modulo the case where the client requests
notifications of directory changes).
In 2023, Rick Macklem gave a talk at the NFS Bakeathon on his
implementation of directory delegations for FreeBSD [2], and showed that
it can greatly improve LOOKUP-heavy workloads. There is also some
earlier work by CITI [3] that showed similar results. The SMB protocol
also has a similar sort of construct, and they have also seen large
performance improvements on certain workloads.
This version also starts with support for trivial directory delegations
that support no notifications. From there it adds VFS support for
ignoring certain break_lease() events in directories. It then adds
support for basic CB_NOTIFY calls (with names only). Next, support for
sending attributes in the notifications is added.
I think that this version should be getting close to merge ready. Anna
has graciously agreed to work on the client-side pieces for this. I've
mostly been testing using pynfs tests (which I will submit soon).
The main limitation at this point is that callback requests are
currently limited to a single page, so we can't send very many in a
single CB_NOTIFY call. This will make it easy to "get into the weeds" if
you're changing a directory quickly. The server will just recall the
delegation in that case, so it's harmless even though it's not ideal.
If this approach looks acceptable I'll see if we can increase that
limitation (it seems doable).
If anyone wishes to try this out, it's in the "dir-deleg" branch in my
tree at kernel.org [4].
[1]: https://lore.kernel.org/linux-nfs/20250602-dir-deleg-v2-0-a7919700de86@kernel.org/
[2]: https://www.youtube.com/watch?v=DdFyH3BN5pI
[3]: https://linux-nfs.org/wiki/index.php/CITI_Experience_with_Directory_Delegations
[4]: https://git.kernel.org/pub/scm/linux/kernel/git/jlayton/linux.git/
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
Changes in v3:
- Rework to do minimal work in fsnotify callbacks
- Add support for sending attributes in CB_NOTIFY calls
- Add support for dir attr change notifications
- Link to v2: https://lore.kernel.org/r/20250602-dir-deleg-v2-0-a7919700de86@kernel.org
Changes in v2:
- add support for ignoring certain break_lease() events
- basic support for CB_NOTIFY
- Link to v1: https://lore.kernel.org/r/20240315-dir-deleg-v1-0-a1d6209a3654@kernel.org
---
Jeff Layton (38):
filelock: push the S_ISREG check down to ->setlease handlers
filelock: add a lm_may_setlease lease_manager callback
vfs: add try_break_deleg calls for parents to vfs_{link,rename,unlink}
vfs: allow mkdir to wait for delegation break on parent
vfs: allow rmdir to wait for delegation break on parent
vfs: break parent dir delegations in open(..., O_CREAT) codepath
vfs: make vfs_create break delegations on parent directory
vfs: make vfs_mknod break delegations on parent directory
filelock: lift the ban on directory leases in generic_setlease
nfsd: allow filecache to hold S_IFDIR files
nfsd: allow DELEGRETURN on directories
nfsd: check for delegation conflicts vs. the same client
nfsd: wire up GET_DIR_DELEGATION handling
filelock: rework the __break_lease API to use flags
filelock: add struct delegated_inode
filelock: add support for ignoring deleg breaks for dir change events
filelock: add a tracepoint to start of break_lease()
filelock: add an inode_lease_ignore_mask helper
nfsd: add protocol support for CB_NOTIFY
nfs_common: add new NOTIFY4_* flags proposed in RFC8881bis
nfsd: allow nfsd to get a dir lease with an ignore mask
vfs: add fsnotify_modify_mark_mask()
nfsd: update the fsnotify mark when setting or removing a dir delegation
nfsd: make nfsd4_callback_ops->prepare operation bool return
nfsd: add callback encoding and decoding linkages for CB_NOTIFY
nfsd: add data structures for handling CB_NOTIFY to directory delegation
nfsd: add notification handlers for dir events
nfsd: add tracepoint to dir_event handler
nfsd: apply the notify mask to the delegation when requested
nfsd: add helper to marshal a fattr4 from completed args
nfsd: allow nfsd4_encode_fattr4_change() to work with no export
nfsd: send basic file attributes in CB_NOTIFY
nfsd: allow encoding a filehandle into fattr4 without a svc_fh
nfsd: add a fi_connectable flag to struct nfs4_file
nfsd: add the filehandle to returned attributes in CB_NOTIFY
nfsd: properly track requested child attributes
nfsd: track requested dir attributes
nfsd: add support to CB_NOTIFY for dir attribute changes
Documentation/sunrpc/xdr/nfs4_1.x | 267 +++++++++++++++++-
drivers/base/devtmpfs.c | 2 +-
fs/attr.c | 4 +-
fs/cachefiles/namei.c | 2 +-
fs/ecryptfs/inode.c | 2 +-
fs/fuse/dir.c | 1 +
fs/init.c | 2 +-
fs/locks.c | 122 ++++++--
fs/namei.c | 253 +++++++++++------
fs/nfs/nfs4file.c | 2 +
fs/nfsd/filecache.c | 101 +++++--
fs/nfsd/filecache.h | 2 +
fs/nfsd/nfs4callback.c | 60 +++-
fs/nfsd/nfs4layouts.c | 3 +-
fs/nfsd/nfs4proc.c | 36 ++-
fs/nfsd/nfs4recover.c | 2 +-
fs/nfsd/nfs4state.c | 531 +++++++++++++++++++++++++++++++++--
fs/nfsd/nfs4xdr.c | 298 +++++++++++++++++---
fs/nfsd/nfs4xdr_gen.c | 506 ++++++++++++++++++++++++++++++++-
fs/nfsd/nfs4xdr_gen.h | 20 +-
fs/nfsd/state.h | 73 ++++-
fs/nfsd/trace.h | 21 ++
fs/nfsd/vfs.c | 7 +-
fs/nfsd/vfs.h | 2 +-
fs/nfsd/xdr4.h | 3 +
fs/nfsd/xdr4cb.h | 12 +
fs/notify/mark.c | 29 ++
fs/open.c | 8 +-
fs/overlayfs/overlayfs.h | 2 +-
fs/posix_acl.c | 12 +-
fs/smb/client/cifsfs.c | 3 +
fs/smb/server/vfs.c | 2 +-
fs/utimes.c | 4 +-
fs/xattr.c | 16 +-
fs/xfs/scrub/orphanage.c | 2 +-
include/linux/filelock.h | 143 +++++++---
include/linux/fs.h | 11 +-
include/linux/fsnotify_backend.h | 1 +
include/linux/nfs4.h | 127 ---------
include/linux/sunrpc/xdrgen/nfs4_1.h | 304 +++++++++++++++++++-
include/linux/xattr.h | 4 +-
include/trace/events/filelock.h | 38 ++-
include/uapi/linux/nfs4.h | 2 -
43 files changed, 2636 insertions(+), 406 deletions(-)
---
base-commit: 36c204d169319562eed170f266c58460d5dad635
change-id: 20240215-dir-deleg-e212210ba9d4
Best regards,
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 53+ messages in thread
* [PATCH v3 01/38] filelock: push the S_ISREG check down to ->setlease handlers
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
@ 2025-09-24 18:05 ` Jeff Layton
2025-09-24 18:05 ` [PATCH v3 02/38] filelock: add a lm_may_setlease lease_manager callback Jeff Layton
` (37 subsequent siblings)
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:05 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
When nfsd starts requesting directory delegations, setlease handlers may
see requests for leases on directories. Push the !S_ISREG check down
into the non-trivial setlease handlers, so we can selectively enable
them where they're supported.
FUSE is special: It's the only filesystem that supports atomic_open and
allows kernel-internal leases. Ensure that we don't allow directory
leases by default going forward by explicitly disabling them there.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/fuse/dir.c | 1 +
fs/locks.c | 5 +++--
fs/nfs/nfs4file.c | 2 ++
fs/smb/client/cifsfs.c | 3 +++
4 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 5c569c3cb53f3d20a9f284124dee657fca5ffc9a..f5288eef5711dca46e78ef6b784ae78737e92201 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -2237,6 +2237,7 @@ static const struct file_operations fuse_dir_operations = {
.fsync = fuse_dir_fsync,
.unlocked_ioctl = fuse_dir_ioctl,
.compat_ioctl = fuse_dir_compat_ioctl,
+ .setlease = simple_nosetlease,
};
static const struct inode_operations fuse_common_inode_operations = {
diff --git a/fs/locks.c b/fs/locks.c
index 559f02aa4172214a14d907b7bc090c1a9235967c..edf34b9859a16c34dd75ce4d1a6a412dd426c875 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -1929,6 +1929,9 @@ static int generic_delete_lease(struct file *filp, void *owner)
int generic_setlease(struct file *filp, int arg, struct file_lease **flp,
void **priv)
{
+ if (!S_ISREG(file_inode(filp)->i_mode))
+ return -EINVAL;
+
switch (arg) {
case F_UNLCK:
return generic_delete_lease(filp, *priv);
@@ -2018,8 +2021,6 @@ vfs_setlease(struct file *filp, int arg, struct file_lease **lease, void **priv)
if ((!vfsuid_eq_kuid(vfsuid, current_fsuid())) && !capable(CAP_LEASE))
return -EACCES;
- if (!S_ISREG(inode->i_mode))
- return -EINVAL;
error = security_file_lock(filp, arg);
if (error)
return error;
diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c
index c9a0d1e420c6cb17b209e3f7f48a12dd479dbc24..730e04b8a768debd34ec0c97a0cf0c44f1c18de5 100644
--- a/fs/nfs/nfs4file.c
+++ b/fs/nfs/nfs4file.c
@@ -431,6 +431,8 @@ void nfs42_ssc_unregister_ops(void)
static int nfs4_setlease(struct file *file, int arg, struct file_lease **lease,
void **priv)
{
+ if (!S_ISREG(file_inode(file)->i_mode))
+ return -EINVAL;
return nfs4_proc_setlease(file, arg, lease, priv);
}
diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c
index e1848276bab41364f894f5f92d7ec08f343aeb1c..8364eed8a246bef43a10bc4078906d8a9aaf26fc 100644
--- a/fs/smb/client/cifsfs.c
+++ b/fs/smb/client/cifsfs.c
@@ -1093,6 +1093,9 @@ cifs_setlease(struct file *file, int arg, struct file_lease **lease, void **priv
struct inode *inode = file_inode(file);
struct cifsFileInfo *cfile = file->private_data;
+ if (!S_ISREG(inode->i_mode))
+ return -EINVAL;
+
/* Check if file is oplocked if this is request for new lease */
if (arg == F_UNLCK ||
((arg == F_RDLCK) && CIFS_CACHE_READ(CIFS_I(inode))) ||
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 02/38] filelock: add a lm_may_setlease lease_manager callback
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
2025-09-24 18:05 ` [PATCH v3 01/38] filelock: push the S_ISREG check down to ->setlease handlers Jeff Layton
@ 2025-09-24 18:05 ` Jeff Layton
2025-09-24 18:05 ` [PATCH v3 03/38] vfs: add try_break_deleg calls for parents to vfs_{link,rename,unlink} Jeff Layton
` (36 subsequent siblings)
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:05 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
The NFSv4.1 protocol adds support for directory delegations, but it
specifies that if you already have a delegation and try to request a new
one on the same filehandle, the server must reply that the delegation is
unavailable.
Add a new lease manager callback to allow the lease manager (nfsd in
this case) to impose this extra check when performing a setlease.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/locks.c | 5 +++++
include/linux/filelock.h | 14 ++++++++++++++
2 files changed, 19 insertions(+)
diff --git a/fs/locks.c b/fs/locks.c
index edf34b9859a16c34dd75ce4d1a6a412dd426c875..8bd0faa384a9bdb0ef0ff40ba7269aed72439739 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -1826,6 +1826,11 @@ generic_add_lease(struct file *filp, int arg, struct file_lease **flp, void **pr
continue;
}
+ /* Allow the lease manager to veto the setlease */
+ if (lease->fl_lmops->lm_may_setlease &&
+ !lease->fl_lmops->lm_may_setlease(lease, fl))
+ goto out;
+
/*
* No exclusive leases if someone else has a lease on
* this file:
diff --git a/include/linux/filelock.h b/include/linux/filelock.h
index c2ce8ba05d068b451ecf8f513b7e532819a29944..70079beddf61aa32ef01f1114cf0cb3ffaf2131a 100644
--- a/include/linux/filelock.h
+++ b/include/linux/filelock.h
@@ -49,6 +49,20 @@ struct lease_manager_operations {
int (*lm_change)(struct file_lease *, int, struct list_head *);
void (*lm_setup)(struct file_lease *, void **);
bool (*lm_breaker_owns_lease)(struct file_lease *);
+
+ /**
+ * lm_may_setlease - extra conditions for setlease
+ * @new: new file_lease being set
+ * @old: old (extant) file_lease
+ *
+ * This allows the lease manager to add extra conditions when
+ * setting a lease, based on the presence of an existing lease.
+ *
+ * Return values:
+ * %false: @new and @old conflict
+ * %true: No conflict detected
+ */
+ bool (*lm_may_setlease)(struct file_lease *new, struct file_lease *old);
};
struct lock_manager {
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 03/38] vfs: add try_break_deleg calls for parents to vfs_{link,rename,unlink}
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
2025-09-24 18:05 ` [PATCH v3 01/38] filelock: push the S_ISREG check down to ->setlease handlers Jeff Layton
2025-09-24 18:05 ` [PATCH v3 02/38] filelock: add a lm_may_setlease lease_manager callback Jeff Layton
@ 2025-09-24 18:05 ` Jeff Layton
2025-09-25 15:52 ` Jan Kara
2025-09-24 18:05 ` [PATCH v3 04/38] vfs: allow mkdir to wait for delegation break on parent Jeff Layton
` (35 subsequent siblings)
38 siblings, 1 reply; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:05 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
In order to add directory delegation support, we need to break
delegations on the parent whenever there is going to be a change in the
directory.
vfs_link, vfs_unlink, and vfs_rename all have existing delegation break
handling for the children in the rename. Add the necessary calls for
breaking delegations in the parent(s) as well.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/namei.c | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/fs/namei.c b/fs/namei.c
index cd43ff89fbaa38206db2aec4f097ca119819f92e..cd517eb232317d326e6d2fc5a60cb4c7569a137d 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -4580,6 +4580,9 @@ int vfs_unlink(struct mnt_idmap *idmap, struct inode *dir,
else {
error = security_inode_unlink(dir, dentry);
if (!error) {
+ error = try_break_deleg(dir, delegated_inode);
+ if (error)
+ goto out;
error = try_break_deleg(target, delegated_inode);
if (error)
goto out;
@@ -4849,7 +4852,9 @@ int vfs_link(struct dentry *old_dentry, struct mnt_idmap *idmap,
else if (max_links && inode->i_nlink >= max_links)
error = -EMLINK;
else {
- error = try_break_deleg(inode, delegated_inode);
+ error = try_break_deleg(dir, delegated_inode);
+ if (!error)
+ error = try_break_deleg(inode, delegated_inode);
if (!error)
error = dir->i_op->link(old_dentry, dir, new_dentry);
}
@@ -5116,6 +5121,14 @@ int vfs_rename(struct renamedata *rd)
old_dir->i_nlink >= max_links)
goto out;
}
+ error = try_break_deleg(old_dir, delegated_inode);
+ if (error)
+ goto out;
+ if (new_dir != old_dir) {
+ error = try_break_deleg(new_dir, delegated_inode);
+ if (error)
+ goto out;
+ }
if (!is_dir) {
error = try_break_deleg(source, delegated_inode);
if (error)
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 04/38] vfs: allow mkdir to wait for delegation break on parent
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (2 preceding siblings ...)
2025-09-24 18:05 ` [PATCH v3 03/38] vfs: add try_break_deleg calls for parents to vfs_{link,rename,unlink} Jeff Layton
@ 2025-09-24 18:05 ` Jeff Layton
2025-09-25 15:58 ` Jan Kara
2025-09-24 18:05 ` [PATCH v3 05/38] vfs: allow rmdir " Jeff Layton
` (34 subsequent siblings)
38 siblings, 1 reply; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:05 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
In order to add directory delegation support, we need to break
delegations on the parent whenever there is going to be a change in the
directory.
Rename the existing vfs_mkdir to __vfs_mkdir, make it static and add a
new delegated_inode parameter. Add a new exported vfs_mkdir wrapper
around it that passes a NULL pointer for delegated_inode.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
drivers/base/devtmpfs.c | 2 +-
fs/cachefiles/namei.c | 2 +-
fs/ecryptfs/inode.c | 2 +-
fs/init.c | 2 +-
fs/namei.c | 24 ++++++++++++++++++------
fs/nfsd/nfs4recover.c | 2 +-
fs/nfsd/vfs.c | 2 +-
fs/overlayfs/overlayfs.h | 2 +-
fs/smb/server/vfs.c | 2 +-
fs/xfs/scrub/orphanage.c | 2 +-
include/linux/fs.h | 2 +-
11 files changed, 28 insertions(+), 16 deletions(-)
diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c
index 31bfb3194b4c29a1d6a002449045bf4e4141911d..a57da600ce7523e9e2755b78f75342bf4fa56ef6 100644
--- a/drivers/base/devtmpfs.c
+++ b/drivers/base/devtmpfs.c
@@ -180,7 +180,7 @@ static int dev_mkdir(const char *name, umode_t mode)
if (IS_ERR(dentry))
return PTR_ERR(dentry);
- dentry = vfs_mkdir(&nop_mnt_idmap, d_inode(path.dentry), dentry, mode);
+ dentry = vfs_mkdir(&nop_mnt_idmap, d_inode(path.dentry), dentry, mode, NULL);
if (!IS_ERR(dentry))
/* mark as kernel-created inode */
d_inode(dentry)->i_private = &thread;
diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c
index 91dfd02318772fa63050ecf40fa5625ab48ad589..b3dac91efec622261186fbba8e704ae9e782bea0 100644
--- a/fs/cachefiles/namei.c
+++ b/fs/cachefiles/namei.c
@@ -130,7 +130,7 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
goto mkdir_error;
ret = cachefiles_inject_write_error();
if (ret == 0)
- subdir = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), subdir, 0700);
+ subdir = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), subdir, 0700, NULL);
else
subdir = ERR_PTR(ret);
if (IS_ERR(subdir)) {
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index 72fbe1316ab8831bb4228d573278f32fe52b6b25..00f54c125b102856c33ffff24627475f40dcbc7b 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -517,7 +517,7 @@ static struct dentry *ecryptfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
goto out;
lower_dentry = vfs_mkdir(&nop_mnt_idmap, lower_dir,
- lower_dentry, mode);
+ lower_dentry, mode, NULL);
rc = PTR_ERR(lower_dentry);
if (IS_ERR(lower_dentry))
goto out;
diff --git a/fs/init.c b/fs/init.c
index eef5124885e372ac020d2923692116c5e884b3cf..dd5240ce8ad41f02367a54ddf1b6ac0aa28e9721 100644
--- a/fs/init.c
+++ b/fs/init.c
@@ -232,7 +232,7 @@ int __init init_mkdir(const char *pathname, umode_t mode)
error = security_path_mkdir(&path, dentry, mode);
if (!error) {
dentry = vfs_mkdir(mnt_idmap(path.mnt), path.dentry->d_inode,
- dentry, mode);
+ dentry, mode, NULL);
if (IS_ERR(dentry))
error = PTR_ERR(dentry);
}
diff --git a/fs/namei.c b/fs/namei.c
index cd517eb232317d326e6d2fc5a60cb4c7569a137d..c939a58f16f9c4edded424475aff52f2c423d301 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -4320,10 +4320,11 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d
/**
* vfs_mkdir - create directory returning correct dentry if possible
- * @idmap: idmap of the mount the inode was found from
- * @dir: inode of the parent directory
- * @dentry: dentry of the child directory
- * @mode: mode of the child directory
+ * @idmap: idmap of the mount the inode was found from
+ * @dir: inode of the parent directory
+ * @dentry: dentry of the child directory
+ * @mode: mode of the child directory
+ * @delegated_inode: returns victim inode, if the inode is delegated.
*
* Create a directory.
*
@@ -4340,7 +4341,8 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d
* In case of an error the dentry is dput() and an ERR_PTR() is returned.
*/
struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
- struct dentry *dentry, umode_t mode)
+ struct dentry *dentry, umode_t mode,
+ struct inode **delegated_inode)
{
int error;
unsigned max_links = dir->i_sb->s_max_links;
@@ -4363,6 +4365,10 @@ struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
if (max_links && dir->i_nlink >= max_links)
goto err;
+ error = try_break_deleg(dir, delegated_inode);
+ if (error)
+ goto err;
+
de = dir->i_op->mkdir(idmap, dir, dentry, mode);
error = PTR_ERR(de);
if (IS_ERR(de))
@@ -4386,6 +4392,7 @@ int do_mkdirat(int dfd, struct filename *name, umode_t mode)
struct path path;
int error;
unsigned int lookup_flags = LOOKUP_DIRECTORY;
+ struct inode *delegated_inode = NULL;
retry:
dentry = filename_create(dfd, name, &path, lookup_flags);
@@ -4397,11 +4404,16 @@ int do_mkdirat(int dfd, struct filename *name, umode_t mode)
mode_strip_umask(path.dentry->d_inode, mode));
if (!error) {
dentry = vfs_mkdir(mnt_idmap(path.mnt), path.dentry->d_inode,
- dentry, mode);
+ dentry, mode, &delegated_inode);
if (IS_ERR(dentry))
error = PTR_ERR(dentry);
}
done_path_create(&path, dentry);
+ if (delegated_inode) {
+ error = break_deleg_wait(&delegated_inode);
+ if (!error)
+ goto retry;
+ }
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
goto retry;
diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c
index b1005abcb9035b2cf743200808a251b00af7e3f4..423dd102b51198ea7c447be2b9a0a5020c950dba 100644
--- a/fs/nfsd/nfs4recover.c
+++ b/fs/nfsd/nfs4recover.c
@@ -202,7 +202,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
* as well be forgiving and just succeed silently.
*/
goto out_put;
- dentry = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), dentry, S_IRWXU);
+ dentry = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), dentry, 0700, NULL);
if (IS_ERR(dentry))
status = PTR_ERR(dentry);
out_put:
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 2026431500ecbc0cf5fb5d4af1a7632c611ce4f4..6f1275fdc8ac831aa0ea8da588f751eddff88df1 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1560,7 +1560,7 @@ nfsd_create_locked(struct svc_rqst *rqstp, struct svc_fh *fhp,
nfsd_check_ignore_resizing(iap);
break;
case S_IFDIR:
- dchild = vfs_mkdir(&nop_mnt_idmap, dirp, dchild, iap->ia_mode);
+ dchild = vfs_mkdir(&nop_mnt_idmap, dirp, dchild, iap->ia_mode, NULL);
if (IS_ERR(dchild)) {
host_err = PTR_ERR(dchild);
} else if (d_is_negative(dchild)) {
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index bb0d7ded8e763a4a7a6fc506d966ed2f3bdb4f06..4a3a22f422c37d45e49a762cd3c9957aa2c6a485 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -248,7 +248,7 @@ static inline struct dentry *ovl_do_mkdir(struct ovl_fs *ofs,
{
struct dentry *ret;
- ret = vfs_mkdir(ovl_upper_mnt_idmap(ofs), dir, dentry, mode);
+ ret = vfs_mkdir(ovl_upper_mnt_idmap(ofs), dir, dentry, mode, NULL);
pr_debug("mkdir(%pd2, 0%o) = %i\n", dentry, mode, PTR_ERR_OR_ZERO(ret));
return ret;
}
diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
index 04539037108c93e285f4e9d6aa61f93a507ae5da..b0fb73b277876a56797f5cc8a5aa53f156bb7a26 100644
--- a/fs/smb/server/vfs.c
+++ b/fs/smb/server/vfs.c
@@ -229,7 +229,7 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode)
idmap = mnt_idmap(path.mnt);
mode |= S_IFDIR;
d = dentry;
- dentry = vfs_mkdir(idmap, d_inode(path.dentry), dentry, mode);
+ dentry = vfs_mkdir(idmap, d_inode(path.dentry), dentry, mode, NULL);
if (IS_ERR(dentry))
err = PTR_ERR(dentry);
else if (d_is_negative(dentry))
diff --git a/fs/xfs/scrub/orphanage.c b/fs/xfs/scrub/orphanage.c
index 9c12cb8442311ca26b169e4d1567939ae44a5be0..91c9d07b97f306f57aebb9b69ba564b0c2cb8c17 100644
--- a/fs/xfs/scrub/orphanage.c
+++ b/fs/xfs/scrub/orphanage.c
@@ -167,7 +167,7 @@ xrep_orphanage_create(
*/
if (d_really_is_negative(orphanage_dentry)) {
orphanage_dentry = vfs_mkdir(&nop_mnt_idmap, root_inode,
- orphanage_dentry, 0750);
+ orphanage_dentry, 0750, NULL);
error = PTR_ERR(orphanage_dentry);
if (IS_ERR(orphanage_dentry))
goto out_unlock_root;
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 74f2bfc519263c6411a8e3427e1bd6680a1121db..24a091509f12ce65a2c8343d438fccf423d3062b 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1997,7 +1997,7 @@ bool inode_owner_or_capable(struct mnt_idmap *idmap,
int vfs_create(struct mnt_idmap *, struct inode *,
struct dentry *, umode_t, bool);
struct dentry *vfs_mkdir(struct mnt_idmap *, struct inode *,
- struct dentry *, umode_t);
+ struct dentry *, umode_t, struct inode **);
int vfs_mknod(struct mnt_idmap *, struct inode *, struct dentry *,
umode_t, dev_t);
int vfs_symlink(struct mnt_idmap *, struct inode *,
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 05/38] vfs: allow rmdir to wait for delegation break on parent
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (3 preceding siblings ...)
2025-09-24 18:05 ` [PATCH v3 04/38] vfs: allow mkdir to wait for delegation break on parent Jeff Layton
@ 2025-09-24 18:05 ` Jeff Layton
2025-09-25 17:13 ` Jeff Layton
2025-09-24 18:05 ` [PATCH v3 06/38] vfs: break parent dir delegations in open(..., O_CREAT) codepath Jeff Layton
` (33 subsequent siblings)
38 siblings, 1 reply; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:05 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
In order to add directory delegation support, we need to break
delegations on the parent whenever there is going to be a change in the
directory.
Rename vfs_rmdir as __vfs_rmdir, make it static and add a new
delegated_inode parameter. Add a vfs_rmdir wrapper that passes in a NULL
pointer for it. Add the necessary try_break_deleg calls to
__vfs_rmdir(). Convert do_rmdir to use __vfs_rmdir and wait for the
delegation break to complete before proceeding.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/namei.c | 51 ++++++++++++++++++++++++++++++++++-----------------
1 file changed, 34 insertions(+), 17 deletions(-)
diff --git a/fs/namei.c b/fs/namei.c
index c939a58f16f9c4edded424475aff52f2c423d301..4e058b00208c1663ba828c6f8ed1f82c26a4f136 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -4433,22 +4433,8 @@ SYSCALL_DEFINE2(mkdir, const char __user *, pathname, umode_t, mode)
return do_mkdirat(AT_FDCWD, getname(pathname), mode);
}
-/**
- * vfs_rmdir - remove directory
- * @idmap: idmap of the mount the inode was found from
- * @dir: inode of the parent directory
- * @dentry: dentry of the child directory
- *
- * Remove a directory.
- *
- * If the inode has been found through an idmapped mount the idmap of
- * the vfsmount must be passed through @idmap. This function will then take
- * care to map the inode according to @idmap before checking permissions.
- * On non-idmapped mounts or if permission checking is to be performed on the
- * raw inode simply pass @nop_mnt_idmap.
- */
-int vfs_rmdir(struct mnt_idmap *idmap, struct inode *dir,
- struct dentry *dentry)
+static int __vfs_rmdir(struct mnt_idmap *idmap, struct inode *dir,
+ struct dentry *dentry, struct inode **delegated_inode)
{
int error = may_delete(idmap, dir, dentry, 1);
@@ -4470,6 +4456,10 @@ int vfs_rmdir(struct mnt_idmap *idmap, struct inode *dir,
if (error)
goto out;
+ error = try_break_deleg(dir, delegated_inode);
+ if (error)
+ goto out;
+
error = dir->i_op->rmdir(dir, dentry);
if (error)
goto out;
@@ -4486,6 +4476,26 @@ int vfs_rmdir(struct mnt_idmap *idmap, struct inode *dir,
d_delete_notify(dir, dentry);
return error;
}
+
+/**
+ * vfs_rmdir - remove directory
+ * @idmap: idmap of the mount the inode was found from
+ * @dir: inode of the parent directory
+ * @dentry: dentry of the child directory
+ *
+ * Remove a directory.
+ *
+ * If the inode has been found through an idmapped mount the idmap of
+ * the vfsmount must be passed through @idmap. This function will then take
+ * care to map the inode according to @idmap before checking permissions.
+ * On non-idmapped mounts or if permission checking is to be performed on the
+ * raw inode simply pass @nop_mnt_idmap.
+ */
+int vfs_rmdir(struct mnt_idmap *idmap, struct inode *dir,
+ struct dentry *dentry)
+{
+ return __vfs_rmdir(idmap, dir, dentry, NULL);
+}
EXPORT_SYMBOL(vfs_rmdir);
int do_rmdir(int dfd, struct filename *name)
@@ -4496,6 +4506,7 @@ int do_rmdir(int dfd, struct filename *name)
struct qstr last;
int type;
unsigned int lookup_flags = 0;
+ struct inode *delegated_inode = NULL;
retry:
error = filename_parentat(dfd, name, lookup_flags, &path, &last, &type);
if (error)
@@ -4525,7 +4536,8 @@ int do_rmdir(int dfd, struct filename *name)
error = security_path_rmdir(&path, dentry);
if (error)
goto exit4;
- error = vfs_rmdir(mnt_idmap(path.mnt), path.dentry->d_inode, dentry);
+ error = __vfs_rmdir(mnt_idmap(path.mnt), path.dentry->d_inode,
+ dentry, &delegated_inode);
exit4:
dput(dentry);
exit3:
@@ -4533,6 +4545,11 @@ int do_rmdir(int dfd, struct filename *name)
mnt_drop_write(path.mnt);
exit2:
path_put(&path);
+ if (delegated_inode) {
+ error = break_deleg_wait(&delegated_inode);
+ if (!error)
+ goto retry;
+ }
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
goto retry;
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 06/38] vfs: break parent dir delegations in open(..., O_CREAT) codepath
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (4 preceding siblings ...)
2025-09-24 18:05 ` [PATCH v3 05/38] vfs: allow rmdir " Jeff Layton
@ 2025-09-24 18:05 ` Jeff Layton
2025-09-26 15:32 ` Jan Kara
2025-09-24 18:05 ` [PATCH v3 07/38] vfs: make vfs_create break delegations on parent directory Jeff Layton
` (32 subsequent siblings)
38 siblings, 1 reply; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:05 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
In order to add directory delegation support, we need to break
delegations on the parent whenever there is going to be a change in the
directory.
Add a delegated_inode parameter to lookup_open and have it break the
delegation. Then, open_last_lookups can wait for the delegation break
and retry the call to lookup_open once it's done.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/namei.c | 22 ++++++++++++++++++----
1 file changed, 18 insertions(+), 4 deletions(-)
diff --git a/fs/namei.c b/fs/namei.c
index 4e058b00208c1663ba828c6f8ed1f82c26a4f136..903b70a82530938a0fdf10508529a1b7cc38136d 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -3609,7 +3609,7 @@ static struct dentry *atomic_open(struct nameidata *nd, struct dentry *dentry,
*/
static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
const struct open_flags *op,
- bool got_write)
+ bool got_write, struct inode **delegated_inode)
{
struct mnt_idmap *idmap;
struct dentry *dir = nd->path.dentry;
@@ -3698,6 +3698,11 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
/* Negative dentry, just create the file */
if (!dentry->d_inode && (open_flag & O_CREAT)) {
+ /* but break the directory lease first! */
+ error = try_break_deleg(dir_inode, delegated_inode);
+ if (error)
+ goto out_dput;
+
file->f_mode |= FMODE_CREATED;
audit_inode_child(dir_inode, dentry, AUDIT_TYPE_CHILD_CREATE);
if (!dir_inode->i_op->create) {
@@ -3761,6 +3766,7 @@ static const char *open_last_lookups(struct nameidata *nd,
struct file *file, const struct open_flags *op)
{
struct dentry *dir = nd->path.dentry;
+ struct inode *delegated_inode = NULL;
int open_flag = op->open_flag;
bool got_write = false;
struct dentry *dentry;
@@ -3791,7 +3797,7 @@ static const char *open_last_lookups(struct nameidata *nd,
return ERR_PTR(-ECHILD);
}
}
-
+retry:
if (open_flag & (O_CREAT | O_TRUNC | O_WRONLY | O_RDWR)) {
got_write = !mnt_want_write(nd->path.mnt);
/*
@@ -3804,7 +3810,7 @@ static const char *open_last_lookups(struct nameidata *nd,
inode_lock(dir->d_inode);
else
inode_lock_shared(dir->d_inode);
- dentry = lookup_open(nd, file, op, got_write);
+ dentry = lookup_open(nd, file, op, got_write, &delegated_inode);
if (!IS_ERR(dentry)) {
if (file->f_mode & FMODE_CREATED)
fsnotify_create(dir->d_inode, dentry);
@@ -3819,8 +3825,16 @@ static const char *open_last_lookups(struct nameidata *nd,
if (got_write)
mnt_drop_write(nd->path.mnt);
- if (IS_ERR(dentry))
+ if (IS_ERR(dentry)) {
+ if (delegated_inode) {
+ int error = break_deleg_wait(&delegated_inode);
+
+ if (!error)
+ goto retry;
+ return ERR_PTR(error);
+ }
return ERR_CAST(dentry);
+ }
if (file->f_mode & (FMODE_OPENED | FMODE_CREATED)) {
dput(nd->path.dentry);
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 07/38] vfs: make vfs_create break delegations on parent directory
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (5 preceding siblings ...)
2025-09-24 18:05 ` [PATCH v3 06/38] vfs: break parent dir delegations in open(..., O_CREAT) codepath Jeff Layton
@ 2025-09-24 18:05 ` Jeff Layton
2025-09-26 15:21 ` Jan Kara
2025-09-26 15:23 ` Jan Kara
2025-09-24 18:05 ` [PATCH v3 08/38] vfs: make vfs_mknod " Jeff Layton
` (31 subsequent siblings)
38 siblings, 2 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:05 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
In order to add directory delegation support, we need to break
delegations on the parent whenever there is going to be a change in the
directory.
Rename vfs_create as __vfs_create, make it static, and add a new
delegated_inode parameter. Fix do_mknodat to call __vfs_create and wait
for a delegation break if there is one. Add a new exported vfs_create
wrapper that passes in NULL for delegated_inode.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/namei.c | 55 ++++++++++++++++++++++++++++++++++++-------------------
1 file changed, 36 insertions(+), 19 deletions(-)
diff --git a/fs/namei.c b/fs/namei.c
index 903b70a82530938a0fdf10508529a1b7cc38136d..d4b8330a3eb97e205dc2e71766fed1e45503323b 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -3370,6 +3370,32 @@ static inline umode_t vfs_prepare_mode(struct mnt_idmap *idmap,
return mode;
}
+static int __vfs_create(struct mnt_idmap *idmap, struct inode *dir,
+ struct dentry *dentry, umode_t mode, bool want_excl,
+ struct inode **delegated_inode)
+{
+ int error;
+
+ error = may_create(idmap, dir, dentry);
+ if (error)
+ return error;
+
+ if (!dir->i_op->create)
+ return -EACCES; /* shouldn't it be ENOSYS? */
+
+ mode = vfs_prepare_mode(idmap, dir, mode, S_IALLUGO, S_IFREG);
+ error = security_inode_create(dir, dentry, mode);
+ if (error)
+ return error;
+ error = try_break_deleg(dir, delegated_inode);
+ if (error)
+ return error;
+ error = dir->i_op->create(idmap, dir, dentry, mode, want_excl);
+ if (!error)
+ fsnotify_create(dir, dentry);
+ return error;
+}
+
/**
* vfs_create - create new file
* @idmap: idmap of the mount the inode was found from
@@ -3389,23 +3415,7 @@ static inline umode_t vfs_prepare_mode(struct mnt_idmap *idmap,
int vfs_create(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, umode_t mode, bool want_excl)
{
- int error;
-
- error = may_create(idmap, dir, dentry);
- if (error)
- return error;
-
- if (!dir->i_op->create)
- return -EACCES; /* shouldn't it be ENOSYS? */
-
- mode = vfs_prepare_mode(idmap, dir, mode, S_IALLUGO, S_IFREG);
- error = security_inode_create(dir, dentry, mode);
- if (error)
- return error;
- error = dir->i_op->create(idmap, dir, dentry, mode, want_excl);
- if (!error)
- fsnotify_create(dir, dentry);
- return error;
+ return __vfs_create(idmap, dir, dentry, mode, want_excl, NULL);
}
EXPORT_SYMBOL(vfs_create);
@@ -4278,6 +4288,7 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode,
struct path path;
int error;
unsigned int lookup_flags = 0;
+ struct inode *delegated_inode = NULL;
error = may_mknod(mode);
if (error)
@@ -4296,8 +4307,9 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode,
idmap = mnt_idmap(path.mnt);
switch (mode & S_IFMT) {
case 0: case S_IFREG:
- error = vfs_create(idmap, path.dentry->d_inode,
- dentry, mode, true);
+ error = __vfs_create(idmap, path.dentry->d_inode,
+ dentry, mode, true,
+ &delegated_inode);
if (!error)
security_path_post_mknod(idmap, dentry);
break;
@@ -4312,6 +4324,11 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode,
}
out2:
done_path_create(&path, dentry);
+ if (delegated_inode) {
+ error = break_deleg_wait(&delegated_inode);
+ if (!error)
+ goto retry;
+ }
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
goto retry;
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 08/38] vfs: make vfs_mknod break delegations on parent directory
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (6 preceding siblings ...)
2025-09-24 18:05 ` [PATCH v3 07/38] vfs: make vfs_create break delegations on parent directory Jeff Layton
@ 2025-09-24 18:05 ` Jeff Layton
2025-09-26 15:32 ` Jan Kara
2025-09-24 18:05 ` [PATCH v3 09/38] filelock: lift the ban on directory leases in generic_setlease Jeff Layton
` (30 subsequent siblings)
38 siblings, 1 reply; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:05 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
In order to add directory delegation support, we need to break
delegations on the parent whenever there is going to be a change in the
directory.
Rename vfs_mknod as __vfs_mknod, make it static, and add a new
delegated_inode parameter. Make do_mknodat call __vfs_mknod and wait
synchronously for delegation breaks to complete. Add a new exported
vfs_mknod wrapper that calls __vfs_mknod with a NULL delegated_inode
pointer.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/namei.c | 57 +++++++++++++++++++++++++++++++++++----------------------
1 file changed, 35 insertions(+), 22 deletions(-)
diff --git a/fs/namei.c b/fs/namei.c
index d4b8330a3eb97e205dc2e71766fed1e45503323b..7bcd898c84138061030f1f8b91273261cdf2a9b4 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -4215,24 +4215,9 @@ inline struct dentry *user_path_create(int dfd, const char __user *pathname,
}
EXPORT_SYMBOL(user_path_create);
-/**
- * vfs_mknod - create device node or file
- * @idmap: idmap of the mount the inode was found from
- * @dir: inode of the parent directory
- * @dentry: dentry of the child device node
- * @mode: mode of the child device node
- * @dev: device number of device to create
- *
- * Create a device node or file.
- *
- * If the inode has been found through an idmapped mount the idmap of
- * the vfsmount must be passed through @idmap. This function will then take
- * care to map the inode according to @idmap before checking permissions.
- * On non-idmapped mounts or if permission checking is to be performed on the
- * raw inode simply pass @nop_mnt_idmap.
- */
-int vfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
- struct dentry *dentry, umode_t mode, dev_t dev)
+static int __vfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
+ struct dentry *dentry, umode_t mode, dev_t dev,
+ struct inode **delegated_inode)
{
bool is_whiteout = S_ISCHR(mode) && dev == WHITEOUT_DEV;
int error = may_create(idmap, dir, dentry);
@@ -4256,11 +4241,37 @@ int vfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
if (error)
return error;
+ error = try_break_deleg(dir, delegated_inode);
+ if (error)
+ return error;
+
error = dir->i_op->mknod(idmap, dir, dentry, mode, dev);
if (!error)
fsnotify_create(dir, dentry);
return error;
}
+
+/**
+ * vfs_mknod - create device node or file
+ * @idmap: idmap of the mount the inode was found from
+ * @dir: inode of the parent directory
+ * @dentry: dentry of the child device node
+ * @mode: mode of the child device node
+ * @dev: device number of device to create
+ *
+ * Create a device node or file.
+ *
+ * If the inode has been found through an idmapped mount the idmap of
+ * the vfsmount must be passed through @idmap. This function will then take
+ * care to map the inode according to @idmap before checking permissions.
+ * On non-idmapped mounts or if permission checking is to be performed on the
+ * raw inode simply pass @nop_mnt_idmap.
+ */
+int vfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
+ struct dentry *dentry, umode_t mode, dev_t dev)
+{
+ return __vfs_mknod(idmap, dir, dentry, mode, dev, NULL);
+}
EXPORT_SYMBOL(vfs_mknod);
static int may_mknod(umode_t mode)
@@ -4314,12 +4325,14 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode,
security_path_post_mknod(idmap, dentry);
break;
case S_IFCHR: case S_IFBLK:
- error = vfs_mknod(idmap, path.dentry->d_inode,
- dentry, mode, new_decode_dev(dev));
+ error = __vfs_mknod(idmap, path.dentry->d_inode,
+ dentry, mode, new_decode_dev(dev),
+ &delegated_inode);
break;
case S_IFIFO: case S_IFSOCK:
- error = vfs_mknod(idmap, path.dentry->d_inode,
- dentry, mode, 0);
+ error = __vfs_mknod(idmap, path.dentry->d_inode,
+ dentry, mode, 0,
+ &delegated_inode);
break;
}
out2:
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 09/38] filelock: lift the ban on directory leases in generic_setlease
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (7 preceding siblings ...)
2025-09-24 18:05 ` [PATCH v3 08/38] vfs: make vfs_mknod " Jeff Layton
@ 2025-09-24 18:05 ` Jeff Layton
2025-09-24 18:05 ` [PATCH v3 10/38] nfsd: allow filecache to hold S_IFDIR files Jeff Layton
` (29 subsequent siblings)
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:05 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
With the addition of the try_break_lease calls in directory changing
operations, allow generic_setlease to hand them out.
Note that this also makes directory leases available to userland via
fcntl(). I don't see a real reason to prevent userland from acquiring
one, but we could reinstate the prohibition if that's preferable.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/locks.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/fs/locks.c b/fs/locks.c
index 8bd0faa384a9bdb0ef0ff40ba7269aed72439739..c1b4575c827648275a8d6628a8f279d382e46fc4 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -1934,7 +1934,9 @@ static int generic_delete_lease(struct file *filp, void *owner)
int generic_setlease(struct file *filp, int arg, struct file_lease **flp,
void **priv)
{
- if (!S_ISREG(file_inode(filp)->i_mode))
+ struct inode *inode = file_inode(filp);
+
+ if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode))
return -EINVAL;
switch (arg) {
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 10/38] nfsd: allow filecache to hold S_IFDIR files
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (8 preceding siblings ...)
2025-09-24 18:05 ` [PATCH v3 09/38] filelock: lift the ban on directory leases in generic_setlease Jeff Layton
@ 2025-09-24 18:05 ` Jeff Layton
2025-09-24 18:05 ` [PATCH v3 11/38] nfsd: allow DELEGRETURN on directories Jeff Layton
` (28 subsequent siblings)
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:05 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
The filecache infrastructure will only handle S_ISREG files at the
moment. Plumb a "type" variable into nfsd_file_do_acquire and have all
of the existing callers set it to S_ISREG. Add a new
nfsd_file_acquire_dir() wrapper that we can then call to request a
nfsd_file that holds a directory open.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/nfsd/filecache.c | 50 ++++++++++++++++++++++++++++++++++++++------------
fs/nfsd/filecache.h | 2 ++
fs/nfsd/vfs.c | 5 +++--
fs/nfsd/vfs.h | 2 +-
4 files changed, 44 insertions(+), 15 deletions(-)
diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c
index 75bc48031c0770cda4b68b4f1b5c5be1c51acd3e..0b9ee6c6baa89a306c88c56d8a7b80b5683c03e3 100644
--- a/fs/nfsd/filecache.c
+++ b/fs/nfsd/filecache.c
@@ -1054,7 +1054,7 @@ nfsd_file_do_acquire(struct svc_rqst *rqstp, struct net *net,
struct auth_domain *client,
struct svc_fh *fhp,
unsigned int may_flags, struct file *file,
- struct nfsd_file **pnf, bool want_gc)
+ umode_t type, bool want_gc, struct nfsd_file **pnf)
{
unsigned char need = may_flags & NFSD_FILE_MAY_MASK;
struct nfsd_file *new, *nf;
@@ -1065,13 +1065,13 @@ nfsd_file_do_acquire(struct svc_rqst *rqstp, struct net *net,
int ret;
retry:
- if (rqstp) {
- status = fh_verify(rqstp, fhp, S_IFREG,
+ if (rqstp)
+ status = fh_verify(rqstp, fhp, type,
may_flags|NFSD_MAY_OWNER_OVERRIDE);
- } else {
- status = fh_verify_local(net, cred, client, fhp, S_IFREG,
+ else
+ status = fh_verify_local(net, cred, client, fhp, type,
may_flags|NFSD_MAY_OWNER_OVERRIDE);
- }
+
if (status != nfs_ok)
return status;
inode = d_inode(fhp->fh_dentry);
@@ -1152,7 +1152,7 @@ nfsd_file_do_acquire(struct svc_rqst *rqstp, struct net *net,
status = nfs_ok;
trace_nfsd_file_opened(nf, status);
} else {
- ret = nfsd_open_verified(fhp, may_flags, &nf->nf_file);
+ ret = nfsd_open_verified(fhp, type, may_flags, &nf->nf_file);
if (ret == -EOPENSTALE && stale_retry) {
stale_retry = false;
nfsd_file_unhash(nf);
@@ -1212,7 +1212,7 @@ nfsd_file_acquire_gc(struct svc_rqst *rqstp, struct svc_fh *fhp,
unsigned int may_flags, struct nfsd_file **pnf)
{
return nfsd_file_do_acquire(rqstp, SVC_NET(rqstp), NULL, NULL,
- fhp, may_flags, NULL, pnf, true);
+ fhp, may_flags, NULL, S_IFREG, true, pnf);
}
/**
@@ -1237,7 +1237,7 @@ nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
unsigned int may_flags, struct nfsd_file **pnf)
{
return nfsd_file_do_acquire(rqstp, SVC_NET(rqstp), NULL, NULL,
- fhp, may_flags, NULL, pnf, false);
+ fhp, may_flags, NULL, S_IFREG, false, pnf);
}
/**
@@ -1280,8 +1280,8 @@ nfsd_file_acquire_local(struct net *net, struct svc_cred *cred,
const struct cred *save_cred = get_current_cred();
__be32 beres;
- beres = nfsd_file_do_acquire(NULL, net, cred, client,
- fhp, may_flags, NULL, pnf, false);
+ beres = nfsd_file_do_acquire(NULL, net, cred, client, fhp, may_flags,
+ NULL, S_IFREG, false, pnf);
put_cred(revert_creds(save_cred));
return beres;
}
@@ -1310,7 +1310,33 @@ nfsd_file_acquire_opened(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct nfsd_file **pnf)
{
return nfsd_file_do_acquire(rqstp, SVC_NET(rqstp), NULL, NULL,
- fhp, may_flags, file, pnf, false);
+ fhp, may_flags, file, S_IFREG, false, pnf);
+}
+
+/**
+ * nfsd_file_acquire_dir - Get a struct nfsd_file with an open directory
+ * @rqstp: the RPC transaction being executed
+ * @fhp: the NFS filehandle of the file to be opened
+ * @pnf: OUT: new or found "struct nfsd_file" object
+ *
+ * The nfsd_file_object returned by this API is reference-counted
+ * but not garbage-collected. The object is unhashed after the
+ * final nfsd_file_put(). This opens directories only, and only
+ * in O_RDONLY mode.
+ *
+ * Return values:
+ * %nfs_ok - @pnf points to an nfsd_file with its reference
+ * count boosted.
+ *
+ * On error, an nfsstat value in network byte order is returned.
+ */
+__be32
+nfsd_file_acquire_dir(struct svc_rqst *rqstp, struct svc_fh *fhp,
+ struct nfsd_file **pnf)
+{
+ return nfsd_file_do_acquire(rqstp, SVC_NET(rqstp), NULL, NULL, fhp,
+ NFSD_MAY_READ|NFSD_MAY_64BIT_COOKIE,
+ NULL, S_IFDIR, false, pnf);
}
/*
diff --git a/fs/nfsd/filecache.h b/fs/nfsd/filecache.h
index 24ddf60e8434a23122cc08e23a609c4bd8f0f6c0..c36bd0ac8ce37b9ebde03b4367c60994a4f2d88d 100644
--- a/fs/nfsd/filecache.h
+++ b/fs/nfsd/filecache.h
@@ -78,5 +78,7 @@ __be32 nfsd_file_acquire_opened(struct svc_rqst *rqstp, struct svc_fh *fhp,
__be32 nfsd_file_acquire_local(struct net *net, struct svc_cred *cred,
struct auth_domain *client, struct svc_fh *fhp,
unsigned int may_flags, struct nfsd_file **pnf);
+__be32 nfsd_file_acquire_dir(struct svc_rqst *rqstp, struct svc_fh *fhp,
+ struct nfsd_file **pnf);
int nfsd_file_cache_stats_show(struct seq_file *m, void *v);
#endif /* _FS_NFSD_FILECACHE_H */
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 6f1275fdc8ac831aa0ea8da588f751eddff88df1..e1a84117f61610c0b2fb82680a2fa7d50d39238b 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -959,15 +959,16 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type,
/**
* nfsd_open_verified - Open a regular file for the filecache
* @fhp: NFS filehandle of the file to open
+ * @type: S_IFMT inode type allowed (0 means any type is allowed)
* @may_flags: internal permission flags
* @filp: OUT: open "struct file *"
*
* Returns zero on success, or a negative errno value.
*/
int
-nfsd_open_verified(struct svc_fh *fhp, int may_flags, struct file **filp)
+nfsd_open_verified(struct svc_fh *fhp, umode_t type, int may_flags, struct file **filp)
{
- return __nfsd_open(fhp, S_IFREG, may_flags, filp);
+ return __nfsd_open(fhp, type, may_flags, filp);
}
/*
diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h
index 0c0292611c6de3daf6f3ed51e2c61c0ad2751de4..09de48c50cbef8e7c4828b38dcb663b529514a30 100644
--- a/fs/nfsd/vfs.h
+++ b/fs/nfsd/vfs.h
@@ -114,7 +114,7 @@ __be32 nfsd_setxattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
int nfsd_open_break_lease(struct inode *, int);
__be32 nfsd_open(struct svc_rqst *, struct svc_fh *, umode_t,
int, struct file **);
-int nfsd_open_verified(struct svc_fh *fhp, int may_flags,
+int nfsd_open_verified(struct svc_fh *fhp, umode_t type, int may_flags,
struct file **filp);
__be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct file *file, loff_t offset,
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 11/38] nfsd: allow DELEGRETURN on directories
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (9 preceding siblings ...)
2025-09-24 18:05 ` [PATCH v3 10/38] nfsd: allow filecache to hold S_IFDIR files Jeff Layton
@ 2025-09-24 18:05 ` Jeff Layton
2025-09-24 18:05 ` [PATCH v3 12/38] nfsd: check for delegation conflicts vs. the same client Jeff Layton
` (27 subsequent siblings)
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:05 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
As Trond pointed out: "...provided that the presented stateid is
actually valid, it is also sufficient to uniquely identify the file to
which it is associated (see RFC8881 Section 8.2.4), so the filehandle
should be considered mostly irrelevant for operations like DELEGRETURN."
Don't ask fh_verify to filter on file type.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/nfsd/nfs4state.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index fa073353f30b68704cf6d503a752990e6d18c8aa..25e4dc0a1459b73a0484c05cb3d1f0306784bb74 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -7859,7 +7859,8 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
__be32 status;
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
- if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0)))
+ status = fh_verify(rqstp, &cstate->current_fh, 0, 0);
+ if (status)
return status;
status = nfsd4_lookup_stateid(cstate, stateid, SC_TYPE_DELEG, SC_STATUS_REVOKED, &s, nn);
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 12/38] nfsd: check for delegation conflicts vs. the same client
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (10 preceding siblings ...)
2025-09-24 18:05 ` [PATCH v3 11/38] nfsd: allow DELEGRETURN on directories Jeff Layton
@ 2025-09-24 18:05 ` Jeff Layton
2025-09-24 18:05 ` [PATCH v3 13/38] nfsd: wire up GET_DIR_DELEGATION handling Jeff Layton
` (26 subsequent siblings)
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:05 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
RFC 8881 requires that the server reply with GDD_UNAVAIL when the client
requests a directory delegation that it already holds.
When setting a directory delegation, check that the client associated
with the stateid doesn't match an existing delegation. If it does,
reject the setlease attempt.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/nfsd/nfs4state.c | 29 ++++++++++++++++++++++++++++-
1 file changed, 28 insertions(+), 1 deletion(-)
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 25e4dc0a1459b73a0484c05cb3d1f0306784bb74..87857b351cd92c509ab7101645e17474f2dabcd4 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -88,6 +88,7 @@ void nfsd4_end_grace(struct nfsd_net *nn);
static void _free_cpntf_state_locked(struct nfsd_net *nn, struct nfs4_cpntf_state *cps);
static void nfsd4_file_hash_remove(struct nfs4_file *fi);
static void deleg_reaper(struct nfsd_net *nn);
+static bool nfsd_dir_may_setlease(struct file_lease *new, struct file_lease *old);
/* Locking: */
@@ -5580,6 +5581,31 @@ static const struct lease_manager_operations nfsd_lease_mng_ops = {
.lm_change = nfsd_change_deleg_cb,
};
+static const struct lease_manager_operations nfsd_dir_lease_mng_ops = {
+ .lm_breaker_owns_lease = nfsd_breaker_owns_lease,
+ .lm_break = nfsd_break_deleg_cb,
+ .lm_change = nfsd_change_deleg_cb,
+ .lm_may_setlease = nfsd_dir_may_setlease,
+};
+
+static bool
+nfsd_dir_may_setlease(struct file_lease *new, struct file_lease *old)
+{
+ struct nfs4_delegation *od, *nd;
+
+ /* Only conflicts with other nfsd dir delegs */
+ if (old->fl_lmops != &nfsd_dir_lease_mng_ops)
+ return true;
+
+ od = old->c.flc_owner;
+ nd = new->c.flc_owner;
+
+ /* Are these for the same client? No bueno if so */
+ if (od->dl_stid.sc_client == nd->dl_stid.sc_client)
+ return false;
+ return true;
+}
+
static __be32 nfsd4_check_seqid(struct nfsd4_compound_state *cstate, struct nfs4_stateowner *so, u32 seqid)
{
if (nfsd4_has_session(cstate))
@@ -5918,12 +5944,13 @@ static struct file_lease *nfs4_alloc_init_lease(struct nfs4_delegation *dp)
fl = locks_alloc_lease();
if (!fl)
return NULL;
- fl->fl_lmops = &nfsd_lease_mng_ops;
fl->c.flc_flags = FL_DELEG;
fl->c.flc_type = deleg_is_read(dp->dl_type) ? F_RDLCK : F_WRLCK;
fl->c.flc_owner = (fl_owner_t)dp;
fl->c.flc_pid = current->tgid;
fl->c.flc_file = dp->dl_stid.sc_file->fi_deleg_file->nf_file;
+ fl->fl_lmops = S_ISDIR(file_inode(fl->c.flc_file)->i_mode) ?
+ &nfsd_dir_lease_mng_ops : &nfsd_lease_mng_ops;
return fl;
}
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 13/38] nfsd: wire up GET_DIR_DELEGATION handling
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (11 preceding siblings ...)
2025-09-24 18:05 ` [PATCH v3 12/38] nfsd: check for delegation conflicts vs. the same client Jeff Layton
@ 2025-09-24 18:05 ` Jeff Layton
2025-09-24 18:06 ` [PATCH v3 14/38] filelock: rework the __break_lease API to use flags Jeff Layton
` (25 subsequent siblings)
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:05 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
Add a new routine for acquiring a read delegation on a directory. Since
the same CB_RECALL/DELEGRETURN infrastrure is used for regular and
directory delegations, we can just use a normal nfs4_delegation to
represent it.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/nfsd/nfs4proc.c | 21 +++++++++++++-
fs/nfsd/nfs4state.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++
fs/nfsd/state.h | 5 ++++
3 files changed, 107 insertions(+), 1 deletion(-)
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index e466cf52d7d7e1a78c3a469613a85ab3546d6d17..277022d437cec18e527c836f108f0e97c6844b23 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -2341,6 +2341,13 @@ nfsd4_get_dir_delegation(struct svc_rqst *rqstp,
union nfsd4_op_u *u)
{
struct nfsd4_get_dir_delegation *gdd = &u->get_dir_delegation;
+ struct nfs4_delegation *dd;
+ struct nfsd_file *nf;
+ __be32 status;
+
+ status = nfsd_file_acquire_dir(rqstp, &cstate->current_fh, &nf);
+ if (status != nfs_ok)
+ return status;
/*
* RFC 8881, section 18.39.3 says:
@@ -2354,7 +2361,19 @@ nfsd4_get_dir_delegation(struct svc_rqst *rqstp,
* return NFS4_OK with a non-fatal status of GDD4_UNAVAIL in this
* situation.
*/
- gdd->gddrnf_status = GDD4_UNAVAIL;
+ dd = nfsd_get_dir_deleg(cstate, gdd, nf);
+ if (IS_ERR(dd)) {
+ int err = PTR_ERR(dd);
+
+ if (err != -EAGAIN)
+ return nfserrno(err);
+ gdd->gddrnf_status = GDD4_UNAVAIL;
+ return nfs_ok;
+ }
+
+ gdd->gddrnf_status = GDD4_OK;
+ memcpy(&gdd->gddr_stateid, &dd->dl_stid.sc_stateid, sizeof(gdd->gddr_stateid));
+ nfs4_put_stid(&dd->dl_stid);
return nfs_ok;
}
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 87857b351cd92c509ab7101645e17474f2dabcd4..d1d586ec0e4e2bef908dc0671c34edab9cad5ba2 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -9421,3 +9421,85 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
nfs4_put_stid(&dp->dl_stid);
return status;
}
+
+/**
+ * nfsd_get_dir_deleg - attempt to get a directory delegation
+ * @cstate: compound state
+ * @gdd: GET_DIR_DELEGATION arg/resp structure
+ * @nf: nfsd_file opened on the directory
+ *
+ * Given a GET_DIR_DELEGATION request @gdd, attempt to acquire a delegation
+ * on the directory to which @nf refers. Note that this does not set up any
+ * sort of async notifications for the delegation.
+ */
+struct nfs4_delegation *
+nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate,
+ struct nfsd4_get_dir_delegation *gdd,
+ struct nfsd_file *nf)
+{
+ struct nfs4_client *clp = cstate->clp;
+ struct nfs4_delegation *dp;
+ struct file_lease *fl;
+ struct nfs4_file *fp;
+ int status = 0;
+
+ fp = nfsd4_alloc_file();
+ if (!fp)
+ return ERR_PTR(-ENOMEM);
+
+ nfsd4_file_init(&cstate->current_fh, fp);
+ fp->fi_deleg_file = nf;
+ fp->fi_delegees = 1;
+
+ /* if this client already has one, return that it's unavailable */
+ spin_lock(&state_lock);
+ spin_lock(&fp->fi_lock);
+ if (nfs4_delegation_exists(clp, fp))
+ status = -EAGAIN;
+ spin_unlock(&fp->fi_lock);
+ spin_unlock(&state_lock);
+
+ if (status)
+ goto out_delegees;
+
+ /* Try to set up the lease */
+ status = -ENOMEM;
+ dp = alloc_init_deleg(clp, fp, NULL, NFS4_OPEN_DELEGATE_READ);
+ if (!dp)
+ goto out_delegees;
+
+ fl = nfs4_alloc_init_lease(dp);
+ if (!fl)
+ goto out_put_stid;
+
+ status = kernel_setlease(nf->nf_file,
+ fl->c.flc_type, &fl, NULL);
+ if (fl)
+ locks_free_lease(fl);
+ if (status)
+ goto out_put_stid;
+
+ /*
+ * Now, try to hash it. This can fail if we race another nfsd task
+ * trying to set a delegation on the same file. If that happens,
+ * then just say UNAVAIL.
+ */
+ spin_lock(&state_lock);
+ spin_lock(&clp->cl_lock);
+ spin_lock(&fp->fi_lock);
+ status = hash_delegation_locked(dp, fp);
+ spin_unlock(&fp->fi_lock);
+ spin_unlock(&clp->cl_lock);
+ spin_unlock(&state_lock);
+
+ if (!status)
+ return dp;
+
+ /* Something failed. Drop the lease and clean up the stid */
+ kernel_setlease(fp->fi_deleg_file->nf_file, F_UNLCK, NULL, (void **)&dp);
+out_put_stid:
+ nfs4_put_stid(&dp->dl_stid);
+out_delegees:
+ put_deleg_file(fp);
+ return ERR_PTR(status);
+}
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 1e736f4024263ffa9c93bcc9ec48f44566a8cc77..b052c1effdc5356487c610db9728df8ecfe851d4 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -867,4 +867,9 @@ static inline bool try_to_expire_client(struct nfs4_client *clp)
extern __be32 nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp,
struct dentry *dentry, struct nfs4_delegation **pdp);
+
+struct nfsd4_get_dir_delegation;
+struct nfs4_delegation *nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate,
+ struct nfsd4_get_dir_delegation *gdd,
+ struct nfsd_file *nf);
#endif /* NFSD4_STATE_H */
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 14/38] filelock: rework the __break_lease API to use flags
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (12 preceding siblings ...)
2025-09-24 18:05 ` [PATCH v3 13/38] nfsd: wire up GET_DIR_DELEGATION handling Jeff Layton
@ 2025-09-24 18:06 ` Jeff Layton
2025-09-24 18:06 ` [PATCH v3 15/38] filelock: add struct delegated_inode Jeff Layton
` (24 subsequent siblings)
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:06 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
Declare a set of LEASE_BREAK_* flags that can be used to control how
lease breaks work instead of requiring a type and an openmode.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/locks.c | 30 +++++++++++++++++-----------
include/linux/filelock.h | 52 +++++++++++++++++++++++++++++++++++-------------
2 files changed, 56 insertions(+), 26 deletions(-)
diff --git a/fs/locks.c b/fs/locks.c
index c1b4575c827648275a8d6628a8f279d382e46fc4..4cfa4fc7130137b2850cab871bc3b2b23bbd3db1 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -1529,29 +1529,35 @@ any_leases_conflict(struct inode *inode, struct file_lease *breaker)
/**
* __break_lease - revoke all outstanding leases on file
* @inode: the inode of the file to return
- * @mode: O_RDONLY: break only write leases; O_WRONLY or O_RDWR:
- * break all leases
- * @type: FL_LEASE: break leases and delegations; FL_DELEG: break
- * only delegations
+ * @flags: LEASE_BREAK_* flags
*
* break_lease (inlined for speed) has checked there already is at least
* some kind of lock (maybe a lease) on this file. Leases are broken on
- * a call to open() or truncate(). This function can sleep unless you
- * specified %O_NONBLOCK to your open().
+ * a call to open() or truncate(). This function can block waiting for the
+ * lease break unless you specify LEASE_BREAK_NONBLOCK.
*/
-int __break_lease(struct inode *inode, unsigned int mode, unsigned int type)
+int __break_lease(struct inode *inode, unsigned int flags)
{
- int error = 0;
- struct file_lock_context *ctx;
struct file_lease *new_fl, *fl, *tmp;
+ struct file_lock_context *ctx;
unsigned long break_time;
- int want_write = (mode & O_ACCMODE) != O_RDONLY;
LIST_HEAD(dispose);
+ bool want_write = !(flags & LEASE_BREAK_OPEN_RDONLY);
+ int error = 0;
+
new_fl = lease_alloc(NULL, want_write ? F_WRLCK : F_RDLCK);
if (IS_ERR(new_fl))
return PTR_ERR(new_fl);
- new_fl->c.flc_flags = type;
+
+ if (flags & LEASE_BREAK_LEASE)
+ new_fl->c.flc_flags = FL_LEASE;
+ else if (flags & LEASE_BREAK_DELEG)
+ new_fl->c.flc_flags = FL_DELEG;
+ else if (flags & LEASE_BREAK_LAYOUT)
+ new_fl->c.flc_flags = FL_LAYOUT;
+ else
+ return -EINVAL;
/* typically we will check that ctx is non-NULL before calling */
ctx = locks_inode_context(inode);
@@ -1596,7 +1602,7 @@ int __break_lease(struct inode *inode, unsigned int mode, unsigned int type)
if (list_empty(&ctx->flc_lease))
goto out;
- if (mode & O_NONBLOCK) {
+ if (flags & LEASE_BREAK_NONBLOCK) {
trace_break_lease_noblock(inode, new_fl);
error = -EWOULDBLOCK;
goto out;
diff --git a/include/linux/filelock.h b/include/linux/filelock.h
index 70079beddf61aa32ef01f1114cf0cb3ffaf2131a..29078156d863b4e66dc59085f7c2cebd2c078ec3 100644
--- a/include/linux/filelock.h
+++ b/include/linux/filelock.h
@@ -226,7 +226,14 @@ int locks_lock_inode_wait(struct inode *inode, struct file_lock *fl);
void locks_init_lease(struct file_lease *);
void locks_free_lease(struct file_lease *fl);
struct file_lease *locks_alloc_lease(void);
-int __break_lease(struct inode *inode, unsigned int flags, unsigned int type);
+
+#define LEASE_BREAK_LEASE BIT(0) // break leases and delegations
+#define LEASE_BREAK_DELEG BIT(1) // break delegations only
+#define LEASE_BREAK_LAYOUT BIT(2) // break layouts only
+#define LEASE_BREAK_NONBLOCK BIT(3) // non-blocking break
+#define LEASE_BREAK_OPEN_RDONLY BIT(4) // readonly open event
+
+int __break_lease(struct inode *inode, unsigned int flags);
void lease_get_mtime(struct inode *, struct timespec64 *time);
int generic_setlease(struct file *, int, struct file_lease **, void **priv);
int kernel_setlease(struct file *, int, struct file_lease **, void **);
@@ -381,7 +388,7 @@ static inline int locks_lock_inode_wait(struct inode *inode, struct file_lock *f
return -ENOLCK;
}
-static inline int __break_lease(struct inode *inode, unsigned int mode, unsigned int type)
+static inline int __break_lease(struct inode *inode, unsigned int flags)
{
return 0;
}
@@ -442,6 +449,17 @@ static inline int locks_lock_file_wait(struct file *filp, struct file_lock *fl)
}
#ifdef CONFIG_FILE_LOCKING
+static inline unsigned int openmode_to_lease_flags(unsigned int mode)
+{
+ unsigned int flags = 0;
+
+ if ((mode & O_ACCMODE) == O_RDONLY)
+ flags |= LEASE_BREAK_OPEN_RDONLY;
+ if (mode & O_NONBLOCK)
+ flags |= LEASE_BREAK_NONBLOCK;
+ return flags;
+}
+
static inline int break_lease(struct inode *inode, unsigned int mode)
{
struct file_lock_context *flctx;
@@ -457,11 +475,11 @@ static inline int break_lease(struct inode *inode, unsigned int mode)
return 0;
smp_mb();
if (!list_empty_careful(&flctx->flc_lease))
- return __break_lease(inode, mode, FL_LEASE);
+ return __break_lease(inode, LEASE_BREAK_LEASE | openmode_to_lease_flags(mode));
return 0;
}
-static inline int break_deleg(struct inode *inode, unsigned int mode)
+static inline int break_deleg(struct inode *inode, unsigned int flags)
{
struct file_lock_context *flctx;
@@ -475,8 +493,10 @@ static inline int break_deleg(struct inode *inode, unsigned int mode)
if (!flctx)
return 0;
smp_mb();
- if (!list_empty_careful(&flctx->flc_lease))
- return __break_lease(inode, mode, FL_DELEG);
+ if (!list_empty_careful(&flctx->flc_lease)) {
+ flags |= LEASE_BREAK_DELEG;
+ return __break_lease(inode, flags);
+ }
return 0;
}
@@ -484,7 +504,7 @@ static inline int try_break_deleg(struct inode *inode, struct inode **delegated_
{
int ret;
- ret = break_deleg(inode, O_WRONLY|O_NONBLOCK);
+ ret = break_deleg(inode, LEASE_BREAK_NONBLOCK);
if (ret == -EWOULDBLOCK && delegated_inode) {
*delegated_inode = inode;
ihold(inode);
@@ -496,7 +516,7 @@ static inline int break_deleg_wait(struct inode **delegated_inode)
{
int ret;
- ret = break_deleg(*delegated_inode, O_WRONLY);
+ ret = break_deleg(*delegated_inode, 0);
iput(*delegated_inode);
*delegated_inode = NULL;
return ret;
@@ -505,20 +525,24 @@ static inline int break_deleg_wait(struct inode **delegated_inode)
static inline int break_layout(struct inode *inode, bool wait)
{
smp_mb();
- if (inode->i_flctx && !list_empty_careful(&inode->i_flctx->flc_lease))
- return __break_lease(inode,
- wait ? O_WRONLY : O_WRONLY | O_NONBLOCK,
- FL_LAYOUT);
+ if (inode->i_flctx && !list_empty_careful(&inode->i_flctx->flc_lease)) {
+ unsigned int flags = LEASE_BREAK_LAYOUT;
+
+ if (!wait)
+ flags |= LEASE_BREAK_NONBLOCK;
+
+ return __break_lease(inode, flags);
+ }
return 0;
}
#else /* !CONFIG_FILE_LOCKING */
-static inline int break_lease(struct inode *inode, unsigned int mode)
+static inline int break_lease(struct inode *inode, bool wait)
{
return 0;
}
-static inline int break_deleg(struct inode *inode, unsigned int mode)
+static inline int break_deleg(struct inode *inode, unsigned int flags)
{
return 0;
}
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 15/38] filelock: add struct delegated_inode
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (13 preceding siblings ...)
2025-09-24 18:06 ` [PATCH v3 14/38] filelock: rework the __break_lease API to use flags Jeff Layton
@ 2025-09-24 18:06 ` Jeff Layton
2025-09-24 18:06 ` [PATCH v3 16/38] filelock: add support for ignoring deleg breaks for dir change events Jeff Layton
` (23 subsequent siblings)
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:06 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
Later patches will add support for ignoring certain events rather than
breaking the delegation. To do this, the VFS must inform __break_lease()
about why the delegation is being broken. Convert the delegated_inode
double pointer in various VFS functions into a struct that has a inode
and a reason for the delegation break. Also set the reason value in the
appropriate places.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/attr.c | 4 +--
fs/namei.c | 75 +++++++++++++++++++++++++-----------------------
fs/open.c | 8 +++---
fs/posix_acl.c | 12 ++++----
fs/utimes.c | 4 +--
fs/xattr.c | 16 +++++------
include/linux/filelock.h | 63 +++++++++++++++++++++++++++++-----------
include/linux/fs.h | 11 +++----
include/linux/xattr.h | 4 +--
9 files changed, 116 insertions(+), 81 deletions(-)
diff --git a/fs/attr.c b/fs/attr.c
index 795f231d00e8eaaadf5b62f241655cb4b69cb507..01374453805acba7ea8e1e3e4818b76fffb45334 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -415,7 +415,7 @@ EXPORT_SYMBOL(may_setattr);
* performed on the raw inode simply pass @nop_mnt_idmap.
*/
int notify_change(struct mnt_idmap *idmap, struct dentry *dentry,
- struct iattr *attr, struct inode **delegated_inode)
+ struct iattr *attr, struct delegated_inode *delegated_inode)
{
struct inode *inode = dentry->d_inode;
umode_t mode = inode->i_mode;
@@ -537,7 +537,7 @@ int notify_change(struct mnt_idmap *idmap, struct dentry *dentry,
* breaking the delegation in this case.
*/
if (!(ia_valid & ATTR_DELEG)) {
- error = try_break_deleg(inode, delegated_inode);
+ error = try_break_deleg(inode, 0, delegated_inode);
if (error)
return error;
}
diff --git a/fs/namei.c b/fs/namei.c
index 7bcd898c84138061030f1f8b91273261cdf2a9b4..e50af814b25079038a48d53bd73c00a26db4f829 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -3372,7 +3372,7 @@ static inline umode_t vfs_prepare_mode(struct mnt_idmap *idmap,
static int __vfs_create(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, umode_t mode, bool want_excl,
- struct inode **delegated_inode)
+ struct delegated_inode *delegated_inode)
{
int error;
@@ -3387,7 +3387,7 @@ static int __vfs_create(struct mnt_idmap *idmap, struct inode *dir,
error = security_inode_create(dir, dentry, mode);
if (error)
return error;
- error = try_break_deleg(dir, delegated_inode);
+ error = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, delegated_inode);
if (error)
return error;
error = dir->i_op->create(idmap, dir, dentry, mode, want_excl);
@@ -3618,8 +3618,8 @@ static struct dentry *atomic_open(struct nameidata *nd, struct dentry *dentry,
* An error code is returned on failure.
*/
static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
- const struct open_flags *op,
- bool got_write, struct inode **delegated_inode)
+ const struct open_flags *op, bool got_write,
+ struct delegated_inode *delegated_inode)
{
struct mnt_idmap *idmap;
struct dentry *dir = nd->path.dentry;
@@ -3709,7 +3709,7 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
/* Negative dentry, just create the file */
if (!dentry->d_inode && (open_flag & O_CREAT)) {
/* but break the directory lease first! */
- error = try_break_deleg(dir_inode, delegated_inode);
+ error = try_break_deleg(dir_inode, LEASE_BREAK_DIR_CREATE, delegated_inode);
if (error)
goto out_dput;
@@ -3776,7 +3776,7 @@ static const char *open_last_lookups(struct nameidata *nd,
struct file *file, const struct open_flags *op)
{
struct dentry *dir = nd->path.dentry;
- struct inode *delegated_inode = NULL;
+ struct delegated_inode delegated_inode = { };
int open_flag = op->open_flag;
bool got_write = false;
struct dentry *dentry;
@@ -3836,7 +3836,7 @@ static const char *open_last_lookups(struct nameidata *nd,
mnt_drop_write(nd->path.mnt);
if (IS_ERR(dentry)) {
- if (delegated_inode) {
+ if (deleg_inode(&delegated_inode)) {
int error = break_deleg_wait(&delegated_inode);
if (!error)
@@ -4217,7 +4217,7 @@ EXPORT_SYMBOL(user_path_create);
static int __vfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, umode_t mode, dev_t dev,
- struct inode **delegated_inode)
+ struct delegated_inode *delegated_inode)
{
bool is_whiteout = S_ISCHR(mode) && dev == WHITEOUT_DEV;
int error = may_create(idmap, dir, dentry);
@@ -4241,7 +4241,7 @@ static int __vfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
if (error)
return error;
- error = try_break_deleg(dir, delegated_inode);
+ error = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, delegated_inode);
if (error)
return error;
@@ -4299,7 +4299,7 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode,
struct path path;
int error;
unsigned int lookup_flags = 0;
- struct inode *delegated_inode = NULL;
+ struct delegated_inode delegated_inode = { };
error = may_mknod(mode);
if (error)
@@ -4337,7 +4337,7 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode,
}
out2:
done_path_create(&path, dentry);
- if (delegated_inode) {
+ if (deleg_inode(&delegated_inode)) {
error = break_deleg_wait(&delegated_inode);
if (!error)
goto retry;
@@ -4386,7 +4386,7 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d
*/
struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, umode_t mode,
- struct inode **delegated_inode)
+ struct delegated_inode *delegated_inode)
{
int error;
unsigned max_links = dir->i_sb->s_max_links;
@@ -4409,7 +4409,7 @@ struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
if (max_links && dir->i_nlink >= max_links)
goto err;
- error = try_break_deleg(dir, delegated_inode);
+ error = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, delegated_inode);
if (error)
goto err;
@@ -4436,7 +4436,7 @@ int do_mkdirat(int dfd, struct filename *name, umode_t mode)
struct path path;
int error;
unsigned int lookup_flags = LOOKUP_DIRECTORY;
- struct inode *delegated_inode = NULL;
+ struct delegated_inode delegated_inode = { };
retry:
dentry = filename_create(dfd, name, &path, lookup_flags);
@@ -4453,7 +4453,7 @@ int do_mkdirat(int dfd, struct filename *name, umode_t mode)
error = PTR_ERR(dentry);
}
done_path_create(&path, dentry);
- if (delegated_inode) {
+ if (deleg_inode(&delegated_inode)) {
error = break_deleg_wait(&delegated_inode);
if (!error)
goto retry;
@@ -4478,7 +4478,7 @@ SYSCALL_DEFINE2(mkdir, const char __user *, pathname, umode_t, mode)
}
static int __vfs_rmdir(struct mnt_idmap *idmap, struct inode *dir,
- struct dentry *dentry, struct inode **delegated_inode)
+ struct dentry *dentry, struct delegated_inode *delegated_inode)
{
int error = may_delete(idmap, dir, dentry, 1);
@@ -4500,7 +4500,7 @@ static int __vfs_rmdir(struct mnt_idmap *idmap, struct inode *dir,
if (error)
goto out;
- error = try_break_deleg(dir, delegated_inode);
+ error = try_break_deleg(dir, LEASE_BREAK_DIR_DELETE, delegated_inode);
if (error)
goto out;
@@ -4550,7 +4550,7 @@ int do_rmdir(int dfd, struct filename *name)
struct qstr last;
int type;
unsigned int lookup_flags = 0;
- struct inode *delegated_inode = NULL;
+ struct delegated_inode delegated_inode = { };
retry:
error = filename_parentat(dfd, name, lookup_flags, &path, &last, &type);
if (error)
@@ -4589,7 +4589,7 @@ int do_rmdir(int dfd, struct filename *name)
mnt_drop_write(path.mnt);
exit2:
path_put(&path);
- if (delegated_inode) {
+ if (deleg_inode(&delegated_inode)) {
error = break_deleg_wait(&delegated_inode);
if (!error)
goto retry;
@@ -4634,7 +4634,7 @@ SYSCALL_DEFINE1(rmdir, const char __user *, pathname)
* raw inode simply pass @nop_mnt_idmap.
*/
int vfs_unlink(struct mnt_idmap *idmap, struct inode *dir,
- struct dentry *dentry, struct inode **delegated_inode)
+ struct dentry *dentry, struct delegated_inode *delegated_inode)
{
struct inode *target = dentry->d_inode;
int error = may_delete(idmap, dir, dentry, 0);
@@ -4653,10 +4653,10 @@ int vfs_unlink(struct mnt_idmap *idmap, struct inode *dir,
else {
error = security_inode_unlink(dir, dentry);
if (!error) {
- error = try_break_deleg(dir, delegated_inode);
+ error = try_break_deleg(dir, LEASE_BREAK_DIR_DELETE, delegated_inode);
if (error)
goto out;
- error = try_break_deleg(target, delegated_inode);
+ error = try_break_deleg(target, 0, delegated_inode);
if (error)
goto out;
error = dir->i_op->unlink(dir, dentry);
@@ -4695,7 +4695,7 @@ int do_unlinkat(int dfd, struct filename *name)
struct qstr last;
int type;
struct inode *inode = NULL;
- struct inode *delegated_inode = NULL;
+ struct delegated_inode delegated_inode = { };
unsigned int lookup_flags = 0;
retry:
error = filename_parentat(dfd, name, lookup_flags, &path, &last, &type);
@@ -4732,7 +4732,7 @@ int do_unlinkat(int dfd, struct filename *name)
if (inode)
iput(inode); /* truncate the inode here */
inode = NULL;
- if (delegated_inode) {
+ if (deleg_inode(&delegated_inode)) {
error = break_deleg_wait(&delegated_inode);
if (!error)
goto retry_deleg;
@@ -4881,7 +4881,7 @@ SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newn
*/
int vfs_link(struct dentry *old_dentry, struct mnt_idmap *idmap,
struct inode *dir, struct dentry *new_dentry,
- struct inode **delegated_inode)
+ struct delegated_inode *delegated_inode)
{
struct inode *inode = old_dentry->d_inode;
unsigned max_links = dir->i_sb->s_max_links;
@@ -4925,9 +4925,9 @@ int vfs_link(struct dentry *old_dentry, struct mnt_idmap *idmap,
else if (max_links && inode->i_nlink >= max_links)
error = -EMLINK;
else {
- error = try_break_deleg(dir, delegated_inode);
+ error = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, delegated_inode);
if (!error)
- error = try_break_deleg(inode, delegated_inode);
+ error = try_break_deleg(inode, 0, delegated_inode);
if (!error)
error = dir->i_op->link(old_dentry, dir, new_dentry);
}
@@ -4959,7 +4959,7 @@ int do_linkat(int olddfd, struct filename *old, int newdfd,
struct mnt_idmap *idmap;
struct dentry *new_dentry;
struct path old_path, new_path;
- struct inode *delegated_inode = NULL;
+ struct delegated_inode delegated_inode = { };
int how = 0;
int error;
@@ -5003,7 +5003,7 @@ int do_linkat(int olddfd, struct filename *old, int newdfd,
new_dentry, &delegated_inode);
out_dput:
done_path_create(&new_path, new_dentry);
- if (delegated_inode) {
+ if (deleg_inode(&delegated_inode)) {
error = break_deleg_wait(&delegated_inode);
if (!error) {
path_put(&old_path);
@@ -5089,7 +5089,7 @@ int vfs_rename(struct renamedata *rd)
struct inode *new_dir = d_inode(rd->new_parent);
struct dentry *old_dentry = rd->old_dentry;
struct dentry *new_dentry = rd->new_dentry;
- struct inode **delegated_inode = rd->delegated_inode;
+ struct delegated_inode *delegated_inode = rd->delegated_inode;
unsigned int flags = rd->flags;
bool is_dir = d_is_dir(old_dentry);
struct inode *source = old_dentry->d_inode;
@@ -5194,21 +5194,24 @@ int vfs_rename(struct renamedata *rd)
old_dir->i_nlink >= max_links)
goto out;
}
- error = try_break_deleg(old_dir, delegated_inode);
+ error = try_break_deleg(old_dir,
+ old_dir == new_dir ? LEASE_BREAK_DIR_RENAME :
+ LEASE_BREAK_DIR_DELETE,
+ delegated_inode);
if (error)
goto out;
if (new_dir != old_dir) {
- error = try_break_deleg(new_dir, delegated_inode);
+ error = try_break_deleg(new_dir, LEASE_BREAK_DIR_CREATE, delegated_inode);
if (error)
goto out;
}
if (!is_dir) {
- error = try_break_deleg(source, delegated_inode);
+ error = try_break_deleg(source, 0, delegated_inode);
if (error)
goto out;
}
if (target && !new_is_dir) {
- error = try_break_deleg(target, delegated_inode);
+ error = try_break_deleg(target, 0, delegated_inode);
if (error)
goto out;
}
@@ -5260,7 +5263,7 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd,
struct path old_path, new_path;
struct qstr old_last, new_last;
int old_type, new_type;
- struct inode *delegated_inode = NULL;
+ struct delegated_inode delegated_inode = { };
unsigned int lookup_flags = 0, target_flags =
LOOKUP_RENAME_TARGET | LOOKUP_CREATE;
bool should_retry = false;
@@ -5369,7 +5372,7 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd,
exit3:
unlock_rename(new_path.dentry, old_path.dentry);
exit_lock_rename:
- if (delegated_inode) {
+ if (deleg_inode(&delegated_inode)) {
error = break_deleg_wait(&delegated_inode);
if (!error)
goto retry_deleg;
diff --git a/fs/open.c b/fs/open.c
index 9655158c38853e3435adb77f22ce0c81c3f0aed4..f9fe763a7c8802f5b6fad1c7ff8256dddcd8f48c 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -631,7 +631,7 @@ SYSCALL_DEFINE1(chroot, const char __user *, filename)
int chmod_common(const struct path *path, umode_t mode)
{
struct inode *inode = path->dentry->d_inode;
- struct inode *delegated_inode = NULL;
+ struct delegated_inode delegated_inode = { };
struct iattr newattrs;
int error;
@@ -651,7 +651,7 @@ int chmod_common(const struct path *path, umode_t mode)
&newattrs, &delegated_inode);
out_unlock:
inode_unlock(inode);
- if (delegated_inode) {
+ if (deleg_inode(&delegated_inode)) {
error = break_deleg_wait(&delegated_inode);
if (!error)
goto retry_deleg;
@@ -756,7 +756,7 @@ int chown_common(const struct path *path, uid_t user, gid_t group)
struct mnt_idmap *idmap;
struct user_namespace *fs_userns;
struct inode *inode = path->dentry->d_inode;
- struct inode *delegated_inode = NULL;
+ struct delegated_inode delegated_inode = { };
int error;
struct iattr newattrs;
kuid_t uid;
@@ -791,7 +791,7 @@ int chown_common(const struct path *path, uid_t user, gid_t group)
error = notify_change(idmap, path->dentry, &newattrs,
&delegated_inode);
inode_unlock(inode);
- if (delegated_inode) {
+ if (deleg_inode(&delegated_inode)) {
error = break_deleg_wait(&delegated_inode);
if (!error)
goto retry_deleg;
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index 4050942ab52f95741da2df13d191ade5c5ca12a2..19a45fc8e413d0fb2e2d906488c3ce648bb318a4 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -1091,7 +1091,7 @@ int vfs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
int acl_type;
int error;
struct inode *inode = d_inode(dentry);
- struct inode *delegated_inode = NULL;
+ struct delegated_inode delegated_inode = { };
acl_type = posix_acl_type(acl_name);
if (acl_type < 0)
@@ -1125,7 +1125,7 @@ int vfs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
if (error)
goto out_inode_unlock;
- error = try_break_deleg(inode, &delegated_inode);
+ error = try_break_deleg(inode, 0, &delegated_inode);
if (error)
goto out_inode_unlock;
@@ -1141,7 +1141,7 @@ int vfs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
out_inode_unlock:
inode_unlock(inode);
- if (delegated_inode) {
+ if (deleg_inode(&delegated_inode)) {
error = break_deleg_wait(&delegated_inode);
if (!error)
goto retry_deleg;
@@ -1212,7 +1212,7 @@ int vfs_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry,
int acl_type;
int error;
struct inode *inode = d_inode(dentry);
- struct inode *delegated_inode = NULL;
+ struct delegated_inode delegated_inode = { };
acl_type = posix_acl_type(acl_name);
if (acl_type < 0)
@@ -1233,7 +1233,7 @@ int vfs_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry,
if (error)
goto out_inode_unlock;
- error = try_break_deleg(inode, &delegated_inode);
+ error = try_break_deleg(inode, 0, &delegated_inode);
if (error)
goto out_inode_unlock;
@@ -1249,7 +1249,7 @@ int vfs_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry,
out_inode_unlock:
inode_unlock(inode);
- if (delegated_inode) {
+ if (deleg_inode(&delegated_inode)) {
error = break_deleg_wait(&delegated_inode);
if (!error)
goto retry_deleg;
diff --git a/fs/utimes.c b/fs/utimes.c
index c7c7958e57b22f91646ca9f76d18781b64d371a3..4145cbbc190ffb5990fef248300c853ec32d643f 100644
--- a/fs/utimes.c
+++ b/fs/utimes.c
@@ -22,7 +22,7 @@ int vfs_utimes(const struct path *path, struct timespec64 *times)
int error;
struct iattr newattrs;
struct inode *inode = path->dentry->d_inode;
- struct inode *delegated_inode = NULL;
+ struct delegated_inode delegated_inode = { };
if (times) {
if (!nsec_valid(times[0].tv_nsec) ||
@@ -66,7 +66,7 @@ int vfs_utimes(const struct path *path, struct timespec64 *times)
error = notify_change(mnt_idmap(path->mnt), path->dentry, &newattrs,
&delegated_inode);
inode_unlock(inode);
- if (delegated_inode) {
+ if (deleg_inode(&delegated_inode)) {
error = break_deleg_wait(&delegated_inode);
if (!error)
goto retry_deleg;
diff --git a/fs/xattr.c b/fs/xattr.c
index 8851a5ef34f5ab34383975dd4cef537de3f6391e..276b445bfa013e8c0e10231c00dd8e36368c7bdf 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -274,7 +274,7 @@ int __vfs_setxattr_noperm(struct mnt_idmap *idmap,
int
__vfs_setxattr_locked(struct mnt_idmap *idmap, struct dentry *dentry,
const char *name, const void *value, size_t size,
- int flags, struct inode **delegated_inode)
+ int flags, struct delegated_inode *delegated_inode)
{
struct inode *inode = dentry->d_inode;
int error;
@@ -288,7 +288,7 @@ __vfs_setxattr_locked(struct mnt_idmap *idmap, struct dentry *dentry,
if (error)
goto out;
- error = try_break_deleg(inode, delegated_inode);
+ error = try_break_deleg(inode, 0, delegated_inode);
if (error)
goto out;
@@ -305,7 +305,7 @@ vfs_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
const char *name, const void *value, size_t size, int flags)
{
struct inode *inode = dentry->d_inode;
- struct inode *delegated_inode = NULL;
+ struct delegated_inode delegated_inode = { };
const void *orig_value = value;
int error;
@@ -322,7 +322,7 @@ vfs_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
flags, &delegated_inode);
inode_unlock(inode);
- if (delegated_inode) {
+ if (deleg_inode(&delegated_inode)) {
error = break_deleg_wait(&delegated_inode);
if (!error)
goto retry_deleg;
@@ -533,7 +533,7 @@ EXPORT_SYMBOL(__vfs_removexattr);
int
__vfs_removexattr_locked(struct mnt_idmap *idmap,
struct dentry *dentry, const char *name,
- struct inode **delegated_inode)
+ struct delegated_inode *delegated_inode)
{
struct inode *inode = dentry->d_inode;
int error;
@@ -546,7 +546,7 @@ __vfs_removexattr_locked(struct mnt_idmap *idmap,
if (error)
goto out;
- error = try_break_deleg(inode, delegated_inode);
+ error = try_break_deleg(inode, 0, delegated_inode);
if (error)
goto out;
@@ -567,7 +567,7 @@ vfs_removexattr(struct mnt_idmap *idmap, struct dentry *dentry,
const char *name)
{
struct inode *inode = dentry->d_inode;
- struct inode *delegated_inode = NULL;
+ struct delegated_inode delegated_inode = { };
int error;
retry_deleg:
@@ -576,7 +576,7 @@ vfs_removexattr(struct mnt_idmap *idmap, struct dentry *dentry,
name, &delegated_inode);
inode_unlock(inode);
- if (delegated_inode) {
+ if (deleg_inode(&delegated_inode)) {
error = break_deleg_wait(&delegated_inode);
if (!error)
goto retry_deleg;
diff --git a/include/linux/filelock.h b/include/linux/filelock.h
index 29078156d863b4e66dc59085f7c2cebd2c078ec3..68901da7671694f68d939085d08e0da21712540d 100644
--- a/include/linux/filelock.h
+++ b/include/linux/filelock.h
@@ -160,6 +160,19 @@ struct file_lock_context {
struct list_head flc_lease;
};
+#define LEASE_BREAK_LEASE BIT(0) // break leases and delegations
+#define LEASE_BREAK_DELEG BIT(1) // break delegations only
+#define LEASE_BREAK_LAYOUT BIT(2) // break layouts only
+#define LEASE_BREAK_NONBLOCK BIT(3) // non-blocking break
+#define LEASE_BREAK_OPEN_RDONLY BIT(4) // readonly open event
+#define LEASE_BREAK_DIR_CREATE BIT(6) // dir deleg create event
+#define LEASE_BREAK_DIR_DELETE BIT(7) // dir deleg delete event
+#define LEASE_BREAK_DIR_RENAME BIT(8) // dir deleg rename event
+
+#define LEASE_BREAK_DIR_REASON_MASK (LEASE_BREAK_DIR_CREATE | \
+ LEASE_BREAK_DIR_DELETE | \
+ LEASE_BREAK_DIR_RENAME)
+
#ifdef CONFIG_FILE_LOCKING
int fcntl_getlk(struct file *, unsigned int, struct flock *);
int fcntl_setlk(unsigned int, struct file *, unsigned int,
@@ -227,12 +240,6 @@ void locks_init_lease(struct file_lease *);
void locks_free_lease(struct file_lease *fl);
struct file_lease *locks_alloc_lease(void);
-#define LEASE_BREAK_LEASE BIT(0) // break leases and delegations
-#define LEASE_BREAK_DELEG BIT(1) // break delegations only
-#define LEASE_BREAK_LAYOUT BIT(2) // break layouts only
-#define LEASE_BREAK_NONBLOCK BIT(3) // non-blocking break
-#define LEASE_BREAK_OPEN_RDONLY BIT(4) // readonly open event
-
int __break_lease(struct inode *inode, unsigned int flags);
void lease_get_mtime(struct inode *, struct timespec64 *time);
int generic_setlease(struct file *, int, struct file_lease **, void **priv);
@@ -500,25 +507,41 @@ static inline int break_deleg(struct inode *inode, unsigned int flags)
return 0;
}
-static inline int try_break_deleg(struct inode *inode, struct inode **delegated_inode)
+struct delegated_inode {
+ struct inode *di_inode;
+ unsigned int di_reason; // LEASE_BREAK_* flags
+};
+
+static inline struct inode *deleg_inode(struct delegated_inode *di)
+{
+ return di->di_inode;
+}
+
+static inline int try_break_deleg(struct inode *inode, unsigned int reason,
+ struct delegated_inode *di)
{
int ret;
- ret = break_deleg(inode, LEASE_BREAK_NONBLOCK);
- if (ret == -EWOULDBLOCK && delegated_inode) {
- *delegated_inode = inode;
+ /* Clear any extraneous reason bits, after warning if any are set */
+ WARN_ON_ONCE(reason & ~LEASE_BREAK_DIR_REASON_MASK);
+ reason &= LEASE_BREAK_DIR_REASON_MASK;
+
+ ret = break_deleg(inode, reason | LEASE_BREAK_NONBLOCK);
+ if (ret == -EWOULDBLOCK && di) {
+ di->di_inode = inode;
+ di->di_reason = reason;
ihold(inode);
}
return ret;
}
-static inline int break_deleg_wait(struct inode **delegated_inode)
+static inline int break_deleg_wait(struct delegated_inode *di)
{
int ret;
- ret = break_deleg(*delegated_inode, 0);
- iput(*delegated_inode);
- *delegated_inode = NULL;
+ ret = break_deleg(di->di_inode, di->di_reason);
+ iput(di->di_inode);
+ di->di_inode = NULL;
return ret;
}
@@ -537,6 +560,13 @@ static inline int break_layout(struct inode *inode, bool wait)
}
#else /* !CONFIG_FILE_LOCKING */
+struct delegated_inode { };
+
+static inline struct inode *deleg_inode(struct delegated_inode *di)
+{
+ return NULL;
+}
+
static inline int break_lease(struct inode *inode, bool wait)
{
return 0;
@@ -547,12 +577,13 @@ static inline int break_deleg(struct inode *inode, unsigned int flags)
return 0;
}
-static inline int try_break_deleg(struct inode *inode, struct inode **delegated_inode)
+static inline int try_break_deleg(struct inode *inode, unsigned int reason,
+ struct delegated_inode *delegated_inode)
{
return 0;
}
-static inline int break_deleg_wait(struct inode **delegated_inode)
+static inline int break_deleg_wait(struct delegated_inode *delegated_inode)
{
BUG();
return 0;
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 24a091509f12ce65a2c8343d438fccf423d3062b..de178875462a1cb8eded868d8f8539dcc747e03f 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -82,6 +82,7 @@ struct fs_context;
struct fs_parameter_spec;
struct file_kattr;
struct iomap_ops;
+struct delegated_inode;
extern void __init inode_init(void);
extern void __init inode_init_early(void);
@@ -1997,16 +1998,16 @@ bool inode_owner_or_capable(struct mnt_idmap *idmap,
int vfs_create(struct mnt_idmap *, struct inode *,
struct dentry *, umode_t, bool);
struct dentry *vfs_mkdir(struct mnt_idmap *, struct inode *,
- struct dentry *, umode_t, struct inode **);
+ struct dentry *, umode_t, struct delegated_inode *);
int vfs_mknod(struct mnt_idmap *, struct inode *, struct dentry *,
umode_t, dev_t);
int vfs_symlink(struct mnt_idmap *, struct inode *,
struct dentry *, const char *);
int vfs_link(struct dentry *, struct mnt_idmap *, struct inode *,
- struct dentry *, struct inode **);
+ struct dentry *, struct delegated_inode *);
int vfs_rmdir(struct mnt_idmap *, struct inode *, struct dentry *);
int vfs_unlink(struct mnt_idmap *, struct inode *, struct dentry *,
- struct inode **);
+ struct delegated_inode *);
/**
* struct renamedata - contains all information required for renaming
@@ -2026,7 +2027,7 @@ struct renamedata {
struct mnt_idmap *new_mnt_idmap;
struct dentry *new_parent;
struct dentry *new_dentry;
- struct inode **delegated_inode;
+ struct delegated_inode *delegated_inode;
unsigned int flags;
} __randomize_layout;
@@ -3069,7 +3070,7 @@ static inline int bmap(struct inode *inode, sector_t *block)
#endif
int notify_change(struct mnt_idmap *, struct dentry *,
- struct iattr *, struct inode **);
+ struct iattr *, struct delegated_inode *);
int inode_permission(struct mnt_idmap *, struct inode *, int);
int generic_permission(struct mnt_idmap *, struct inode *, int);
static inline int file_permission(struct file *file, int mask)
diff --git a/include/linux/xattr.h b/include/linux/xattr.h
index 86b0d47984a16d935dd1c45ca80a3b8bb5b7295b..64e9afe7d647dc38f686a4b5c6f765e061cde54c 100644
--- a/include/linux/xattr.h
+++ b/include/linux/xattr.h
@@ -85,12 +85,12 @@ int __vfs_setxattr_noperm(struct mnt_idmap *, struct dentry *,
const char *, const void *, size_t, int);
int __vfs_setxattr_locked(struct mnt_idmap *, struct dentry *,
const char *, const void *, size_t, int,
- struct inode **);
+ struct delegated_inode *);
int vfs_setxattr(struct mnt_idmap *, struct dentry *, const char *,
const void *, size_t, int);
int __vfs_removexattr(struct mnt_idmap *, struct dentry *, const char *);
int __vfs_removexattr_locked(struct mnt_idmap *, struct dentry *,
- const char *, struct inode **);
+ const char *, struct delegated_inode *);
int vfs_removexattr(struct mnt_idmap *, struct dentry *, const char *);
ssize_t generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size);
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 16/38] filelock: add support for ignoring deleg breaks for dir change events
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (14 preceding siblings ...)
2025-09-24 18:06 ` [PATCH v3 15/38] filelock: add struct delegated_inode Jeff Layton
@ 2025-09-24 18:06 ` Jeff Layton
2025-09-24 18:06 ` [PATCH v3 17/38] filelock: add a tracepoint to start of break_lease() Jeff Layton
` (22 subsequent siblings)
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:06 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
If a NFS client requests a directory delegation with a notification
bitmask covering directory change events, the server shouldn't recall
the delegation. Instead the client will be notified of the change after
the fact.
Add a support for ignoring lease breaks on directory changes.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/locks.c | 56 ++++++++++++++++++++++++++++++++++-------
include/linux/filelock.h | 29 +++++++++++----------
include/trace/events/filelock.h | 5 +++-
3 files changed, 67 insertions(+), 23 deletions(-)
diff --git a/fs/locks.c b/fs/locks.c
index 4cfa4fc7130137b2850cab871bc3b2b23bbd3db1..ff5f5a85680a2c8511f5828947e829c7ffc1bd59 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -1526,15 +1526,52 @@ any_leases_conflict(struct inode *inode, struct file_lease *breaker)
return false;
}
+static bool
+ignore_dir_deleg_break(struct file_lease *fl, unsigned int flags)
+{
+ if ((flags & LEASE_BREAK_DIR_CREATE) && (fl->c.flc_flags & FL_IGN_DIR_CREATE))
+ return true;
+ if ((flags & LEASE_BREAK_DIR_DELETE) && (fl->c.flc_flags & FL_IGN_DIR_DELETE))
+ return true;
+ if ((flags & LEASE_BREAK_DIR_RENAME) && (fl->c.flc_flags & FL_IGN_DIR_RENAME))
+ return true;
+
+ return false;
+}
+
+static bool
+visible_leases_remaining(struct inode *inode, unsigned int flags)
+{
+ struct file_lock_context *ctx = locks_inode_context(inode);
+ struct file_lease *fl;
+
+ lockdep_assert_held(&ctx->flc_lock);
+
+ if (list_empty(&ctx->flc_lease))
+ return false;
+
+ if (!S_ISDIR(inode->i_mode))
+ return true;
+
+ list_for_each_entry(fl, &ctx->flc_lease, c.flc_list) {
+ if (!ignore_dir_deleg_break(fl, flags))
+ return true;
+ }
+ return false;
+}
+
/**
- * __break_lease - revoke all outstanding leases on file
- * @inode: the inode of the file to return
- * @flags: LEASE_BREAK_* flags
+ * __break_lease - revoke all outstanding leases on file
+ * @inode: the inode of the file to return
+ * @flags: LEASE_BREAK_* flags
*
- * break_lease (inlined for speed) has checked there already is at least
- * some kind of lock (maybe a lease) on this file. Leases are broken on
- * a call to open() or truncate(). This function can block waiting for the
- * lease break unless you specify LEASE_BREAK_NONBLOCK.
+ * break_lease (inlined for speed) has checked there already is at least
+ * some kind of lock (maybe a lease) on this file. Leases and Delegations
+ * are broken on a call to open() or truncate(). Delegations are also
+ * broken on any event that would change the ctime. Directory delegations
+ * are broken whenever the directory changes (unless the delegation is set
+ * up to ignore the event). This function can block waiting for the lease
+ * break unless you specify LEASE_BREAK_NONBLOCK.
*/
int __break_lease(struct inode *inode, unsigned int flags)
{
@@ -1545,7 +1582,6 @@ int __break_lease(struct inode *inode, unsigned int flags)
bool want_write = !(flags & LEASE_BREAK_OPEN_RDONLY);
int error = 0;
-
new_fl = lease_alloc(NULL, want_write ? F_WRLCK : F_RDLCK);
if (IS_ERR(new_fl))
return PTR_ERR(new_fl);
@@ -1584,6 +1620,8 @@ int __break_lease(struct inode *inode, unsigned int flags)
list_for_each_entry_safe(fl, tmp, &ctx->flc_lease, c.flc_list) {
if (!leases_conflict(&fl->c, &new_fl->c))
continue;
+ if (S_ISDIR(inode->i_mode) && ignore_dir_deleg_break(fl, flags))
+ continue;
if (want_write) {
if (fl->c.flc_flags & FL_UNLOCK_PENDING)
continue;
@@ -1599,7 +1637,7 @@ int __break_lease(struct inode *inode, unsigned int flags)
locks_delete_lock_ctx(&fl->c, &dispose);
}
- if (list_empty(&ctx->flc_lease))
+ if (!visible_leases_remaining(inode, flags))
goto out;
if (flags & LEASE_BREAK_NONBLOCK) {
diff --git a/include/linux/filelock.h b/include/linux/filelock.h
index 68901da7671694f68d939085d08e0da21712540d..0f0e56490c7ba4242c1caa79c68ab1db459609d8 100644
--- a/include/linux/filelock.h
+++ b/include/linux/filelock.h
@@ -4,19 +4,22 @@
#include <linux/fs.h>
-#define FL_POSIX 1
-#define FL_FLOCK 2
-#define FL_DELEG 4 /* NFSv4 delegation */
-#define FL_ACCESS 8 /* not trying to lock, just looking */
-#define FL_EXISTS 16 /* when unlocking, test for existence */
-#define FL_LEASE 32 /* lease held on this file */
-#define FL_CLOSE 64 /* unlock on close */
-#define FL_SLEEP 128 /* A blocking lock */
-#define FL_DOWNGRADE_PENDING 256 /* Lease is being downgraded */
-#define FL_UNLOCK_PENDING 512 /* Lease is being broken */
-#define FL_OFDLCK 1024 /* lock is "owned" by struct file */
-#define FL_LAYOUT 2048 /* outstanding pNFS layout */
-#define FL_RECLAIM 4096 /* reclaiming from a reboot server */
+#define FL_POSIX BIT(0) /* POSIX lock */
+#define FL_FLOCK BIT(1) /* BSD lock */
+#define FL_LEASE BIT(2) /* file lease */
+#define FL_DELEG BIT(3) /* NFSv4 delegation */
+#define FL_LAYOUT BIT(4) /* outstanding pNFS layout */
+#define FL_ACCESS BIT(5) /* not trying to lock, just looking */
+#define FL_EXISTS BIT(6) /* when unlocking, test for existence */
+#define FL_CLOSE BIT(7) /* unlock on close */
+#define FL_SLEEP BIT(8) /* A blocking lock */
+#define FL_DOWNGRADE_PENDING BIT(9) /* Lease is being downgraded */
+#define FL_UNLOCK_PENDING BIT(10) /* Lease is being broken */
+#define FL_OFDLCK BIT(11) /* POSIX lock "owned" by struct file */
+#define FL_RECLAIM BIT(12) /* reclaiming from a reboot server */
+#define FL_IGN_DIR_CREATE BIT(13) /* ignore DIR_CREATE events */
+#define FL_IGN_DIR_DELETE BIT(14) /* ignore DIR_DELETE events */
+#define FL_IGN_DIR_RENAME BIT(15) /* ignore DIR_RENAME events */
#define FL_CLOSE_POSIX (FL_POSIX | FL_CLOSE)
diff --git a/include/trace/events/filelock.h b/include/trace/events/filelock.h
index 2dfeb158e848a528b7d9c0d5f8872c5060df1bf6..4988804908478912c6b8044dfb3b147fc50e4823 100644
--- a/include/trace/events/filelock.h
+++ b/include/trace/events/filelock.h
@@ -28,7 +28,10 @@
{ FL_DOWNGRADE_PENDING, "FL_DOWNGRADE_PENDING" }, \
{ FL_UNLOCK_PENDING, "FL_UNLOCK_PENDING" }, \
{ FL_OFDLCK, "FL_OFDLCK" }, \
- { FL_RECLAIM, "FL_RECLAIM"})
+ { FL_RECLAIM, "FL_RECLAIM" }, \
+ { FL_IGN_DIR_CREATE, "FL_IGN_DIR_CREATE" }, \
+ { FL_IGN_DIR_DELETE, "FL_IGN_DIR_DELETE" }, \
+ { FL_IGN_DIR_RENAME, "FL_IGN_DIR_RENAME" })
#define show_fl_type(val) \
__print_symbolic(val, \
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 17/38] filelock: add a tracepoint to start of break_lease()
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (15 preceding siblings ...)
2025-09-24 18:06 ` [PATCH v3 16/38] filelock: add support for ignoring deleg breaks for dir change events Jeff Layton
@ 2025-09-24 18:06 ` Jeff Layton
2025-09-24 18:06 ` [PATCH v3 18/38] filelock: add an inode_lease_ignore_mask helper Jeff Layton
` (21 subsequent siblings)
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:06 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
...mostly to show the LEASE_BREAK_* flags.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/locks.c | 2 ++
include/trace/events/filelock.h | 33 +++++++++++++++++++++++++++++++++
2 files changed, 35 insertions(+)
diff --git a/fs/locks.c b/fs/locks.c
index ff5f5a85680a2c8511f5828947e829c7ffc1bd59..d26372ea890cbcd1a47209adeca6778ec23449ab 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -1582,6 +1582,8 @@ int __break_lease(struct inode *inode, unsigned int flags)
bool want_write = !(flags & LEASE_BREAK_OPEN_RDONLY);
int error = 0;
+ trace_break_lease(inode, flags);
+
new_fl = lease_alloc(NULL, want_write ? F_WRLCK : F_RDLCK);
if (IS_ERR(new_fl))
return PTR_ERR(new_fl);
diff --git a/include/trace/events/filelock.h b/include/trace/events/filelock.h
index 4988804908478912c6b8044dfb3b147fc50e4823..2d45cfad18fa7b2139fb75c1aa327c00521ed9f9 100644
--- a/include/trace/events/filelock.h
+++ b/include/trace/events/filelock.h
@@ -120,6 +120,39 @@ DEFINE_EVENT(filelock_lock, flock_lock_inode,
TP_PROTO(struct inode *inode, struct file_lock *fl, int ret),
TP_ARGS(inode, fl, ret));
+#define show_lease_break_flags(val) \
+ __print_flags(val, "|", \
+ { LEASE_BREAK_LEASE, "LEASE" }, \
+ { LEASE_BREAK_DELEG, "DELEG" }, \
+ { LEASE_BREAK_LAYOUT, "LAYOUT" }, \
+ { LEASE_BREAK_NONBLOCK, "NONBLOCK" }, \
+ { LEASE_BREAK_OPEN_RDONLY, "OPEN_RDONLY" }, \
+ { LEASE_BREAK_DIR_CREATE, "DIR_CREATE" }, \
+ { LEASE_BREAK_DIR_DELETE, "DIR_DELETE" }, \
+ { LEASE_BREAK_DIR_RENAME, "DIR_RENAME" })
+
+TRACE_EVENT(break_lease,
+ TP_PROTO(struct inode *inode, unsigned int flags),
+
+ TP_ARGS(inode, flags),
+
+ TP_STRUCT__entry(
+ __field(unsigned long, i_ino)
+ __field(dev_t, s_dev)
+ __field(unsigned int, flags)
+ ),
+
+ TP_fast_assign(
+ __entry->s_dev = inode->i_sb->s_dev;
+ __entry->i_ino = inode->i_ino;
+ __entry->flags = flags;
+ ),
+
+ TP_printk("dev=0x%x:0x%x ino=0x%lx flags=%s",
+ MAJOR(__entry->s_dev), MINOR(__entry->s_dev),
+ __entry->i_ino, show_lease_break_flags(flags))
+);
+
DECLARE_EVENT_CLASS(filelock_lease,
TP_PROTO(struct inode *inode, struct file_lease *fl),
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 18/38] filelock: add an inode_lease_ignore_mask helper
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (16 preceding siblings ...)
2025-09-24 18:06 ` [PATCH v3 17/38] filelock: add a tracepoint to start of break_lease() Jeff Layton
@ 2025-09-24 18:06 ` Jeff Layton
2025-09-24 18:06 ` [PATCH v3 19/38] nfsd: add protocol support for CB_NOTIFY Jeff Layton
` (20 subsequent siblings)
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:06 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
Add a new routine that returns a mask of all dir change events that are
currently ignored by any leases. nfsd will use this to determine how to
configure the fsnotify_mark mask.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/locks.c | 32 ++++++++++++++++++++++++++++++++
include/linux/filelock.h | 1 +
2 files changed, 33 insertions(+)
diff --git a/fs/locks.c b/fs/locks.c
index d26372ea890cbcd1a47209adeca6778ec23449ab..79d8991b5ec43ac006acb73182fc53d5aff5d42d 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -1526,6 +1526,38 @@ any_leases_conflict(struct inode *inode, struct file_lease *breaker)
return false;
}
+#define IGNORE_MASK (FL_IGN_DIR_CREATE | FL_IGN_DIR_DELETE | FL_IGN_DIR_RENAME)
+
+/**
+ * inode_lease_ignore_mask - return union of all ignored inode events for this inode
+ * @inode: inode of which to get ignore mask
+ *
+ * Walk the list of leases, and return the result of all of
+ * their FL_IGN_DIR_* bits or'ed together.
+ */
+u32
+inode_lease_ignore_mask(struct inode *inode)
+{
+ struct file_lock_context *ctx;
+ struct file_lock_core *flc;
+ u32 mask = 0;
+
+ ctx = locks_inode_context(inode);
+ if (!ctx)
+ return 0;
+
+ spin_lock(&ctx->flc_lock);
+ list_for_each_entry(flc, &ctx->flc_lease, flc_list) {
+ mask |= flc->flc_flags & IGNORE_MASK;
+ /* If we already have everything, we can stop */
+ if (mask == IGNORE_MASK)
+ break;
+ }
+ spin_unlock(&ctx->flc_lock);
+ return mask;
+}
+EXPORT_SYMBOL_GPL(inode_lease_ignore_mask);
+
static bool
ignore_dir_deleg_break(struct file_lease *fl, unsigned int flags)
{
diff --git a/include/linux/filelock.h b/include/linux/filelock.h
index 0f0e56490c7ba4242c1caa79c68ab1db459609d8..b35cb5dd85ff7e5dbb7712718613f4ddcd5839a5 100644
--- a/include/linux/filelock.h
+++ b/include/linux/filelock.h
@@ -249,6 +249,7 @@ int generic_setlease(struct file *, int, struct file_lease **, void **priv);
int kernel_setlease(struct file *, int, struct file_lease **, void **);
int vfs_setlease(struct file *, int, struct file_lease **, void **);
int lease_modify(struct file_lease *, int, struct list_head *);
+u32 inode_lease_ignore_mask(struct inode *inode);
struct notifier_block;
int lease_register_notifier(struct notifier_block *);
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 19/38] nfsd: add protocol support for CB_NOTIFY
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (17 preceding siblings ...)
2025-09-24 18:06 ` [PATCH v3 18/38] filelock: add an inode_lease_ignore_mask helper Jeff Layton
@ 2025-09-24 18:06 ` Jeff Layton
2025-09-24 18:06 ` [PATCH v3 20/38] nfs_common: add new NOTIFY4_* flags proposed in RFC8881bis Jeff Layton
` (19 subsequent siblings)
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:06 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
Add the necessary bits to nfs4_1.x and remove the duplicate definitions
from nfs4.h and the uapi nfs4 header. Regenerate the xdr files.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
Documentation/sunrpc/xdr/nfs4_1.x | 253 +++++++++++++++++-
fs/nfsd/nfs4xdr_gen.c | 506 ++++++++++++++++++++++++++++++++++-
fs/nfsd/nfs4xdr_gen.h | 20 +-
fs/nfsd/trace.h | 1 +
include/linux/nfs4.h | 127 ---------
include/linux/sunrpc/xdrgen/nfs4_1.h | 293 +++++++++++++++++++-
include/uapi/linux/nfs4.h | 2 -
7 files changed, 1061 insertions(+), 141 deletions(-)
diff --git a/Documentation/sunrpc/xdr/nfs4_1.x b/Documentation/sunrpc/xdr/nfs4_1.x
index ca95150a3a29fc5418991bf2395326bd73645ea8..9e00910c02e0aecfb0f86ff7b534049d2c588cf3 100644
--- a/Documentation/sunrpc/xdr/nfs4_1.x
+++ b/Documentation/sunrpc/xdr/nfs4_1.x
@@ -45,13 +45,162 @@ pragma header nfs4;
/*
* Basic typedefs for RFC 1832 data type definitions
*/
-typedef hyper int64_t;
-typedef unsigned int uint32_t;
+typedef int int32_t;
+typedef unsigned int uint32_t;
+typedef hyper int64_t;
+typedef unsigned hyper uint64_t;
+
+const NFS4_VERIFIER_SIZE = 8;
+const NFS4_FHSIZE = 128;
+
+enum nfsstat4 {
+ NFS4_OK = 0, /* everything is okay */
+ NFS4ERR_PERM = 1, /* caller not privileged */
+ NFS4ERR_NOENT = 2, /* no such file/directory */
+ NFS4ERR_IO = 5, /* hard I/O error */
+ NFS4ERR_NXIO = 6, /* no such device */
+ NFS4ERR_ACCESS = 13, /* access denied */
+ NFS4ERR_EXIST = 17, /* file already exists */
+ NFS4ERR_XDEV = 18, /* different filesystems */
+
+ /*
+ * Please do not allocate value 19; it was used in NFSv3
+ * and we do not want a value in NFSv3 to have a different
+ * meaning in NFSv4.x.
+ */
+
+ NFS4ERR_NOTDIR = 20, /* should be a directory */
+ NFS4ERR_ISDIR = 21, /* should not be directory */
+ NFS4ERR_INVAL = 22, /* invalid argument */
+ NFS4ERR_FBIG = 27, /* file exceeds server max */
+ NFS4ERR_NOSPC = 28, /* no space on filesystem */
+ NFS4ERR_ROFS = 30, /* read-only filesystem */
+ NFS4ERR_MLINK = 31, /* too many hard links */
+ NFS4ERR_NAMETOOLONG = 63, /* name exceeds server max */
+ NFS4ERR_NOTEMPTY = 66, /* directory not empty */
+ NFS4ERR_DQUOT = 69, /* hard quota limit reached*/
+ NFS4ERR_STALE = 70, /* file no longer exists */
+ NFS4ERR_BADHANDLE = 10001,/* Illegal filehandle */
+ NFS4ERR_BAD_COOKIE = 10003,/* READDIR cookie is stale */
+ NFS4ERR_NOTSUPP = 10004,/* operation not supported */
+ NFS4ERR_TOOSMALL = 10005,/* response limit exceeded */
+ NFS4ERR_SERVERFAULT = 10006,/* undefined server error */
+ NFS4ERR_BADTYPE = 10007,/* type invalid for CREATE */
+ NFS4ERR_DELAY = 10008,/* file "busy" - retry */
+ NFS4ERR_SAME = 10009,/* nverify says attrs same */
+ NFS4ERR_DENIED = 10010,/* lock unavailable */
+ NFS4ERR_EXPIRED = 10011,/* lock lease expired */
+ NFS4ERR_LOCKED = 10012,/* I/O failed due to lock */
+ NFS4ERR_GRACE = 10013,/* in grace period */
+ NFS4ERR_FHEXPIRED = 10014,/* filehandle expired */
+ NFS4ERR_SHARE_DENIED = 10015,/* share reserve denied */
+ NFS4ERR_WRONGSEC = 10016,/* wrong security flavor */
+ NFS4ERR_CLID_INUSE = 10017,/* clientid in use */
+
+ /* NFS4ERR_RESOURCE is not a valid error in NFSv4.1 */
+ NFS4ERR_RESOURCE = 10018,/* resource exhaustion */
+
+ NFS4ERR_MOVED = 10019,/* filesystem relocated */
+ NFS4ERR_NOFILEHANDLE = 10020,/* current FH is not set */
+ NFS4ERR_MINOR_VERS_MISMATCH= 10021,/* minor vers not supp */
+ NFS4ERR_STALE_CLIENTID = 10022,/* server has rebooted */
+ NFS4ERR_STALE_STATEID = 10023,/* server has rebooted */
+ NFS4ERR_OLD_STATEID = 10024,/* state is out of sync */
+ NFS4ERR_BAD_STATEID = 10025,/* incorrect stateid */
+ NFS4ERR_BAD_SEQID = 10026,/* request is out of seq. */
+ NFS4ERR_NOT_SAME = 10027,/* verify - attrs not same */
+ NFS4ERR_LOCK_RANGE = 10028,/* overlapping lock range */
+ NFS4ERR_SYMLINK = 10029,/* should be file/directory*/
+ NFS4ERR_RESTOREFH = 10030,/* no saved filehandle */
+ NFS4ERR_LEASE_MOVED = 10031,/* some filesystem moved */
+ NFS4ERR_ATTRNOTSUPP = 10032,/* recommended attr not sup*/
+ NFS4ERR_NO_GRACE = 10033,/* reclaim outside of grace*/
+ NFS4ERR_RECLAIM_BAD = 10034,/* reclaim error at server */
+ NFS4ERR_RECLAIM_CONFLICT= 10035,/* conflict on reclaim */
+ NFS4ERR_BADXDR = 10036,/* XDR decode failed */
+ NFS4ERR_LOCKS_HELD = 10037,/* file locks held at CLOSE*/
+ NFS4ERR_OPENMODE = 10038,/* conflict in OPEN and I/O*/
+ NFS4ERR_BADOWNER = 10039,/* owner translation bad */
+ NFS4ERR_BADCHAR = 10040,/* utf-8 char not supported*/
+ NFS4ERR_BADNAME = 10041,/* name not supported */
+ NFS4ERR_BAD_RANGE = 10042,/* lock range not supported*/
+ NFS4ERR_LOCK_NOTSUPP = 10043,/* no atomic up/downgrade */
+ NFS4ERR_OP_ILLEGAL = 10044,/* undefined operation */
+ NFS4ERR_DEADLOCK = 10045,/* file locking deadlock */
+ NFS4ERR_FILE_OPEN = 10046,/* open file blocks op. */
+ NFS4ERR_ADMIN_REVOKED = 10047,/* lockowner state revoked */
+ NFS4ERR_CB_PATH_DOWN = 10048,/* callback path down */
+
+ /* NFSv4.1 errors start here. */
+
+ NFS4ERR_BADIOMODE = 10049,
+ NFS4ERR_BADLAYOUT = 10050,
+ NFS4ERR_BAD_SESSION_DIGEST = 10051,
+ NFS4ERR_BADSESSION = 10052,
+ NFS4ERR_BADSLOT = 10053,
+ NFS4ERR_COMPLETE_ALREADY = 10054,
+ NFS4ERR_CONN_NOT_BOUND_TO_SESSION = 10055,
+ NFS4ERR_DELEG_ALREADY_WANTED = 10056,
+ NFS4ERR_BACK_CHAN_BUSY = 10057,/*backchan reqs outstanding*/
+ NFS4ERR_LAYOUTTRYLATER = 10058,
+ NFS4ERR_LAYOUTUNAVAILABLE = 10059,
+ NFS4ERR_NOMATCHING_LAYOUT = 10060,
+ NFS4ERR_RECALLCONFLICT = 10061,
+ NFS4ERR_UNKNOWN_LAYOUTTYPE = 10062,
+ NFS4ERR_SEQ_MISORDERED = 10063,/* unexpected seq.ID in req*/
+ NFS4ERR_SEQUENCE_POS = 10064,/* [CB_]SEQ. op not 1st op */
+ NFS4ERR_REQ_TOO_BIG = 10065,/* request too big */
+ NFS4ERR_REP_TOO_BIG = 10066,/* reply too big */
+ NFS4ERR_REP_TOO_BIG_TO_CACHE =10067,/* rep. not all cached*/
+ NFS4ERR_RETRY_UNCACHED_REP =10068,/* retry & rep. uncached*/
+ NFS4ERR_UNSAFE_COMPOUND =10069,/* retry/recovery too hard */
+ NFS4ERR_TOO_MANY_OPS = 10070,/*too many ops in [CB_]COMP*/
+ NFS4ERR_OP_NOT_IN_SESSION =10071,/* op needs [CB_]SEQ. op */
+ NFS4ERR_HASH_ALG_UNSUPP = 10072, /* hash alg. not supp. */
+ /* Error 10073 is unused. */
+ NFS4ERR_CLIENTID_BUSY = 10074,/* clientid has state */
+ NFS4ERR_PNFS_IO_HOLE = 10075,/* IO to _SPARSE file hole */
+ NFS4ERR_SEQ_FALSE_RETRY= 10076,/* Retry != original req. */
+ NFS4ERR_BAD_HIGH_SLOT = 10077,/* req has bad highest_slot*/
+ NFS4ERR_DEADSESSION = 10078,/*new req sent to dead sess*/
+ NFS4ERR_ENCR_ALG_UNSUPP= 10079,/* encr alg. not supp. */
+ NFS4ERR_PNFS_NO_LAYOUT = 10080,/* I/O without a layout */
+ NFS4ERR_NOT_ONLY_OP = 10081,/* addl ops not allowed */
+ NFS4ERR_WRONG_CRED = 10082,/* op done by wrong cred */
+ NFS4ERR_WRONG_TYPE = 10083,/* op on wrong type object */
+ NFS4ERR_DIRDELEG_UNAVAIL=10084,/* delegation not avail. */
+ NFS4ERR_REJECT_DELEG = 10085,/* cb rejected delegation */
+ NFS4ERR_RETURNCONFLICT = 10086,/* layout get before return*/
+ NFS4ERR_DELEG_REVOKED = 10087, /* deleg./layout revoked */
+ NFS4ERR_PARTNER_NOTSUPP = 10088,
+ NFS4ERR_PARTNER_NO_AUTH = 10089,
+ NFS4ERR_UNION_NOTSUPP = 10090,
+ NFS4ERR_OFFLOAD_DENIED = 10091,
+ NFS4ERR_WRONG_LFS = 10092,
+ NFS4ERR_BADLABEL = 10093,
+ NFS4ERR_OFFLOAD_NO_REQS = 10094,
+ NFS4ERR_NOXATTR = 10095,
+ NFS4ERR_XATTR2BIG = 10096,
+
+ /* always set this to one more than the last one in the enum */
+ NFS4ERR_FIRST_FREE = 10097
+};
/*
* Basic data types
*/
+typedef opaque attrlist4<>;
typedef uint32_t bitmap4<>;
+typedef uint64_t nfs_cookie4;
+typedef opaque nfs_fh4<NFS4_FHSIZE>;
+typedef opaque utf8string<>;
+typedef utf8string utf8str_cis;
+typedef utf8string utf8str_cs;
+typedef utf8string utf8str_mixed;
+typedef utf8str_cs component4;
+typedef utf8str_cs linktext4;
+typedef component4 pathname4<>;
+typedef opaque verifier4[NFS4_VERIFIER_SIZE];
/*
* Timeval
@@ -61,6 +210,21 @@ struct nfstime4 {
uint32_t nseconds;
};
+/*
+ * File attribute container
+ */
+struct fattr4 {
+ bitmap4 attrmask;
+ attrlist4 attr_vals;
+};
+
+/*
+ * Stateid
+ */
+struct stateid4 {
+ uint32_t seqid;
+ opaque other[12];
+};
/*
* The following content was extracted from draft-ietf-nfsv4-delstid
@@ -184,3 +348,88 @@ enum open_delegation_type4 {
OPEN_DELEGATE_READ_ATTRS_DELEG = 4,
OPEN_DELEGATE_WRITE_ATTRS_DELEG = 5
};
+
+/*
+ * Directory notification types.
+ */
+enum notify_type4 {
+ NOTIFY4_CHANGE_CHILD_ATTRS = 0,
+ NOTIFY4_CHANGE_DIR_ATTRS = 1,
+ NOTIFY4_REMOVE_ENTRY = 2,
+ NOTIFY4_ADD_ENTRY = 3,
+ NOTIFY4_RENAME_ENTRY = 4,
+ NOTIFY4_CHANGE_COOKIE_VERIFIER = 5
+};
+
+/* Changed entry information. */
+struct notify_entry4 {
+ component4 ne_file;
+ fattr4 ne_attrs;
+};
+
+/* Previous entry information */
+struct prev_entry4 {
+ notify_entry4 pe_prev_entry;
+ /* what READDIR returned for this entry */
+ nfs_cookie4 pe_prev_entry_cookie;
+};
+
+struct notify_remove4 {
+ notify_entry4 nrm_old_entry;
+ nfs_cookie4 nrm_old_entry_cookie;
+};
+pragma public notify_remove4;
+
+struct notify_add4 {
+ /*
+ * Information on object
+ * possibly renamed over.
+ */
+ notify_remove4 nad_old_entry<1>;
+ notify_entry4 nad_new_entry;
+ /* what READDIR would have returned for this entry */
+ nfs_cookie4 nad_new_entry_cookie<1>;
+ prev_entry4 nad_prev_entry<1>;
+ bool nad_last_entry;
+};
+pragma public notify_add4;
+
+struct notify_attr4 {
+ notify_entry4 na_changed_entry;
+};
+pragma public notify_attr4;
+
+struct notify_rename4 {
+ notify_remove4 nrn_old_entry;
+ notify_add4 nrn_new_entry;
+};
+pragma public notify_rename4;
+
+struct notify_verifier4 {
+ verifier4 nv_old_cookieverf;
+ verifier4 nv_new_cookieverf;
+};
+
+/*
+ * Objects of type notify_<>4 and
+ * notify_device_<>4 are encoded in this.
+ */
+typedef opaque notifylist4<>;
+
+struct notify4 {
+ /* composed from notify_type4 or notify_deviceid_type4 */
+ bitmap4 notify_mask;
+ notifylist4 notify_vals;
+};
+
+struct CB_NOTIFY4args {
+ stateid4 cna_stateid;
+ nfs_fh4 cna_fh;
+ notify4 cna_changes<>;
+};
+pragma public CB_NOTIFY4args;
+
+struct CB_NOTIFY4res {
+ nfsstat4 cnr_status;
+};
+pragma public CB_NOTIFY4res;
diff --git a/fs/nfsd/nfs4xdr_gen.c b/fs/nfsd/nfs4xdr_gen.c
index a17b5d8e60b3579caa2e2a8b40ed757070e1a622..306a4c30c3a4e6b9066c5ad41c1f0f7ffbb27bae 100644
--- a/fs/nfsd/nfs4xdr_gen.c
+++ b/fs/nfsd/nfs4xdr_gen.c
@@ -1,16 +1,16 @@
// SPDX-License-Identifier: GPL-2.0
// Generated by xdrgen. Manual edits will be lost.
// XDR specification file: ../../Documentation/sunrpc/xdr/nfs4_1.x
-// XDR specification modification time: Mon Oct 14 09:10:13 2024
+// XDR specification modification time: Wed Sep 24 09:38:03 2025
#include <linux/sunrpc/svc.h>
#include "nfs4xdr_gen.h"
static bool __maybe_unused
-xdrgen_decode_int64_t(struct xdr_stream *xdr, int64_t *ptr)
+xdrgen_decode_int32_t(struct xdr_stream *xdr, int32_t *ptr)
{
- return xdrgen_decode_hyper(xdr, ptr);
+ return xdrgen_decode_int(xdr, ptr);
};
static bool __maybe_unused
@@ -19,6 +19,35 @@ xdrgen_decode_uint32_t(struct xdr_stream *xdr, uint32_t *ptr)
return xdrgen_decode_unsigned_int(xdr, ptr);
};
+static bool __maybe_unused
+xdrgen_decode_int64_t(struct xdr_stream *xdr, int64_t *ptr)
+{
+ return xdrgen_decode_hyper(xdr, ptr);
+};
+
+static bool __maybe_unused
+xdrgen_decode_uint64_t(struct xdr_stream *xdr, uint64_t *ptr)
+{
+ return xdrgen_decode_unsigned_hyper(xdr, ptr);
+};
+
+static bool __maybe_unused
+xdrgen_decode_nfsstat4(struct xdr_stream *xdr, nfsstat4 *ptr)
+{
+ u32 val;
+
+ if (xdr_stream_decode_u32(xdr, &val) < 0)
+ return false;
+ *ptr = val;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_decode_attrlist4(struct xdr_stream *xdr, attrlist4 *ptr)
+{
+ return xdrgen_decode_opaque(xdr, ptr, 0);
+};
+
static bool __maybe_unused
xdrgen_decode_bitmap4(struct xdr_stream *xdr, bitmap4 *ptr)
{
@@ -30,6 +59,71 @@ xdrgen_decode_bitmap4(struct xdr_stream *xdr, bitmap4 *ptr)
return true;
};
+static bool __maybe_unused
+xdrgen_decode_nfs_cookie4(struct xdr_stream *xdr, nfs_cookie4 *ptr)
+{
+ return xdrgen_decode_uint64_t(xdr, ptr);
+};
+
+static bool __maybe_unused
+xdrgen_decode_nfs_fh4(struct xdr_stream *xdr, nfs_fh4 *ptr)
+{
+ return xdrgen_decode_opaque(xdr, ptr, NFS4_FHSIZE);
+};
+
+static bool __maybe_unused
+xdrgen_decode_utf8string(struct xdr_stream *xdr, utf8string *ptr)
+{
+ return xdrgen_decode_opaque(xdr, ptr, 0);
+};
+
+static bool __maybe_unused
+xdrgen_decode_utf8str_cis(struct xdr_stream *xdr, utf8str_cis *ptr)
+{
+ return xdrgen_decode_utf8string(xdr, ptr);
+};
+
+static bool __maybe_unused
+xdrgen_decode_utf8str_cs(struct xdr_stream *xdr, utf8str_cs *ptr)
+{
+ return xdrgen_decode_utf8string(xdr, ptr);
+};
+
+static bool __maybe_unused
+xdrgen_decode_utf8str_mixed(struct xdr_stream *xdr, utf8str_mixed *ptr)
+{
+ return xdrgen_decode_utf8string(xdr, ptr);
+};
+
+static bool __maybe_unused
+xdrgen_decode_component4(struct xdr_stream *xdr, component4 *ptr)
+{
+ return xdrgen_decode_utf8str_cs(xdr, ptr);
+};
+
+static bool __maybe_unused
+xdrgen_decode_linktext4(struct xdr_stream *xdr, linktext4 *ptr)
+{
+ return xdrgen_decode_utf8str_cs(xdr, ptr);
+};
+
+static bool __maybe_unused
+xdrgen_decode_pathname4(struct xdr_stream *xdr, pathname4 *ptr)
+{
+ if (xdr_stream_decode_u32(xdr, &ptr->count) < 0)
+ return false;
+ for (u32 i = 0; i < ptr->count; i++)
+ if (!xdrgen_decode_component4(xdr, &ptr->element[i]))
+ return false;
+ return true;
+};
+
+static bool __maybe_unused
+xdrgen_decode_verifier4(struct xdr_stream *xdr, verifier4 *ptr)
+{
+ return xdr_stream_decode_opaque_fixed(xdr, ptr, NFS4_VERIFIER_SIZE) == 0;
+};
+
static bool __maybe_unused
xdrgen_decode_nfstime4(struct xdr_stream *xdr, struct nfstime4 *ptr)
{
@@ -40,6 +134,26 @@ xdrgen_decode_nfstime4(struct xdr_stream *xdr, struct nfstime4 *ptr)
return true;
};
+static bool __maybe_unused
+xdrgen_decode_fattr4(struct xdr_stream *xdr, struct fattr4 *ptr)
+{
+ if (!xdrgen_decode_bitmap4(xdr, &ptr->attrmask))
+ return false;
+ if (!xdrgen_decode_attrlist4(xdr, &ptr->attr_vals))
+ return false;
+ return true;
+};
+
+static bool __maybe_unused
+xdrgen_decode_stateid4(struct xdr_stream *xdr, struct stateid4 *ptr)
+{
+ if (!xdrgen_decode_uint32_t(xdr, &ptr->seqid))
+ return false;
+ if (xdr_stream_decode_opaque_fixed(xdr, ptr->other, 12) < 0)
+ return false;
+ return true;
+};
+
static bool __maybe_unused
xdrgen_decode_fattr4_offline(struct xdr_stream *xdr, fattr4_offline *ptr)
{
@@ -147,9 +261,148 @@ xdrgen_decode_open_delegation_type4(struct xdr_stream *xdr, open_delegation_type
}
static bool __maybe_unused
-xdrgen_encode_int64_t(struct xdr_stream *xdr, const int64_t value)
+xdrgen_decode_notify_type4(struct xdr_stream *xdr, notify_type4 *ptr)
{
- return xdrgen_encode_hyper(xdr, value);
+ u32 val;
+
+ if (xdr_stream_decode_u32(xdr, &val) < 0)
+ return false;
+ *ptr = val;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_decode_notify_entry4(struct xdr_stream *xdr, struct notify_entry4 *ptr)
+{
+ if (!xdrgen_decode_component4(xdr, &ptr->ne_file))
+ return false;
+ if (!xdrgen_decode_fattr4(xdr, &ptr->ne_attrs))
+ return false;
+ return true;
+};
+
+static bool __maybe_unused
+xdrgen_decode_prev_entry4(struct xdr_stream *xdr, struct prev_entry4 *ptr)
+{
+ if (!xdrgen_decode_notify_entry4(xdr, &ptr->pe_prev_entry))
+ return false;
+ if (!xdrgen_decode_nfs_cookie4(xdr, &ptr->pe_prev_entry_cookie))
+ return false;
+ return true;
+};
+
+bool
+xdrgen_decode_notify_remove4(struct xdr_stream *xdr, struct notify_remove4 *ptr)
+{
+ if (!xdrgen_decode_notify_entry4(xdr, &ptr->nrm_old_entry))
+ return false;
+ if (!xdrgen_decode_nfs_cookie4(xdr, &ptr->nrm_old_entry_cookie))
+ return false;
+ return true;
+};
+
+bool
+xdrgen_decode_notify_add4(struct xdr_stream *xdr, struct notify_add4 *ptr)
+{
+ if (xdr_stream_decode_u32(xdr, &ptr->nad_old_entry.count) < 0)
+ return false;
+ if (ptr->nad_old_entry.count > 1)
+ return false;
+ for (u32 i = 0; i < ptr->nad_old_entry.count; i++)
+ if (!xdrgen_decode_notify_remove4(xdr, &ptr->nad_old_entry.element[i]))
+ return false;
+ if (!xdrgen_decode_notify_entry4(xdr, &ptr->nad_new_entry))
+ return false;
+ if (xdr_stream_decode_u32(xdr, &ptr->nad_new_entry_cookie.count) < 0)
+ return false;
+ if (ptr->nad_new_entry_cookie.count > 1)
+ return false;
+ for (u32 i = 0; i < ptr->nad_new_entry_cookie.count; i++)
+ if (!xdrgen_decode_nfs_cookie4(xdr, &ptr->nad_new_entry_cookie.element[i]))
+ return false;
+ if (xdr_stream_decode_u32(xdr, &ptr->nad_prev_entry.count) < 0)
+ return false;
+ if (ptr->nad_prev_entry.count > 1)
+ return false;
+ for (u32 i = 0; i < ptr->nad_prev_entry.count; i++)
+ if (!xdrgen_decode_prev_entry4(xdr, &ptr->nad_prev_entry.element[i]))
+ return false;
+ if (!xdrgen_decode_bool(xdr, &ptr->nad_last_entry))
+ return false;
+ return true;
+};
+
+bool
+xdrgen_decode_notify_attr4(struct xdr_stream *xdr, struct notify_attr4 *ptr)
+{
+ if (!xdrgen_decode_notify_entry4(xdr, &ptr->na_changed_entry))
+ return false;
+ return true;
+};
+
+bool
+xdrgen_decode_notify_rename4(struct xdr_stream *xdr, struct notify_rename4 *ptr)
+{
+ if (!xdrgen_decode_notify_remove4(xdr, &ptr->nrn_old_entry))
+ return false;
+ if (!xdrgen_decode_notify_add4(xdr, &ptr->nrn_new_entry))
+ return false;
+ return true;
+};
+
+static bool __maybe_unused
+xdrgen_decode_notify_verifier4(struct xdr_stream *xdr, struct notify_verifier4 *ptr)
+{
+ if (!xdrgen_decode_verifier4(xdr, &ptr->nv_old_cookieverf))
+ return false;
+ if (!xdrgen_decode_verifier4(xdr, &ptr->nv_new_cookieverf))
+ return false;
+ return true;
+};
+
+static bool __maybe_unused
+xdrgen_decode_notifylist4(struct xdr_stream *xdr, notifylist4 *ptr)
+{
+ return xdrgen_decode_opaque(xdr, ptr, 0);
+};
+
+static bool __maybe_unused
+xdrgen_decode_notify4(struct xdr_stream *xdr, struct notify4 *ptr)
+{
+ if (!xdrgen_decode_bitmap4(xdr, &ptr->notify_mask))
+ return false;
+ if (!xdrgen_decode_notifylist4(xdr, &ptr->notify_vals))
+ return false;
+ return true;
+};
+
+bool
+xdrgen_decode_CB_NOTIFY4args(struct xdr_stream *xdr, struct CB_NOTIFY4args *ptr)
+{
+ if (!xdrgen_decode_stateid4(xdr, &ptr->cna_stateid))
+ return false;
+ if (!xdrgen_decode_nfs_fh4(xdr, &ptr->cna_fh))
+ return false;
+ if (xdr_stream_decode_u32(xdr, &ptr->cna_changes.count) < 0)
+ return false;
+ for (u32 i = 0; i < ptr->cna_changes.count; i++)
+ if (!xdrgen_decode_notify4(xdr, &ptr->cna_changes.element[i]))
+ return false;
+ return true;
+};
+
+bool
+xdrgen_decode_CB_NOTIFY4res(struct xdr_stream *xdr, struct CB_NOTIFY4res *ptr)
+{
+ if (!xdrgen_decode_nfsstat4(xdr, &ptr->cnr_status))
+ return false;
+ return true;
+};
+
+static bool __maybe_unused
+xdrgen_encode_int32_t(struct xdr_stream *xdr, const int32_t value)
+{
+ return xdrgen_encode_int(xdr, value);
};
static bool __maybe_unused
@@ -158,6 +411,30 @@ xdrgen_encode_uint32_t(struct xdr_stream *xdr, const uint32_t value)
return xdrgen_encode_unsigned_int(xdr, value);
};
+static bool __maybe_unused
+xdrgen_encode_int64_t(struct xdr_stream *xdr, const int64_t value)
+{
+ return xdrgen_encode_hyper(xdr, value);
+};
+
+static bool __maybe_unused
+xdrgen_encode_uint64_t(struct xdr_stream *xdr, const uint64_t value)
+{
+ return xdrgen_encode_unsigned_hyper(xdr, value);
+};
+
+static bool __maybe_unused
+xdrgen_encode_nfsstat4(struct xdr_stream *xdr, nfsstat4 value)
+{
+ return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
+}
+
+static bool __maybe_unused
+xdrgen_encode_attrlist4(struct xdr_stream *xdr, const attrlist4 value)
+{
+ return xdr_stream_encode_opaque(xdr, value.data, value.len) >= 0;
+};
+
static bool __maybe_unused
xdrgen_encode_bitmap4(struct xdr_stream *xdr, const bitmap4 value)
{
@@ -169,6 +446,71 @@ xdrgen_encode_bitmap4(struct xdr_stream *xdr, const bitmap4 value)
return true;
};
+static bool __maybe_unused
+xdrgen_encode_nfs_cookie4(struct xdr_stream *xdr, const nfs_cookie4 value)
+{
+ return xdrgen_encode_uint64_t(xdr, value);
+};
+
+static bool __maybe_unused
+xdrgen_encode_nfs_fh4(struct xdr_stream *xdr, const nfs_fh4 value)
+{
+ return xdr_stream_encode_opaque(xdr, value.data, value.len) >= 0;
+};
+
+static bool __maybe_unused
+xdrgen_encode_utf8string(struct xdr_stream *xdr, const utf8string value)
+{
+ return xdr_stream_encode_opaque(xdr, value.data, value.len) >= 0;
+};
+
+static bool __maybe_unused
+xdrgen_encode_utf8str_cis(struct xdr_stream *xdr, const utf8str_cis value)
+{
+ return xdrgen_encode_utf8string(xdr, value);
+};
+
+static bool __maybe_unused
+xdrgen_encode_utf8str_cs(struct xdr_stream *xdr, const utf8str_cs value)
+{
+ return xdrgen_encode_utf8string(xdr, value);
+};
+
+static bool __maybe_unused
+xdrgen_encode_utf8str_mixed(struct xdr_stream *xdr, const utf8str_mixed value)
+{
+ return xdrgen_encode_utf8string(xdr, value);
+};
+
+static bool __maybe_unused
+xdrgen_encode_component4(struct xdr_stream *xdr, const component4 value)
+{
+ return xdrgen_encode_utf8str_cs(xdr, value);
+};
+
+static bool __maybe_unused
+xdrgen_encode_linktext4(struct xdr_stream *xdr, const linktext4 value)
+{
+ return xdrgen_encode_utf8str_cs(xdr, value);
+};
+
+static bool __maybe_unused
+xdrgen_encode_pathname4(struct xdr_stream *xdr, const pathname4 value)
+{
+ if (xdr_stream_encode_u32(xdr, value.count) != XDR_UNIT)
+ return false;
+ for (u32 i = 0; i < value.count; i++)
+ if (!xdrgen_encode_component4(xdr, value.element[i]))
+ return false;
+ return true;
+};
+
+static bool __maybe_unused
+xdrgen_encode_verifier4(struct xdr_stream *xdr, const verifier4 value)
+{
+ return xdr_stream_encode_opaque_fixed(xdr, value, NFS4_VERIFIER_SIZE) >= 0;
+};
+
static bool __maybe_unused
xdrgen_encode_nfstime4(struct xdr_stream *xdr, const struct nfstime4 *value)
{
@@ -179,6 +521,26 @@ xdrgen_encode_nfstime4(struct xdr_stream *xdr, const struct nfstime4 *value)
return true;
};
+static bool __maybe_unused
+xdrgen_encode_fattr4(struct xdr_stream *xdr, const struct fattr4 *value)
+{
+ if (!xdrgen_encode_bitmap4(xdr, value->attrmask))
+ return false;
+ if (!xdrgen_encode_attrlist4(xdr, value->attr_vals))
+ return false;
+ return true;
+};
+
+static bool __maybe_unused
+xdrgen_encode_stateid4(struct xdr_stream *xdr, const struct stateid4 *value)
+{
+ if (!xdrgen_encode_uint32_t(xdr, value->seqid))
+ return false;
+ if (xdr_stream_encode_opaque_fixed(xdr, value->other, 12) < 0)
+ return false;
+ return true;
+};
+
static bool __maybe_unused
xdrgen_encode_fattr4_offline(struct xdr_stream *xdr, const fattr4_offline value)
{
@@ -254,3 +616,137 @@ xdrgen_encode_open_delegation_type4(struct xdr_stream *xdr, open_delegation_type
{
return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
}
+
+static bool __maybe_unused
+xdrgen_encode_notify_type4(struct xdr_stream *xdr, notify_type4 value)
+{
+ return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
+}
+
+static bool __maybe_unused
+xdrgen_encode_notify_entry4(struct xdr_stream *xdr, const struct notify_entry4 *value)
+{
+ if (!xdrgen_encode_component4(xdr, value->ne_file))
+ return false;
+ if (!xdrgen_encode_fattr4(xdr, &value->ne_attrs))
+ return false;
+ return true;
+};
+
+static bool __maybe_unused
+xdrgen_encode_prev_entry4(struct xdr_stream *xdr, const struct prev_entry4 *value)
+{
+ if (!xdrgen_encode_notify_entry4(xdr, &value->pe_prev_entry))
+ return false;
+ if (!xdrgen_encode_nfs_cookie4(xdr, value->pe_prev_entry_cookie))
+ return false;
+ return true;
+};
+
+bool
+xdrgen_encode_notify_remove4(struct xdr_stream *xdr, const struct notify_remove4 *value)
+{
+ if (!xdrgen_encode_notify_entry4(xdr, &value->nrm_old_entry))
+ return false;
+ if (!xdrgen_encode_nfs_cookie4(xdr, value->nrm_old_entry_cookie))
+ return false;
+ return true;
+};
+
+bool
+xdrgen_encode_notify_add4(struct xdr_stream *xdr, const struct notify_add4 *value)
+{
+ if (value->nad_old_entry.count > 1)
+ return false;
+ if (xdr_stream_encode_u32(xdr, value->nad_old_entry.count) != XDR_UNIT)
+ return false;
+ for (u32 i = 0; i < value->nad_old_entry.count; i++)
+ if (!xdrgen_encode_notify_remove4(xdr, &value->nad_old_entry.element[i]))
+ return false;
+ if (!xdrgen_encode_notify_entry4(xdr, &value->nad_new_entry))
+ return false;
+ if (value->nad_new_entry_cookie.count > 1)
+ return false;
+ if (xdr_stream_encode_u32(xdr, value->nad_new_entry_cookie.count) != XDR_UNIT)
+ return false;
+ for (u32 i = 0; i < value->nad_new_entry_cookie.count; i++)
+ if (!xdrgen_encode_nfs_cookie4(xdr, value->nad_new_entry_cookie.element[i]))
+ return false;
+ if (value->nad_prev_entry.count > 1)
+ return false;
+ if (xdr_stream_encode_u32(xdr, value->nad_prev_entry.count) != XDR_UNIT)
+ return false;
+ for (u32 i = 0; i < value->nad_prev_entry.count; i++)
+ if (!xdrgen_encode_prev_entry4(xdr, &value->nad_prev_entry.element[i]))
+ return false;
+ if (!xdrgen_encode_bool(xdr, value->nad_last_entry))
+ return false;
+ return true;
+};
+
+bool
+xdrgen_encode_notify_attr4(struct xdr_stream *xdr, const struct notify_attr4 *value)
+{
+ if (!xdrgen_encode_notify_entry4(xdr, &value->na_changed_entry))
+ return false;
+ return true;
+};
+
+bool
+xdrgen_encode_notify_rename4(struct xdr_stream *xdr, const struct notify_rename4 *value)
+{
+ if (!xdrgen_encode_notify_remove4(xdr, &value->nrn_old_entry))
+ return false;
+ if (!xdrgen_encode_notify_add4(xdr, &value->nrn_new_entry))
+ return false;
+ return true;
+};
+
+static bool __maybe_unused
+xdrgen_encode_notify_verifier4(struct xdr_stream *xdr, const struct notify_verifier4 *value)
+{
+ if (!xdrgen_encode_verifier4(xdr, value->nv_old_cookieverf))
+ return false;
+ if (!xdrgen_encode_verifier4(xdr, value->nv_new_cookieverf))
+ return false;
+ return true;
+};
+
+static bool __maybe_unused
+xdrgen_encode_notifylist4(struct xdr_stream *xdr, const notifylist4 value)
+{
+ return xdr_stream_encode_opaque(xdr, value.data, value.len) >= 0;
+};
+
+static bool __maybe_unused
+xdrgen_encode_notify4(struct xdr_stream *xdr, const struct notify4 *value)
+{
+ if (!xdrgen_encode_bitmap4(xdr, value->notify_mask))
+ return false;
+ if (!xdrgen_encode_notifylist4(xdr, value->notify_vals))
+ return false;
+ return true;
+};
+
+bool
+xdrgen_encode_CB_NOTIFY4args(struct xdr_stream *xdr, const struct CB_NOTIFY4args *value)
+{
+ if (!xdrgen_encode_stateid4(xdr, &value->cna_stateid))
+ return false;
+ if (!xdrgen_encode_nfs_fh4(xdr, value->cna_fh))
+ return false;
+ if (xdr_stream_encode_u32(xdr, value->cna_changes.count) != XDR_UNIT)
+ return false;
+ for (u32 i = 0; i < value->cna_changes.count; i++)
+ if (!xdrgen_encode_notify4(xdr, &value->cna_changes.element[i]))
+ return false;
+ return true;
+};
+
+bool
+xdrgen_encode_CB_NOTIFY4res(struct xdr_stream *xdr, const struct CB_NOTIFY4res *value)
+{
+ if (!xdrgen_encode_nfsstat4(xdr, value->cnr_status))
+ return false;
+ return true;
+};
diff --git a/fs/nfsd/nfs4xdr_gen.h b/fs/nfsd/nfs4xdr_gen.h
index 41a0033b72562ee3c1fcdcd4a887ce635385b22b..e8f8dd65d58a23f39d432bed02ac21cb0640261f 100644
--- a/fs/nfsd/nfs4xdr_gen.h
+++ b/fs/nfsd/nfs4xdr_gen.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Generated by xdrgen. Manual edits will be lost. */
/* XDR specification file: ../../Documentation/sunrpc/xdr/nfs4_1.x */
-/* XDR specification modification time: Mon Oct 14 09:10:13 2024 */
+/* XDR specification modification time: Wed Sep 24 09:38:03 2025 */
#ifndef _LINUX_XDRGEN_NFS4_1_DECL_H
#define _LINUX_XDRGEN_NFS4_1_DECL_H
@@ -22,4 +22,22 @@ bool xdrgen_encode_fattr4_time_deleg_access(struct xdr_stream *xdr, const fattr4
bool xdrgen_decode_fattr4_time_deleg_modify(struct xdr_stream *xdr, fattr4_time_deleg_modify *ptr);
bool xdrgen_encode_fattr4_time_deleg_modify(struct xdr_stream *xdr, const fattr4_time_deleg_modify *value);
+bool xdrgen_decode_notify_remove4(struct xdr_stream *xdr, struct notify_remove4 *ptr);
+bool xdrgen_encode_notify_remove4(struct xdr_stream *xdr, const struct notify_remove4 *value);
+
+bool xdrgen_decode_notify_add4(struct xdr_stream *xdr, struct notify_add4 *ptr);
+bool xdrgen_encode_notify_add4(struct xdr_stream *xdr, const struct notify_add4 *value);
+
+bool xdrgen_decode_notify_attr4(struct xdr_stream *xdr, struct notify_attr4 *ptr);
+bool xdrgen_encode_notify_attr4(struct xdr_stream *xdr, const struct notify_attr4 *value);
+
+bool xdrgen_decode_notify_rename4(struct xdr_stream *xdr, struct notify_rename4 *ptr);
+bool xdrgen_encode_notify_rename4(struct xdr_stream *xdr, const struct notify_rename4 *value);
+
+bool xdrgen_decode_CB_NOTIFY4args(struct xdr_stream *xdr, struct CB_NOTIFY4args *ptr);
+bool xdrgen_encode_CB_NOTIFY4args(struct xdr_stream *xdr, const struct CB_NOTIFY4args *value);
+
+bool xdrgen_decode_CB_NOTIFY4res(struct xdr_stream *xdr, struct CB_NOTIFY4res *ptr);
+bool xdrgen_encode_CB_NOTIFY4res(struct xdr_stream *xdr, const struct CB_NOTIFY4res *value);
+
#endif /* _LINUX_XDRGEN_NFS4_1_DECL_H */
diff --git a/fs/nfsd/trace.h b/fs/nfsd/trace.h
index a664fdf1161e9afe9de59d4edd91762400a99350..01380425347b946bbf5a70a2ba10190ae4ec6303 100644
--- a/fs/nfsd/trace.h
+++ b/fs/nfsd/trace.h
@@ -1611,6 +1611,7 @@ TRACE_EVENT(nfsd_cb_setup_err,
{ OP_CB_RECALL, "CB_RECALL" }, \
{ OP_CB_LAYOUTRECALL, "CB_LAYOUTRECALL" }, \
{ OP_CB_RECALL_ANY, "CB_RECALL_ANY" }, \
+ { OP_CB_NOTIFY, "CB_NOTIFY" }, \
{ OP_CB_NOTIFY_LOCK, "CB_NOTIFY_LOCK" }, \
{ OP_CB_OFFLOAD, "CB_OFFLOAD" })
diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index e947af6a3684a4ae4abedbc56ff1811d15c23f3c..25bf410edec8ffde0508b33648af55d62594ad73 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -171,133 +171,6 @@ Needs to be updated if more operations are defined in future.*/
#define LAST_NFS42_OP OP_REMOVEXATTR
#define LAST_NFS4_OP LAST_NFS42_OP
-enum nfsstat4 {
- NFS4_OK = 0,
- NFS4ERR_PERM = 1,
- NFS4ERR_NOENT = 2,
- NFS4ERR_IO = 5,
- NFS4ERR_NXIO = 6,
- NFS4ERR_ACCESS = 13,
- NFS4ERR_EXIST = 17,
- NFS4ERR_XDEV = 18,
- /* Unused/reserved 19 */
- NFS4ERR_NOTDIR = 20,
- NFS4ERR_ISDIR = 21,
- NFS4ERR_INVAL = 22,
- NFS4ERR_FBIG = 27,
- NFS4ERR_NOSPC = 28,
- NFS4ERR_ROFS = 30,
- NFS4ERR_MLINK = 31,
- NFS4ERR_NAMETOOLONG = 63,
- NFS4ERR_NOTEMPTY = 66,
- NFS4ERR_DQUOT = 69,
- NFS4ERR_STALE = 70,
- NFS4ERR_BADHANDLE = 10001,
- NFS4ERR_BAD_COOKIE = 10003,
- NFS4ERR_NOTSUPP = 10004,
- NFS4ERR_TOOSMALL = 10005,
- NFS4ERR_SERVERFAULT = 10006,
- NFS4ERR_BADTYPE = 10007,
- NFS4ERR_DELAY = 10008,
- NFS4ERR_SAME = 10009,
- NFS4ERR_DENIED = 10010,
- NFS4ERR_EXPIRED = 10011,
- NFS4ERR_LOCKED = 10012,
- NFS4ERR_GRACE = 10013,
- NFS4ERR_FHEXPIRED = 10014,
- NFS4ERR_SHARE_DENIED = 10015,
- NFS4ERR_WRONGSEC = 10016,
- NFS4ERR_CLID_INUSE = 10017,
- NFS4ERR_RESOURCE = 10018,
- NFS4ERR_MOVED = 10019,
- NFS4ERR_NOFILEHANDLE = 10020,
- NFS4ERR_MINOR_VERS_MISMATCH = 10021,
- NFS4ERR_STALE_CLIENTID = 10022,
- NFS4ERR_STALE_STATEID = 10023,
- NFS4ERR_OLD_STATEID = 10024,
- NFS4ERR_BAD_STATEID = 10025,
- NFS4ERR_BAD_SEQID = 10026,
- NFS4ERR_NOT_SAME = 10027,
- NFS4ERR_LOCK_RANGE = 10028,
- NFS4ERR_SYMLINK = 10029,
- NFS4ERR_RESTOREFH = 10030,
- NFS4ERR_LEASE_MOVED = 10031,
- NFS4ERR_ATTRNOTSUPP = 10032,
- NFS4ERR_NO_GRACE = 10033,
- NFS4ERR_RECLAIM_BAD = 10034,
- NFS4ERR_RECLAIM_CONFLICT = 10035,
- NFS4ERR_BADXDR = 10036,
- NFS4ERR_LOCKS_HELD = 10037,
- NFS4ERR_OPENMODE = 10038,
- NFS4ERR_BADOWNER = 10039,
- NFS4ERR_BADCHAR = 10040,
- NFS4ERR_BADNAME = 10041,
- NFS4ERR_BAD_RANGE = 10042,
- NFS4ERR_LOCK_NOTSUPP = 10043,
- NFS4ERR_OP_ILLEGAL = 10044,
- NFS4ERR_DEADLOCK = 10045,
- NFS4ERR_FILE_OPEN = 10046,
- NFS4ERR_ADMIN_REVOKED = 10047,
- NFS4ERR_CB_PATH_DOWN = 10048,
-
- /* nfs41 */
- NFS4ERR_BADIOMODE = 10049,
- NFS4ERR_BADLAYOUT = 10050,
- NFS4ERR_BAD_SESSION_DIGEST = 10051,
- NFS4ERR_BADSESSION = 10052,
- NFS4ERR_BADSLOT = 10053,
- NFS4ERR_COMPLETE_ALREADY = 10054,
- NFS4ERR_CONN_NOT_BOUND_TO_SESSION = 10055,
- NFS4ERR_DELEG_ALREADY_WANTED = 10056,
- NFS4ERR_BACK_CHAN_BUSY = 10057, /* backchan reqs outstanding */
- NFS4ERR_LAYOUTTRYLATER = 10058,
- NFS4ERR_LAYOUTUNAVAILABLE = 10059,
- NFS4ERR_NOMATCHING_LAYOUT = 10060,
- NFS4ERR_RECALLCONFLICT = 10061,
- NFS4ERR_UNKNOWN_LAYOUTTYPE = 10062,
- NFS4ERR_SEQ_MISORDERED = 10063, /* unexpected seq.id in req */
- NFS4ERR_SEQUENCE_POS = 10064, /* [CB_]SEQ. op not 1st op */
- NFS4ERR_REQ_TOO_BIG = 10065, /* request too big */
- NFS4ERR_REP_TOO_BIG = 10066, /* reply too big */
- NFS4ERR_REP_TOO_BIG_TO_CACHE = 10067, /* rep. not all cached */
- NFS4ERR_RETRY_UNCACHED_REP = 10068, /* retry & rep. uncached */
- NFS4ERR_UNSAFE_COMPOUND = 10069, /* retry/recovery too hard */
- NFS4ERR_TOO_MANY_OPS = 10070, /* too many ops in [CB_]COMP */
- NFS4ERR_OP_NOT_IN_SESSION = 10071, /* op needs [CB_]SEQ. op */
- NFS4ERR_HASH_ALG_UNSUPP = 10072, /* hash alg. not supp. */
- /* Error 10073 is unused. */
- NFS4ERR_CLIENTID_BUSY = 10074, /* clientid has state */
- NFS4ERR_PNFS_IO_HOLE = 10075, /* IO to _SPARSE file hole */
- NFS4ERR_SEQ_FALSE_RETRY = 10076, /* retry not original */
- NFS4ERR_BAD_HIGH_SLOT = 10077, /* sequence arg bad */
- NFS4ERR_DEADSESSION = 10078, /* persistent session dead */
- NFS4ERR_ENCR_ALG_UNSUPP = 10079, /* SSV alg mismatch */
- NFS4ERR_PNFS_NO_LAYOUT = 10080, /* direct I/O with no layout */
- NFS4ERR_NOT_ONLY_OP = 10081, /* bad compound */
- NFS4ERR_WRONG_CRED = 10082, /* permissions:state change */
- NFS4ERR_WRONG_TYPE = 10083, /* current operation mismatch */
- NFS4ERR_DIRDELEG_UNAVAIL = 10084, /* no directory delegation */
- NFS4ERR_REJECT_DELEG = 10085, /* on callback */
- NFS4ERR_RETURNCONFLICT = 10086, /* outstanding layoutreturn */
- NFS4ERR_DELEG_REVOKED = 10087, /* deleg./layout revoked */
-
- /* nfs42 */
- NFS4ERR_PARTNER_NOTSUPP = 10088,
- NFS4ERR_PARTNER_NO_AUTH = 10089,
- NFS4ERR_UNION_NOTSUPP = 10090,
- NFS4ERR_OFFLOAD_DENIED = 10091,
- NFS4ERR_WRONG_LFS = 10092,
- NFS4ERR_BADLABEL = 10093,
- NFS4ERR_OFFLOAD_NO_REQS = 10094,
-
- /* xattr (RFC8276) */
- NFS4ERR_NOXATTR = 10095,
- NFS4ERR_XATTR2BIG = 10096,
-
- /* can be used for internal errors */
- NFS4ERR_FIRST_FREE
-};
-
/* error codes for internal client use */
#define NFS4ERR_RESET_TO_MDS 12001
#define NFS4ERR_RESET_TO_PNFS 12002
diff --git a/include/linux/sunrpc/xdrgen/nfs4_1.h b/include/linux/sunrpc/xdrgen/nfs4_1.h
index cf21a14aa8850f4b21cd365cb7bc22a02c6097ce..7d9f4c5f169bc47ddf31ff5b1b96db30329e8ad2 100644
--- a/include/linux/sunrpc/xdrgen/nfs4_1.h
+++ b/include/linux/sunrpc/xdrgen/nfs4_1.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Generated by xdrgen. Manual edits will be lost. */
/* XDR specification file: ../../Documentation/sunrpc/xdr/nfs4_1.x */
-/* XDR specification modification time: Mon Oct 14 09:10:13 2024 */
+/* XDR specification modification time: Wed Sep 24 09:38:03 2025 */
#ifndef _LINUX_XDRGEN_NFS4_1_DEF_H
#define _LINUX_XDRGEN_NFS4_1_DEF_H
@@ -9,20 +9,181 @@
#include <linux/types.h>
#include <linux/sunrpc/xdrgen/_defs.h>
-typedef s64 int64_t;
+typedef s32 int32_t;
typedef u32 uint32_t;
+typedef s64 int64_t;
+
+typedef u64 uint64_t;
+
+enum { NFS4_VERIFIER_SIZE = 8 };
+
+enum { NFS4_FHSIZE = 128 };
+
+enum nfsstat4 {
+ NFS4_OK = 0,
+ NFS4ERR_PERM = 1,
+ NFS4ERR_NOENT = 2,
+ NFS4ERR_IO = 5,
+ NFS4ERR_NXIO = 6,
+ NFS4ERR_ACCESS = 13,
+ NFS4ERR_EXIST = 17,
+ NFS4ERR_XDEV = 18,
+ NFS4ERR_NOTDIR = 20,
+ NFS4ERR_ISDIR = 21,
+ NFS4ERR_INVAL = 22,
+ NFS4ERR_FBIG = 27,
+ NFS4ERR_NOSPC = 28,
+ NFS4ERR_ROFS = 30,
+ NFS4ERR_MLINK = 31,
+ NFS4ERR_NAMETOOLONG = 63,
+ NFS4ERR_NOTEMPTY = 66,
+ NFS4ERR_DQUOT = 69,
+ NFS4ERR_STALE = 70,
+ NFS4ERR_BADHANDLE = 10001,
+ NFS4ERR_BAD_COOKIE = 10003,
+ NFS4ERR_NOTSUPP = 10004,
+ NFS4ERR_TOOSMALL = 10005,
+ NFS4ERR_SERVERFAULT = 10006,
+ NFS4ERR_BADTYPE = 10007,
+ NFS4ERR_DELAY = 10008,
+ NFS4ERR_SAME = 10009,
+ NFS4ERR_DENIED = 10010,
+ NFS4ERR_EXPIRED = 10011,
+ NFS4ERR_LOCKED = 10012,
+ NFS4ERR_GRACE = 10013,
+ NFS4ERR_FHEXPIRED = 10014,
+ NFS4ERR_SHARE_DENIED = 10015,
+ NFS4ERR_WRONGSEC = 10016,
+ NFS4ERR_CLID_INUSE = 10017,
+ NFS4ERR_RESOURCE = 10018,
+ NFS4ERR_MOVED = 10019,
+ NFS4ERR_NOFILEHANDLE = 10020,
+ NFS4ERR_MINOR_VERS_MISMATCH = 10021,
+ NFS4ERR_STALE_CLIENTID = 10022,
+ NFS4ERR_STALE_STATEID = 10023,
+ NFS4ERR_OLD_STATEID = 10024,
+ NFS4ERR_BAD_STATEID = 10025,
+ NFS4ERR_BAD_SEQID = 10026,
+ NFS4ERR_NOT_SAME = 10027,
+ NFS4ERR_LOCK_RANGE = 10028,
+ NFS4ERR_SYMLINK = 10029,
+ NFS4ERR_RESTOREFH = 10030,
+ NFS4ERR_LEASE_MOVED = 10031,
+ NFS4ERR_ATTRNOTSUPP = 10032,
+ NFS4ERR_NO_GRACE = 10033,
+ NFS4ERR_RECLAIM_BAD = 10034,
+ NFS4ERR_RECLAIM_CONFLICT = 10035,
+ NFS4ERR_BADXDR = 10036,
+ NFS4ERR_LOCKS_HELD = 10037,
+ NFS4ERR_OPENMODE = 10038,
+ NFS4ERR_BADOWNER = 10039,
+ NFS4ERR_BADCHAR = 10040,
+ NFS4ERR_BADNAME = 10041,
+ NFS4ERR_BAD_RANGE = 10042,
+ NFS4ERR_LOCK_NOTSUPP = 10043,
+ NFS4ERR_OP_ILLEGAL = 10044,
+ NFS4ERR_DEADLOCK = 10045,
+ NFS4ERR_FILE_OPEN = 10046,
+ NFS4ERR_ADMIN_REVOKED = 10047,
+ NFS4ERR_CB_PATH_DOWN = 10048,
+ NFS4ERR_BADIOMODE = 10049,
+ NFS4ERR_BADLAYOUT = 10050,
+ NFS4ERR_BAD_SESSION_DIGEST = 10051,
+ NFS4ERR_BADSESSION = 10052,
+ NFS4ERR_BADSLOT = 10053,
+ NFS4ERR_COMPLETE_ALREADY = 10054,
+ NFS4ERR_CONN_NOT_BOUND_TO_SESSION = 10055,
+ NFS4ERR_DELEG_ALREADY_WANTED = 10056,
+ NFS4ERR_BACK_CHAN_BUSY = 10057,
+ NFS4ERR_LAYOUTTRYLATER = 10058,
+ NFS4ERR_LAYOUTUNAVAILABLE = 10059,
+ NFS4ERR_NOMATCHING_LAYOUT = 10060,
+ NFS4ERR_RECALLCONFLICT = 10061,
+ NFS4ERR_UNKNOWN_LAYOUTTYPE = 10062,
+ NFS4ERR_SEQ_MISORDERED = 10063,
+ NFS4ERR_SEQUENCE_POS = 10064,
+ NFS4ERR_REQ_TOO_BIG = 10065,
+ NFS4ERR_REP_TOO_BIG = 10066,
+ NFS4ERR_REP_TOO_BIG_TO_CACHE = 10067,
+ NFS4ERR_RETRY_UNCACHED_REP = 10068,
+ NFS4ERR_UNSAFE_COMPOUND = 10069,
+ NFS4ERR_TOO_MANY_OPS = 10070,
+ NFS4ERR_OP_NOT_IN_SESSION = 10071,
+ NFS4ERR_HASH_ALG_UNSUPP = 10072,
+ NFS4ERR_CLIENTID_BUSY = 10074,
+ NFS4ERR_PNFS_IO_HOLE = 10075,
+ NFS4ERR_SEQ_FALSE_RETRY = 10076,
+ NFS4ERR_BAD_HIGH_SLOT = 10077,
+ NFS4ERR_DEADSESSION = 10078,
+ NFS4ERR_ENCR_ALG_UNSUPP = 10079,
+ NFS4ERR_PNFS_NO_LAYOUT = 10080,
+ NFS4ERR_NOT_ONLY_OP = 10081,
+ NFS4ERR_WRONG_CRED = 10082,
+ NFS4ERR_WRONG_TYPE = 10083,
+ NFS4ERR_DIRDELEG_UNAVAIL = 10084,
+ NFS4ERR_REJECT_DELEG = 10085,
+ NFS4ERR_RETURNCONFLICT = 10086,
+ NFS4ERR_DELEG_REVOKED = 10087,
+ NFS4ERR_PARTNER_NOTSUPP = 10088,
+ NFS4ERR_PARTNER_NO_AUTH = 10089,
+ NFS4ERR_UNION_NOTSUPP = 10090,
+ NFS4ERR_OFFLOAD_DENIED = 10091,
+ NFS4ERR_WRONG_LFS = 10092,
+ NFS4ERR_BADLABEL = 10093,
+ NFS4ERR_OFFLOAD_NO_REQS = 10094,
+ NFS4ERR_NOXATTR = 10095,
+ NFS4ERR_XATTR2BIG = 10096,
+ NFS4ERR_FIRST_FREE = 10097,
+};
+typedef enum nfsstat4 nfsstat4;
+
+typedef opaque attrlist4;
+
typedef struct {
u32 count;
uint32_t *element;
} bitmap4;
+typedef uint64_t nfs_cookie4;
+
+typedef opaque nfs_fh4;
+
+typedef opaque utf8string;
+
+typedef utf8string utf8str_cis;
+
+typedef utf8string utf8str_cs;
+
+typedef utf8string utf8str_mixed;
+
+typedef utf8str_cs component4;
+
+typedef utf8str_cs linktext4;
+
+typedef struct {
+ u32 count;
+ component4 *element;
+} pathname4;
+
+typedef u8 verifier4[NFS4_VERIFIER_SIZE];
+
struct nfstime4 {
int64_t seconds;
uint32_t nseconds;
};
+struct fattr4 {
+ bitmap4 attrmask;
+ attrlist4 attr_vals;
+};
+
+struct stateid4 {
+ uint32_t seqid;
+ u8 other[12];
+};
+
typedef bool fattr4_offline;
enum { FATTR4_OFFLINE = 83 };
@@ -126,13 +287,115 @@ enum open_delegation_type4 {
};
typedef enum open_delegation_type4 open_delegation_type4;
-#define NFS4_int64_t_sz \
- (XDR_hyper)
+enum notify_type4 {
+ NOTIFY4_CHANGE_CHILD_ATTRS = 0,
+ NOTIFY4_CHANGE_DIR_ATTRS = 1,
+ NOTIFY4_REMOVE_ENTRY = 2,
+ NOTIFY4_ADD_ENTRY = 3,
+ NOTIFY4_RENAME_ENTRY = 4,
+ NOTIFY4_CHANGE_COOKIE_VERIFIER = 5,
+};
+typedef enum notify_type4 notify_type4;
+
+struct notify_entry4 {
+ component4 ne_file;
+ struct fattr4 ne_attrs;
+};
+
+struct prev_entry4 {
+ struct notify_entry4 pe_prev_entry;
+ nfs_cookie4 pe_prev_entry_cookie;
+};
+
+struct notify_remove4 {
+ struct notify_entry4 nrm_old_entry;
+ nfs_cookie4 nrm_old_entry_cookie;
+};
+
+struct notify_add4 {
+ struct {
+ u32 count;
+ struct notify_remove4 *element;
+ } nad_old_entry;
+ struct notify_entry4 nad_new_entry;
+ struct {
+ u32 count;
+ nfs_cookie4 *element;
+ } nad_new_entry_cookie;
+ struct {
+ u32 count;
+ struct prev_entry4 *element;
+ } nad_prev_entry;
+ bool nad_last_entry;
+};
+
+struct notify_attr4 {
+ struct notify_entry4 na_changed_entry;
+};
+
+struct notify_rename4 {
+ struct notify_remove4 nrn_old_entry;
+ struct notify_add4 nrn_new_entry;
+};
+
+struct notify_verifier4 {
+ verifier4 nv_old_cookieverf;
+ verifier4 nv_new_cookieverf;
+};
+
+typedef opaque notifylist4;
+
+struct notify4 {
+ bitmap4 notify_mask;
+ notifylist4 notify_vals;
+};
+
+struct CB_NOTIFY4args {
+ struct stateid4 cna_stateid;
+ nfs_fh4 cna_fh;
+ struct {
+ u32 count;
+ struct notify4 *element;
+ } cna_changes;
+};
+
+struct CB_NOTIFY4res {
+ nfsstat4 cnr_status;
+};
+
+#define NFS4_int32_t_sz \
+ (XDR_int)
#define NFS4_uint32_t_sz \
(XDR_unsigned_int)
+#define NFS4_int64_t_sz \
+ (XDR_hyper)
+#define NFS4_uint64_t_sz \
+ (XDR_unsigned_hyper)
+#define NFS4_nfsstat4_sz (XDR_int)
+#define NFS4_attrlist4_sz (XDR_unsigned_int)
#define NFS4_bitmap4_sz (XDR_unsigned_int)
+#define NFS4_nfs_cookie4_sz \
+ (NFS4_uint64_t_sz)
+#define NFS4_nfs_fh4_sz (XDR_unsigned_int + XDR_QUADLEN(NFS4_FHSIZE))
+#define NFS4_utf8string_sz (XDR_unsigned_int)
+#define NFS4_utf8str_cis_sz \
+ (NFS4_utf8string_sz)
+#define NFS4_utf8str_cs_sz \
+ (NFS4_utf8string_sz)
+#define NFS4_utf8str_mixed_sz \
+ (NFS4_utf8string_sz)
+#define NFS4_component4_sz \
+ (NFS4_utf8str_cs_sz)
+#define NFS4_linktext4_sz \
+ (NFS4_utf8str_cs_sz)
+#define NFS4_pathname4_sz (XDR_unsigned_int)
+#define NFS4_verifier4_sz (XDR_QUADLEN(NFS4_VERIFIER_SIZE))
#define NFS4_nfstime4_sz \
(NFS4_int64_t_sz + NFS4_uint32_t_sz)
+#define NFS4_fattr4_sz \
+ (NFS4_bitmap4_sz + NFS4_attrlist4_sz)
+#define NFS4_stateid4_sz \
+ (NFS4_uint32_t_sz + XDR_QUADLEN(12))
#define NFS4_fattr4_offline_sz \
(XDR_bool)
#define NFS4_open_arguments4_sz \
@@ -149,5 +412,27 @@ typedef enum open_delegation_type4 open_delegation_type4;
#define NFS4_fattr4_time_deleg_modify_sz \
(NFS4_nfstime4_sz)
#define NFS4_open_delegation_type4_sz (XDR_int)
+#define NFS4_notify_type4_sz (XDR_int)
+#define NFS4_notify_entry4_sz \
+ (NFS4_component4_sz + NFS4_fattr4_sz)
+#define NFS4_prev_entry4_sz \
+ (NFS4_notify_entry4_sz + NFS4_nfs_cookie4_sz)
+#define NFS4_notify_remove4_sz \
+ (NFS4_notify_entry4_sz + NFS4_nfs_cookie4_sz)
+#define NFS4_notify_add4_sz \
+ (XDR_unsigned_int + (1 * (NFS4_notify_remove4_sz)) + NFS4_notify_entry4_sz + XDR_unsigned_int + (1 * (NFS4_nfs_cookie4_sz)) + XDR_unsigned_int + (1 * (NFS4_prev_entry4_sz)) + XDR_bool)
+#define NFS4_notify_attr4_sz \
+ (NFS4_notify_entry4_sz)
+#define NFS4_notify_rename4_sz \
+ (NFS4_notify_remove4_sz + NFS4_notify_add4_sz)
+#define NFS4_notify_verifier4_sz \
+ (NFS4_verifier4_sz + NFS4_verifier4_sz)
+#define NFS4_notifylist4_sz (XDR_unsigned_int)
+#define NFS4_notify4_sz \
+ (NFS4_bitmap4_sz + NFS4_notifylist4_sz)
+#define NFS4_CB_NOTIFY4args_sz \
+ (NFS4_stateid4_sz + NFS4_nfs_fh4_sz + XDR_unsigned_int)
+#define NFS4_CB_NOTIFY4res_sz \
+ (NFS4_nfsstat4_sz)
#endif /* _LINUX_XDRGEN_NFS4_1_DEF_H */
diff --git a/include/uapi/linux/nfs4.h b/include/uapi/linux/nfs4.h
index 4273e0249fcbb54996f5642f9920826b9d68b7b9..289205b53a0858e589380c69ad1ba0cfd5f825fd 100644
--- a/include/uapi/linux/nfs4.h
+++ b/include/uapi/linux/nfs4.h
@@ -17,11 +17,9 @@
#include <linux/types.h>
#define NFS4_BITMAP_SIZE 3
-#define NFS4_VERIFIER_SIZE 8
#define NFS4_STATEID_SEQID_SIZE 4
#define NFS4_STATEID_OTHER_SIZE 12
#define NFS4_STATEID_SIZE (NFS4_STATEID_SEQID_SIZE + NFS4_STATEID_OTHER_SIZE)
-#define NFS4_FHSIZE 128
#define NFS4_MAXPATHLEN PATH_MAX
#define NFS4_MAXNAMLEN NAME_MAX
#define NFS4_OPAQUE_LIMIT 1024
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 20/38] nfs_common: add new NOTIFY4_* flags proposed in RFC8881bis
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (18 preceding siblings ...)
2025-09-24 18:06 ` [PATCH v3 19/38] nfsd: add protocol support for CB_NOTIFY Jeff Layton
@ 2025-09-24 18:06 ` Jeff Layton
2025-09-24 18:06 ` [PATCH v3 21/38] nfsd: allow nfsd to get a dir lease with an ignore mask Jeff Layton
` (18 subsequent siblings)
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:06 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
RFC8881bis adds some new flags to GET_DIR_DELEGATION that we very much
need to support.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
Documentation/sunrpc/xdr/nfs4_1.x | 16 +++++++++++++++-
fs/nfsd/nfs4xdr_gen.c | 2 +-
fs/nfsd/nfs4xdr_gen.h | 2 +-
include/linux/sunrpc/xdrgen/nfs4_1.h | 13 ++++++++++++-
4 files changed, 29 insertions(+), 4 deletions(-)
diff --git a/Documentation/sunrpc/xdr/nfs4_1.x b/Documentation/sunrpc/xdr/nfs4_1.x
index 9e00910c02e0aecfb0f86ff7b534049d2c588cf3..d25e2f5489ea44b74c423702feceb563a1aaa7a4 100644
--- a/Documentation/sunrpc/xdr/nfs4_1.x
+++ b/Documentation/sunrpc/xdr/nfs4_1.x
@@ -358,7 +358,21 @@ enum notify_type4 {
NOTIFY4_REMOVE_ENTRY = 2,
NOTIFY4_ADD_ENTRY = 3,
NOTIFY4_RENAME_ENTRY = 4,
- NOTIFY4_CHANGE_COOKIE_VERIFIER = 5
+ NOTIFY4_CHANGE_COOKIE_VERIFIER = 5,
+ /*
+ * Added in NFSv4.1 bis document
+ */
+ NOTIFY4_GFLAG_EXTEND = 6,
+ NOTIFY4_AUFLAG_VALID = 7,
+ NOTIFY4_AUFLAG_USER = 8,
+ NOTIFY4_AUFLAG_GROUP = 9,
+ NOTIFY4_AUFLAG_OTHER = 10,
+ NOTIFY4_CHANGE_AUTH = 11,
+ NOTIFY4_CFLAG_ORDER = 12,
+ NOTIFY4_AUFLAG_GANOW = 13,
+ NOTIFY4_AUFLAG_GALATER = 14,
+ NOTIFY4_CHANGE_GA = 15,
+ NOTIFY4_CHANGE_AMASK = 16
};
/* Changed entry information. */
diff --git a/fs/nfsd/nfs4xdr_gen.c b/fs/nfsd/nfs4xdr_gen.c
index 306a4c30c3a4e6b9066c5ad41c1f0f7ffbb27bae..e713ca9b15203c1f5fcd14fbf70492a6b0907b40 100644
--- a/fs/nfsd/nfs4xdr_gen.c
+++ b/fs/nfsd/nfs4xdr_gen.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
// Generated by xdrgen. Manual edits will be lost.
// XDR specification file: ../../Documentation/sunrpc/xdr/nfs4_1.x
-// XDR specification modification time: Wed Sep 24 09:38:03 2025
+// XDR specification modification time: Wed Sep 24 09:39:12 2025
#include <linux/sunrpc/svc.h>
diff --git a/fs/nfsd/nfs4xdr_gen.h b/fs/nfsd/nfs4xdr_gen.h
index e8f8dd65d58a23f39d432bed02ac21cb0640261f..50e474a1cd8225108c83bb01caaa5a0a56384413 100644
--- a/fs/nfsd/nfs4xdr_gen.h
+++ b/fs/nfsd/nfs4xdr_gen.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Generated by xdrgen. Manual edits will be lost. */
/* XDR specification file: ../../Documentation/sunrpc/xdr/nfs4_1.x */
-/* XDR specification modification time: Wed Sep 24 09:38:03 2025 */
+/* XDR specification modification time: Wed Sep 24 09:39:12 2025 */
#ifndef _LINUX_XDRGEN_NFS4_1_DECL_H
#define _LINUX_XDRGEN_NFS4_1_DECL_H
diff --git a/include/linux/sunrpc/xdrgen/nfs4_1.h b/include/linux/sunrpc/xdrgen/nfs4_1.h
index 7d9f4c5f169bc47ddf31ff5b1b96db30329e8ad2..ad31d2ec7ce050c6a5900732628e4e7607dabcae 100644
--- a/include/linux/sunrpc/xdrgen/nfs4_1.h
+++ b/include/linux/sunrpc/xdrgen/nfs4_1.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Generated by xdrgen. Manual edits will be lost. */
/* XDR specification file: ../../Documentation/sunrpc/xdr/nfs4_1.x */
-/* XDR specification modification time: Wed Sep 24 09:38:03 2025 */
+/* XDR specification modification time: Wed Sep 24 09:39:12 2025 */
#ifndef _LINUX_XDRGEN_NFS4_1_DEF_H
#define _LINUX_XDRGEN_NFS4_1_DEF_H
@@ -294,6 +294,17 @@ enum notify_type4 {
NOTIFY4_ADD_ENTRY = 3,
NOTIFY4_RENAME_ENTRY = 4,
NOTIFY4_CHANGE_COOKIE_VERIFIER = 5,
+ NOTIFY4_GFLAG_EXTEND = 6,
+ NOTIFY4_AUFLAG_VALID = 7,
+ NOTIFY4_AUFLAG_USER = 8,
+ NOTIFY4_AUFLAG_GROUP = 9,
+ NOTIFY4_AUFLAG_OTHER = 10,
+ NOTIFY4_CHANGE_AUTH = 11,
+ NOTIFY4_CFLAG_ORDER = 12,
+ NOTIFY4_AUFLAG_GANOW = 13,
+ NOTIFY4_AUFLAG_GALATER = 14,
+ NOTIFY4_CHANGE_GA = 15,
+ NOTIFY4_CHANGE_AMASK = 16,
};
typedef enum notify_type4 notify_type4;
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 21/38] nfsd: allow nfsd to get a dir lease with an ignore mask
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (19 preceding siblings ...)
2025-09-24 18:06 ` [PATCH v3 20/38] nfs_common: add new NOTIFY4_* flags proposed in RFC8881bis Jeff Layton
@ 2025-09-24 18:06 ` Jeff Layton
2025-09-24 18:06 ` [PATCH v3 22/38] vfs: add fsnotify_modify_mark_mask() Jeff Layton
` (17 subsequent siblings)
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:06 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
When requesting a directory lease, enable the FL_IGN_DIR_* bits that
correspond to the requested notification types.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/nfsd/nfs4state.c | 27 +++++++++++++++++++++------
1 file changed, 21 insertions(+), 6 deletions(-)
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index d1d586ec0e4e2bef908dc0671c34edab9cad5ba2..e219556e0959dbf0a8147d5edbb725da125a978f 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -5937,14 +5937,30 @@ static bool nfsd4_cb_channel_good(struct nfs4_client *clp)
return clp->cl_minorversion && clp->cl_cb_state == NFSD4_CB_UNKNOWN;
}
-static struct file_lease *nfs4_alloc_init_lease(struct nfs4_delegation *dp)
+static unsigned int
+nfsd_notify_to_ignore(u32 notify)
+{
+ unsigned int mask = 0;
+
+ if (notify & BIT(NOTIFY4_REMOVE_ENTRY))
+ mask |= FL_IGN_DIR_DELETE;
+ if (notify & BIT(NOTIFY4_ADD_ENTRY))
+ mask |= FL_IGN_DIR_CREATE;
+ if (notify & BIT(NOTIFY4_RENAME_ENTRY))
+ mask |= FL_IGN_DIR_RENAME;
+
+ return mask;
+}
+
+static struct file_lease *nfs4_alloc_init_lease(struct nfs4_delegation *dp, u32 notify)
{
struct file_lease *fl;
fl = locks_alloc_lease();
if (!fl)
return NULL;
- fl->c.flc_flags = FL_DELEG;
+
+ fl->c.flc_flags = FL_DELEG | nfsd_notify_to_ignore(notify);
fl->c.flc_type = deleg_is_read(dp->dl_type) ? F_RDLCK : F_WRLCK;
fl->c.flc_owner = (fl_owner_t)dp;
fl->c.flc_pid = current->tgid;
@@ -6161,7 +6177,7 @@ nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
if (!dp)
goto out_delegees;
- fl = nfs4_alloc_init_lease(dp);
+ fl = nfs4_alloc_init_lease(dp, 0);
if (!fl)
goto out_clnt_odstate;
@@ -9468,12 +9484,11 @@ nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate,
if (!dp)
goto out_delegees;
- fl = nfs4_alloc_init_lease(dp);
+ fl = nfs4_alloc_init_lease(dp, gdd->gddr_notification[0]);
if (!fl)
goto out_put_stid;
- status = kernel_setlease(nf->nf_file,
- fl->c.flc_type, &fl, NULL);
+ status = kernel_setlease(nf->nf_file, fl->c.flc_type, &fl, NULL);
if (fl)
locks_free_lease(fl);
if (status)
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 22/38] vfs: add fsnotify_modify_mark_mask()
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (20 preceding siblings ...)
2025-09-24 18:06 ` [PATCH v3 21/38] nfsd: allow nfsd to get a dir lease with an ignore mask Jeff Layton
@ 2025-09-24 18:06 ` Jeff Layton
2025-09-25 15:50 ` Jan Kara
2025-09-24 18:06 ` [PATCH v3 23/38] nfsd: update the fsnotify mark when setting or removing a dir delegation Jeff Layton
` (16 subsequent siblings)
38 siblings, 1 reply; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:06 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
nfsd needs to be able to modify the mask on an existing mark when new
directory delegations are set or unset. Add an exported function that
allows the caller to set and clear bits in the mark->mask, and does
the recalculation if something changed.
Suggested-by: Jan Kara <jack@suse.cz>
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/notify/mark.c | 29 +++++++++++++++++++++++++++++
include/linux/fsnotify_backend.h | 1 +
2 files changed, 30 insertions(+)
diff --git a/fs/notify/mark.c b/fs/notify/mark.c
index 798340db69d761dd05c1b361c251818dee89b9cf..5ed42b24df7f6aa3812a7069b4c37f0c6b3414fa 100644
--- a/fs/notify/mark.c
+++ b/fs/notify/mark.c
@@ -309,6 +309,35 @@ void fsnotify_recalc_mask(struct fsnotify_mark_connector *conn)
fsnotify_conn_set_children_dentry_flags(conn);
}
+/**
+ * fsnotify_modify_mark_mask - set and/or clear flags in a mark's mask
+ * @mark: mark to be modified
+ * @set: bits to be set in mask
+ * @clear: bits to be cleared in mask
+ *
+ * Modify a fsnotify_mark mask as directed, and update its associated conn.
+ * The caller is expected to hold a reference to the mark.
+ */
+void fsnotify_modify_mark_mask(struct fsnotify_mark *mark, u32 set, u32 clear)
+{
+ bool recalc = false;
+ u32 mask;
+
+ WARN_ON_ONCE(clear & set);
+
+ spin_lock(&mark->lock);
+ mask = mark->mask;
+ mark->mask |= set;
+ mark->mask &= ~clear;
+ if (mark->mask != mask)
+ recalc = true;
+ spin_unlock(&mark->lock);
+
+ if (recalc)
+ fsnotify_recalc_mask(mark->connector);
+}
+EXPORT_SYMBOL_GPL(fsnotify_modify_mark_mask);
+
/* Free all connectors queued for freeing once SRCU period ends */
static void fsnotify_connector_destroy_workfn(struct work_struct *work)
{
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
index d4034ddaf3926bf98d8801997e50ba7ddf776292..8d50e6aad3c62c67a9bf73a8d9aab78565668c5f 100644
--- a/include/linux/fsnotify_backend.h
+++ b/include/linux/fsnotify_backend.h
@@ -912,6 +912,7 @@ extern void fsnotify_get_mark(struct fsnotify_mark *mark);
extern void fsnotify_put_mark(struct fsnotify_mark *mark);
extern void fsnotify_finish_user_wait(struct fsnotify_iter_info *iter_info);
extern bool fsnotify_prepare_user_wait(struct fsnotify_iter_info *iter_info);
+extern void fsnotify_modify_mark_mask(struct fsnotify_mark *mark, u32 set, u32 clear);
static inline void fsnotify_init_event(struct fsnotify_event *event)
{
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 23/38] nfsd: update the fsnotify mark when setting or removing a dir delegation
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (21 preceding siblings ...)
2025-09-24 18:06 ` [PATCH v3 22/38] vfs: add fsnotify_modify_mark_mask() Jeff Layton
@ 2025-09-24 18:06 ` Jeff Layton
2025-09-24 18:06 ` [PATCH v3 24/38] nfsd: make nfsd4_callback_ops->prepare operation bool return Jeff Layton
` (15 subsequent siblings)
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:06 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
Add a new helper function that will update the mask on the nfsd_file's
fsnotify_mark to be a union of all current directory delegations on an
inode. Call that when directory delegations are added or removed.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/nfsd/nfs4state.c | 36 +++++++++++++++++++++++++++++++++++-
1 file changed, 35 insertions(+), 1 deletion(-)
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index e219556e0959dbf0a8147d5edbb725da125a978f..eac9e9360e568caf1f615d230cff39e022107f12 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1259,6 +1259,37 @@ static void nfsd4_finalize_deleg_timestamps(struct nfs4_delegation *dp, struct f
}
}
+static void nfsd_fsnotify_recalc_mask(struct nfsd_file *nf)
+{
+ struct fsnotify_mark *mark = &nf->nf_mark->nfm_mark;
+ struct inode *inode = file_inode(nf->nf_file);
+ u32 lease_mask, set = 0, clear = 0;
+
+ /* This is only needed when adding or removing dir delegs */
+ if (!S_ISDIR(inode->i_mode))
+ return;
+
+ /* Set up notifications for any ignored delegation events */
+ lease_mask = inode_lease_ignore_mask(inode);
+
+ if (lease_mask & FL_IGN_DIR_CREATE)
+ set |= FS_CREATE;
+ else
+ clear |= FS_CREATE;
+
+ if (lease_mask & FL_IGN_DIR_DELETE)
+ set |= FS_DELETE;
+ else
+ clear |= FS_DELETE;
+
+ if (lease_mask & FL_IGN_DIR_RENAME)
+ set |= FS_RENAME;
+ else
+ clear |= FS_RENAME;
+
+ fsnotify_modify_mark_mask(mark, set, clear);
+}
+
static void nfs4_unlock_deleg_lease(struct nfs4_delegation *dp)
{
struct nfs4_file *fp = dp->dl_stid.sc_file;
@@ -1267,6 +1298,7 @@ static void nfs4_unlock_deleg_lease(struct nfs4_delegation *dp)
WARN_ON_ONCE(!fp->fi_delegees);
nfsd4_finalize_deleg_timestamps(dp, nf->nf_file);
+ nfsd_fsnotify_recalc_mask(nf);
kernel_setlease(nf->nf_file, F_UNLCK, NULL, (void **)&dp);
put_deleg_file(fp);
}
@@ -9507,8 +9539,10 @@ nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate,
spin_unlock(&clp->cl_lock);
spin_unlock(&state_lock);
- if (!status)
+ if (!status) {
+ nfsd_fsnotify_recalc_mask(nf);
return dp;
+ }
/* Something failed. Drop the lease and clean up the stid */
kernel_setlease(fp->fi_deleg_file->nf_file, F_UNLCK, NULL, (void **)&dp);
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 24/38] nfsd: make nfsd4_callback_ops->prepare operation bool return
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (22 preceding siblings ...)
2025-09-24 18:06 ` [PATCH v3 23/38] nfsd: update the fsnotify mark when setting or removing a dir delegation Jeff Layton
@ 2025-09-24 18:06 ` Jeff Layton
2025-09-24 18:06 ` [PATCH v3 25/38] nfsd: add callback encoding and decoding linkages for CB_NOTIFY Jeff Layton
` (14 subsequent siblings)
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:06 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
For a CB_NOTIFY operation, we need to stop processing the callback
if an allocation fails. Change the ->prepare callback operation to
return true if processing should continue, and false otherwise.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/nfsd/nfs4callback.c | 5 ++++-
fs/nfsd/nfs4layouts.c | 3 ++-
fs/nfsd/nfs4state.c | 6 ++++--
fs/nfsd/state.h | 6 +++---
4 files changed, 13 insertions(+), 7 deletions(-)
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index e00b2aea8da2b93f366d88888f404734953f1942..d13a4819d764269338f2b05a05f975c037e589af 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -1739,7 +1739,10 @@ nfsd4_run_cb_work(struct work_struct *work)
if (!test_and_clear_bit(NFSD4_CALLBACK_REQUEUE, &cb->cb_flags)) {
if (cb->cb_ops && cb->cb_ops->prepare)
- cb->cb_ops->prepare(cb);
+ if (!cb->cb_ops->prepare(cb)) {
+ nfsd41_destroy_cb(cb);
+ return;
+ }
}
cb->cb_msg.rpc_cred = clp->cl_cb_cred;
diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c
index 683bd1130afe298f9df774684192c89f68102b72..1916e5541153126edc357cfeeff9ce30461b69c0 100644
--- a/fs/nfsd/nfs4layouts.c
+++ b/fs/nfsd/nfs4layouts.c
@@ -652,7 +652,7 @@ nfsd4_cb_layout_fail(struct nfs4_layout_stateid *ls, struct nfsd_file *file)
}
}
-static void
+static bool
nfsd4_cb_layout_prepare(struct nfsd4_callback *cb)
{
struct nfs4_layout_stateid *ls =
@@ -661,6 +661,7 @@ nfsd4_cb_layout_prepare(struct nfsd4_callback *cb)
mutex_lock(&ls->ls_mutex);
nfs4_inc_and_copy_stateid(&ls->ls_recall_sid, &ls->ls_stid);
mutex_unlock(&ls->ls_mutex);
+ return true;
}
static int
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index eac9e9360e568caf1f615d230cff39e022107f12..517ba5595da3be5e130e1978ba30235496efbe01 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -361,12 +361,13 @@ remove_blocked_locks(struct nfs4_lockowner *lo)
}
}
-static void
+static bool
nfsd4_cb_notify_lock_prepare(struct nfsd4_callback *cb)
{
struct nfsd4_blocked_lock *nbl = container_of(cb,
struct nfsd4_blocked_lock, nbl_cb);
locks_delete_block(&nbl->nbl_lock);
+ return true;
}
static int
@@ -5450,7 +5451,7 @@ bool nfsd_wait_for_delegreturn(struct svc_rqst *rqstp, struct inode *inode)
return timeo > 0;
}
-static void nfsd4_cb_recall_prepare(struct nfsd4_callback *cb)
+static bool nfsd4_cb_recall_prepare(struct nfsd4_callback *cb)
{
struct nfs4_delegation *dp = cb_to_delegation(cb);
struct nfsd_net *nn = net_generic(dp->dl_stid.sc_client->net,
@@ -5471,6 +5472,7 @@ static void nfsd4_cb_recall_prepare(struct nfsd4_callback *cb)
list_add_tail(&dp->dl_recall_lru, &nn->del_recall_lru);
}
spin_unlock(&state_lock);
+ return true;
}
static int nfsd4_cb_recall_done(struct nfsd4_callback *cb,
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index b052c1effdc5356487c610db9728df8ecfe851d4..bacb9f9eff3aaf7076d53f06826026569a567bd9 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -98,9 +98,9 @@ struct nfsd4_callback {
};
struct nfsd4_callback_ops {
- void (*prepare)(struct nfsd4_callback *);
- int (*done)(struct nfsd4_callback *, struct rpc_task *);
- void (*release)(struct nfsd4_callback *);
+ bool (*prepare)(struct nfsd4_callback *cb);
+ int (*done)(struct nfsd4_callback *cb, struct rpc_task *task);
+ void (*release)(struct nfsd4_callback *cb);
uint32_t opcode;
};
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 25/38] nfsd: add callback encoding and decoding linkages for CB_NOTIFY
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (23 preceding siblings ...)
2025-09-24 18:06 ` [PATCH v3 24/38] nfsd: make nfsd4_callback_ops->prepare operation bool return Jeff Layton
@ 2025-09-24 18:06 ` Jeff Layton
2025-09-24 18:06 ` [PATCH v3 26/38] nfsd: add data structures for handling CB_NOTIFY to directory delegation Jeff Layton
` (13 subsequent siblings)
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:06 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
Add routines for encoding and decoding CB_NOTIFY messages. These call
into the code generated by xdrgen to do the actual encoding and
decoding.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/nfsd/nfs4callback.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
fs/nfsd/state.h | 8 ++++++++
fs/nfsd/xdr4cb.h | 12 ++++++++++++
3 files changed, 66 insertions(+)
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index d13a4819d764269338f2b05a05f975c037e589af..fe7b20b94d76efd309e27c1a3ef359e7101dac80 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -865,6 +865,51 @@ static void encode_stateowner(struct xdr_stream *xdr, struct nfs4_stateowner *so
xdr_encode_opaque(p, so->so_owner.data, so->so_owner.len);
}
+static void nfs4_xdr_enc_cb_notify(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ const void *data)
+{
+ const struct nfsd4_callback *cb = data;
+ struct nfs4_cb_compound_hdr hdr = {
+ .ident = 0,
+ .minorversion = cb->cb_clp->cl_minorversion,
+ };
+ struct CB_NOTIFY4args args = { };
+
+ WARN_ON_ONCE(hdr.minorversion == 0);
+
+ encode_cb_compound4args(xdr, &hdr);
+ encode_cb_sequence4args(xdr, cb, &hdr);
+
+ /*
+ * FIXME: get stateid and fh from delegation. Inline the cna_changes
+ * buffer, and zero it.
+ */
+ WARN_ON_ONCE(!xdrgen_encode_CB_NOTIFY4args(xdr, &args));
+
+ hdr.nops++;
+ encode_cb_nops(&hdr);
+}
+
+static int nfs4_xdr_dec_cb_notify(struct rpc_rqst *rqstp,
+ struct xdr_stream *xdr,
+ void *data)
+{
+ struct nfsd4_callback *cb = data;
+ struct nfs4_cb_compound_hdr hdr;
+ int status;
+
+ status = decode_cb_compound4res(xdr, &hdr);
+ if (unlikely(status))
+ return status;
+
+ status = decode_cb_sequence4res(xdr, cb);
+ if (unlikely(status || cb->cb_seq_status))
+ return status;
+
+ return decode_cb_op_status(xdr, OP_CB_NOTIFY, &cb->cb_status);
+}
+
static void nfs4_xdr_enc_cb_notify_lock(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
@@ -1026,6 +1071,7 @@ static const struct rpc_procinfo nfs4_cb_procedures[] = {
#ifdef CONFIG_NFSD_PNFS
PROC(CB_LAYOUT, COMPOUND, cb_layout, cb_layout),
#endif
+ PROC(CB_NOTIFY, COMPOUND, cb_notify, cb_notify),
PROC(CB_NOTIFY_LOCK, COMPOUND, cb_notify_lock, cb_notify_lock),
PROC(CB_OFFLOAD, COMPOUND, cb_offload, cb_offload),
PROC(CB_RECALL_ANY, COMPOUND, cb_recall_any, cb_recall_any),
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index bacb9f9eff3aaf7076d53f06826026569a567bd9..596d0bbf868c0ca2a31fa20f3ac61db66b60636d 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -189,6 +189,13 @@ struct nfs4_cb_fattr {
u64 ncf_cur_fsize;
};
+/*
+ * FIXME: the current backchannel encoder can't handle a send buffer longer
+ * than a single page (see bc_alloc/bc_free).
+ */
+#define NOTIFY4_EVENT_QUEUE_SIZE 3
+#define NOTIFY4_PAGE_ARRAY_SIZE 1
+
/*
* Represents a delegation stateid. The nfs4_client holds references to these
* and they are put when it is being destroyed or when the delegation is
@@ -755,6 +762,7 @@ enum nfsd4_cb_op {
NFSPROC4_CLNT_CB_NOTIFY_LOCK,
NFSPROC4_CLNT_CB_RECALL_ANY,
NFSPROC4_CLNT_CB_GETATTR,
+ NFSPROC4_CLNT_CB_NOTIFY,
};
/* Returns true iff a is later than b: */
diff --git a/fs/nfsd/xdr4cb.h b/fs/nfsd/xdr4cb.h
index f4e29c0c701c9b04c44dadc752e847dc4da163d6..b06d0170d7c43b9ad3a3a4f49878dc3f8b46099d 100644
--- a/fs/nfsd/xdr4cb.h
+++ b/fs/nfsd/xdr4cb.h
@@ -33,6 +33,18 @@
cb_sequence_dec_sz + \
op_dec_sz)
+#define NFS4_enc_cb_notify_sz (cb_compound_enc_hdr_sz + \
+ cb_sequence_enc_sz + \
+ 1 + enc_stateid_sz + \
+ enc_nfs4_fh_sz + \
+ 1 + \
+ NOTIFY4_EVENT_QUEUE_SIZE * \
+ (2 + (NFS4_OPAQUE_LIMIT >> 2)))
+
+#define NFS4_dec_cb_notify_sz (cb_compound_dec_hdr_sz + \
+ cb_sequence_dec_sz + \
+ op_dec_sz)
+
#define NFS4_enc_cb_notify_lock_sz (cb_compound_enc_hdr_sz + \
cb_sequence_enc_sz + \
2 + 1 + \
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 26/38] nfsd: add data structures for handling CB_NOTIFY to directory delegation
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (24 preceding siblings ...)
2025-09-24 18:06 ` [PATCH v3 25/38] nfsd: add callback encoding and decoding linkages for CB_NOTIFY Jeff Layton
@ 2025-09-24 18:06 ` Jeff Layton
2025-09-24 18:06 ` [PATCH v3 27/38] nfsd: add notification handlers for dir events Jeff Layton
` (12 subsequent siblings)
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:06 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
When a directory delegation is created, have it allocate the necessary
data structures to collect events and run a CB_NOTIFY callback. For now,
the callback_ops are still skeletal. They'll be fleshed out in a
subsequent patch.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/nfsd/nfs4state.c | 119 +++++++++++++++++++++++++++++++++++++++++++++-------
fs/nfsd/state.h | 46 +++++++++++++++++++-
2 files changed, 149 insertions(+), 16 deletions(-)
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 517ba5595da3be5e130e1978ba30235496efbe01..5d3af33e70e26e59f8bc3d5b44c82beafb4f786b 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -130,6 +130,7 @@ static void free_session(struct nfsd4_session *);
static const struct nfsd4_callback_ops nfsd4_cb_recall_ops;
static const struct nfsd4_callback_ops nfsd4_cb_notify_lock_ops;
static const struct nfsd4_callback_ops nfsd4_cb_getattr_ops;
+static const struct nfsd4_callback_ops nfsd4_cb_notify_ops;
static struct workqueue_struct *laundry_wq;
@@ -1127,29 +1128,31 @@ static void block_delegations(struct knfsd_fh *fh)
}
static struct nfs4_delegation *
-alloc_init_deleg(struct nfs4_client *clp, struct nfs4_file *fp,
- struct nfs4_clnt_odstate *odstate, u32 dl_type)
+__alloc_init_deleg(struct nfs4_client *clp, struct nfs4_file *fp,
+ struct nfs4_clnt_odstate *odstate, u32 dl_type,
+ void (*sc_free)(struct nfs4_stid *))
{
struct nfs4_delegation *dp;
struct nfs4_stid *stid;
long n;
- dprintk("NFSD alloc_init_deleg\n");
+ if (delegation_blocked(&fp->fi_fhandle))
+ return NULL;
+
n = atomic_long_inc_return(&num_delegations);
if (n < 0 || n > max_delegations)
goto out_dec;
- if (delegation_blocked(&fp->fi_fhandle))
- goto out_dec;
- stid = nfs4_alloc_stid(clp, deleg_slab, nfs4_free_deleg);
+
+ stid = nfs4_alloc_stid(clp, deleg_slab, sc_free);
if (stid == NULL)
goto out_dec;
- dp = delegstateid(stid);
/*
* delegation seqid's are never incremented. The 4.1 special
* meaning of seqid 0 isn't meaningful, really, but let's avoid
- * 0 anyway just for consistency and use 1:
+ * 0 anyway just for consistency and use 1.
*/
+ dp = delegstateid(stid);
dp->dl_stid.sc_stateid.si_generation = 1;
INIT_LIST_HEAD(&dp->dl_perfile);
INIT_LIST_HEAD(&dp->dl_perclnt);
@@ -1159,19 +1162,77 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_file *fp,
dp->dl_type = dl_type;
dp->dl_retries = 1;
dp->dl_recalled = false;
- nfsd4_init_cb(&dp->dl_recall, dp->dl_stid.sc_client,
- &nfsd4_cb_recall_ops, NFSPROC4_CLNT_CB_RECALL);
- nfsd4_init_cb(&dp->dl_cb_fattr.ncf_getattr, dp->dl_stid.sc_client,
- &nfsd4_cb_getattr_ops, NFSPROC4_CLNT_CB_GETATTR);
- dp->dl_cb_fattr.ncf_file_modified = false;
get_nfs4_file(fp);
dp->dl_stid.sc_file = fp;
+ nfsd4_init_cb(&dp->dl_recall, dp->dl_stid.sc_client,
+ &nfsd4_cb_recall_ops, NFSPROC4_CLNT_CB_RECALL);
return dp;
out_dec:
atomic_long_dec(&num_delegations);
return NULL;
}
+static struct nfs4_delegation *
+alloc_init_deleg(struct nfs4_client *clp, struct nfs4_file *fp,
+ struct nfs4_clnt_odstate *odstate, u32 dl_type)
+{
+ struct nfs4_delegation *dp;
+
+ dprintk("NFSD alloc_init_deleg\n");
+ dp = __alloc_init_deleg(clp, fp, odstate, dl_type, nfs4_free_deleg);
+ if (!dp)
+ return NULL;
+
+ nfsd4_init_cb(&dp->dl_cb_fattr.ncf_getattr, dp->dl_stid.sc_client,
+ &nfsd4_cb_getattr_ops, NFSPROC4_CLNT_CB_GETATTR);
+ dp->dl_cb_fattr.ncf_file_modified = false;
+ return dp;
+}
+
+static void nfs4_free_dir_deleg(struct nfs4_stid *stid)
+{
+ struct nfs4_delegation *dp = delegstateid(stid);
+ struct nfsd4_cb_notify *ncn = &dp->dl_cb_notify;
+ int i;
+
+ for (i = 0; i < ncn->ncn_evt_cnt; ++i)
+ nfsd_notify_event_put(ncn->ncn_evt[i]);
+ release_pages(ncn->ncn_pages, NOTIFY4_PAGE_ARRAY_SIZE);
+ kfree(ncn->ncn_nf);
+ nfs4_free_deleg(stid);
+}
+
+static struct nfs4_delegation *
+alloc_init_dir_deleg(struct nfs4_client *clp, struct nfs4_file *fp)
+{
+ struct nfs4_delegation *dp;
+ struct nfsd4_cb_notify *ncn;
+ int npages;
+
+ dp = __alloc_init_deleg(clp, fp, NULL, NFS4_OPEN_DELEGATE_READ, nfs4_free_dir_deleg);
+ if (!dp)
+ return NULL;
+
+ ncn = &dp->dl_cb_notify;
+
+ npages = alloc_pages_bulk(GFP_KERNEL, NOTIFY4_PAGE_ARRAY_SIZE, ncn->ncn_pages);
+ if (npages != NOTIFY4_PAGE_ARRAY_SIZE) {
+ release_pages(ncn->ncn_pages, npages);
+ nfs4_free_dir_deleg(&dp->dl_stid);
+ }
+
+ ncn->ncn_nf = kcalloc(NOTIFY4_EVENT_QUEUE_SIZE, sizeof(*ncn->ncn_nf), GFP_KERNEL);
+ if (!ncn->ncn_nf) {
+ release_pages(ncn->ncn_pages, npages);
+ nfs4_free_dir_deleg(&dp->dl_stid);
+ return NULL;
+ }
+ spin_lock_init(&ncn->ncn_lock);
+ nfsd4_init_cb(&ncn->ncn_cb, dp->dl_stid.sc_client,
+ &nfsd4_cb_notify_ops, NFSPROC4_CLNT_CB_NOTIFY);
+ return dp;
+}
+
void
nfs4_put_stid(struct nfs4_stid *s)
{
@@ -3272,6 +3333,30 @@ nfsd4_cb_getattr_release(struct nfsd4_callback *cb)
nfs4_put_stid(&dp->dl_stid);
}
+static int
+nfsd4_cb_notify_done(struct nfsd4_callback *cb,
+ struct rpc_task *task)
+{
+ switch (task->tk_status) {
+ case -NFS4ERR_DELAY:
+ rpc_delay(task, 2 * HZ);
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+static void
+nfsd4_cb_notify_release(struct nfsd4_callback *cb)
+{
+ struct nfsd4_cb_notify *ncn =
+ container_of(cb, struct nfsd4_cb_notify, ncn_cb);
+ struct nfs4_delegation *dp =
+ container_of(ncn, struct nfs4_delegation, dl_cb_notify);
+
+ nfs4_put_stid(&dp->dl_stid);
+}
+
static const struct nfsd4_callback_ops nfsd4_cb_recall_any_ops = {
.done = nfsd4_cb_recall_any_done,
.release = nfsd4_cb_recall_any_release,
@@ -3284,6 +3369,12 @@ static const struct nfsd4_callback_ops nfsd4_cb_getattr_ops = {
.opcode = OP_CB_GETATTR,
};
+static const struct nfsd4_callback_ops nfsd4_cb_notify_ops = {
+ .done = nfsd4_cb_notify_done,
+ .release = nfsd4_cb_notify_release,
+ .opcode = OP_CB_NOTIFY,
+};
+
static void nfs4_cb_getattr(struct nfs4_cb_fattr *ncf)
{
struct nfs4_delegation *dp =
@@ -9514,7 +9605,7 @@ nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate,
/* Try to set up the lease */
status = -ENOMEM;
- dp = alloc_init_deleg(clp, fp, NULL, NFS4_OPEN_DELEGATE_READ);
+ dp = alloc_init_dir_deleg(clp, fp);
if (!dp)
goto out_delegees;
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 596d0bbf868c0ca2a31fa20f3ac61db66b60636d..507ce4c097c8b601eecb040876412fc2fe3033b2 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -196,6 +196,44 @@ struct nfs4_cb_fattr {
#define NOTIFY4_EVENT_QUEUE_SIZE 3
#define NOTIFY4_PAGE_ARRAY_SIZE 1
+struct nfsd_notify_event {
+ refcount_t ne_ref; // refcount
+ u32 ne_mask; // FS_* mask from fsnotify callback
+ struct dentry *ne_dentry; // dentry reference to target
+ u32 ne_namelen; // length of ne_name
+ char ne_name[]; // name of dentry being changed
+};
+
+static inline struct nfsd_notify_event *nfsd_notify_event_get(struct nfsd_notify_event *ne)
+{
+ refcount_inc(&ne->ne_ref);
+ return ne;
+}
+
+static inline void nfsd_notify_event_put(struct nfsd_notify_event *ne)
+{
+ if (refcount_dec_and_test(&ne->ne_ref)) {
+ dput(ne->ne_dentry);
+ kfree(ne);
+ }
+}
+
+/*
+ * Represents a directory delegation. The callback is for handling CB_NOTIFYs.
+ * As notifications from fsnotify come in, allocate a new event, take the ncn_lock,
+ * and add it to the ncn_evt queue. The CB_NOTIFY prepare handler will take the
+ * lock, clean out the list and process it.
+ */
+struct nfsd4_cb_notify {
+ spinlock_t ncn_lock; // protects the evt queue and count
+ int ncn_evt_cnt; // count of events in ncn_evt
+ int ncn_nf_cnt; // count of valid entries in ncn_nf
+ struct nfsd_notify_event *ncn_evt[NOTIFY4_EVENT_QUEUE_SIZE]; // list of events
+ struct page *ncn_pages[NOTIFY4_PAGE_ARRAY_SIZE]; // for encoding
+ struct notify4 *ncn_nf; // array of notify4's to be sent
+ struct nfsd4_callback ncn_cb; // notify4 callback
+};
+
/*
* Represents a delegation stateid. The nfs4_client holds references to these
* and they are put when it is being destroyed or when the delegation is
@@ -232,8 +270,12 @@ struct nfs4_delegation {
bool dl_written;
bool dl_setattr;
- /* for CB_GETATTR */
- struct nfs4_cb_fattr dl_cb_fattr;
+ union {
+ /* for CB_GETATTR */
+ struct nfs4_cb_fattr dl_cb_fattr;
+ /* for CB_NOTIFY */
+ struct nfsd4_cb_notify dl_cb_notify;
+ };
/* For delegated timestamps */
struct timespec64 dl_atime;
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 27/38] nfsd: add notification handlers for dir events
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (25 preceding siblings ...)
2025-09-24 18:06 ` [PATCH v3 26/38] nfsd: add data structures for handling CB_NOTIFY to directory delegation Jeff Layton
@ 2025-09-24 18:06 ` Jeff Layton
2025-09-25 17:15 ` Jeff Layton
2025-09-24 18:06 ` [PATCH v3 28/38] nfsd: add tracepoint to dir_event handler Jeff Layton
` (11 subsequent siblings)
38 siblings, 1 reply; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:06 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
Add the necessary parts to accept a fsnotify callback for directory
change event and create a CB_NOTIFY request for it. When a dir nfsd_file
is created set a handle_event callback to handle the notification.
Use that to allocate a nfsd_notify_event object and then hand off a
reference to each delegation's CB_NOTIFY. If anything fails along the
way, recall any affected delegations.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/nfsd/filecache.c | 51 ++++++++++----
fs/nfsd/nfs4callback.c | 19 +++--
fs/nfsd/nfs4state.c | 185 +++++++++++++++++++++++++++++++++++++++++++++++++
fs/nfsd/nfs4xdr.c | 95 +++++++++++++++++++++++++
fs/nfsd/state.h | 2 +
fs/nfsd/xdr4.h | 2 +
6 files changed, 337 insertions(+), 17 deletions(-)
diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c
index 0b9ee6c6baa89a306c88c56d8a7b80b5683c03e3..dffa67a6ed50905be99ffca73b0a38a69e96d1b9 100644
--- a/fs/nfsd/filecache.c
+++ b/fs/nfsd/filecache.c
@@ -72,6 +72,7 @@ static struct kmem_cache *nfsd_file_mark_slab;
static struct list_lru nfsd_file_lru;
static unsigned long nfsd_file_flags;
static struct fsnotify_group *nfsd_file_fsnotify_group;
+static struct fsnotify_group *nfsd_dir_fsnotify_group;
static struct delayed_work nfsd_filecache_laundrette;
static struct rhltable nfsd_file_rhltable
____cacheline_aligned_in_smp;
@@ -147,7 +148,7 @@ static void
nfsd_file_mark_put(struct nfsd_file_mark *nfm)
{
if (refcount_dec_and_test(&nfm->nfm_ref)) {
- fsnotify_destroy_mark(&nfm->nfm_mark, nfsd_file_fsnotify_group);
+ fsnotify_destroy_mark(&nfm->nfm_mark, nfm->nfm_mark.group);
fsnotify_put_mark(&nfm->nfm_mark);
}
}
@@ -155,35 +156,37 @@ nfsd_file_mark_put(struct nfsd_file_mark *nfm)
static struct nfsd_file_mark *
nfsd_file_mark_find_or_create(struct inode *inode)
{
- int err;
- struct fsnotify_mark *mark;
struct nfsd_file_mark *nfm = NULL, *new;
+ struct fsnotify_group *group;
+ struct fsnotify_mark *mark;
+ int err;
+
+ group = S_ISDIR(inode->i_mode) ? nfsd_dir_fsnotify_group : nfsd_file_fsnotify_group;
do {
- fsnotify_group_lock(nfsd_file_fsnotify_group);
- mark = fsnotify_find_inode_mark(inode,
- nfsd_file_fsnotify_group);
+ fsnotify_group_lock(group);
+ mark = fsnotify_find_inode_mark(inode, group);
if (mark) {
nfm = nfsd_file_mark_get(container_of(mark,
struct nfsd_file_mark,
nfm_mark));
- fsnotify_group_unlock(nfsd_file_fsnotify_group);
+ fsnotify_group_unlock(group);
if (nfm) {
fsnotify_put_mark(mark);
break;
}
/* Avoid soft lockup race with nfsd_file_mark_put() */
- fsnotify_destroy_mark(mark, nfsd_file_fsnotify_group);
+ fsnotify_destroy_mark(mark, group);
fsnotify_put_mark(mark);
} else {
- fsnotify_group_unlock(nfsd_file_fsnotify_group);
+ fsnotify_group_unlock(group);
}
/* allocate a new nfm */
new = kmem_cache_alloc(nfsd_file_mark_slab, GFP_KERNEL);
if (!new)
return NULL;
- fsnotify_init_mark(&new->nfm_mark, nfsd_file_fsnotify_group);
+ fsnotify_init_mark(&new->nfm_mark, group);
new->nfm_mark.mask = FS_ATTRIB|FS_DELETE_SELF;
refcount_set(&new->nfm_ref, 1);
@@ -763,12 +766,25 @@ nfsd_file_fsnotify_handle_event(struct fsnotify_mark *mark, u32 mask,
return 0;
}
+static int
+nfsd_dir_fsnotify_handle_event(struct fsnotify_group *group, u32 mask,
+ const void *data, int data_type, struct inode *dir,
+ const struct qstr *name, u32 cookie,
+ struct fsnotify_iter_info *iter_info)
+{
+ return nfsd_handle_dir_event(mask, dir, data, data_type, name);
+}
static const struct fsnotify_ops nfsd_file_fsnotify_ops = {
.handle_inode_event = nfsd_file_fsnotify_handle_event,
.free_mark = nfsd_file_mark_free,
};
+static const struct fsnotify_ops nfsd_dir_fsnotify_ops = {
+ .handle_event = nfsd_dir_fsnotify_handle_event,
+ .free_mark = nfsd_file_mark_free,
+};
+
int
nfsd_file_cache_init(void)
{
@@ -820,8 +836,7 @@ nfsd_file_cache_init(void)
goto out_shrinker;
}
- nfsd_file_fsnotify_group = fsnotify_alloc_group(&nfsd_file_fsnotify_ops,
- 0);
+ nfsd_file_fsnotify_group = fsnotify_alloc_group(&nfsd_file_fsnotify_ops, 0);
if (IS_ERR(nfsd_file_fsnotify_group)) {
pr_err("nfsd: unable to create fsnotify group: %ld\n",
PTR_ERR(nfsd_file_fsnotify_group));
@@ -830,11 +845,23 @@ nfsd_file_cache_init(void)
goto out_notifier;
}
+ nfsd_dir_fsnotify_group = fsnotify_alloc_group(&nfsd_dir_fsnotify_ops, 0);
+ if (IS_ERR(nfsd_dir_fsnotify_group)) {
+ pr_err("nfsd: unable to create fsnotify group: %ld\n",
+ PTR_ERR(nfsd_dir_fsnotify_group));
+ ret = PTR_ERR(nfsd_dir_fsnotify_group);
+ nfsd_dir_fsnotify_group = NULL;
+ goto out_notify_group;
+ }
+
INIT_DELAYED_WORK(&nfsd_filecache_laundrette, nfsd_file_gc_worker);
out:
if (ret)
clear_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags);
return ret;
+out_notify_group:
+ fsnotify_put_group(nfsd_file_fsnotify_group);
+ nfsd_file_fsnotify_group = NULL;
out_notifier:
lease_unregister_notifier(&nfsd_file_lease_notifier);
out_shrinker:
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index fe7b20b94d76efd309e27c1a3ef359e7101dac80..ee85ff54895d46fd25e73b5f96f3b467eff41286 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -870,21 +870,30 @@ static void nfs4_xdr_enc_cb_notify(struct rpc_rqst *req,
const void *data)
{
const struct nfsd4_callback *cb = data;
+ struct nfsd4_cb_notify *ncn = container_of(cb, struct nfsd4_cb_notify, ncn_cb);
+ struct nfs4_delegation *dp = container_of(ncn, struct nfs4_delegation, dl_cb_notify);
struct nfs4_cb_compound_hdr hdr = {
.ident = 0,
.minorversion = cb->cb_clp->cl_minorversion,
};
- struct CB_NOTIFY4args args = { };
+ struct CB_NOTIFY4args args;
+ __be32 *p;
WARN_ON_ONCE(hdr.minorversion == 0);
encode_cb_compound4args(xdr, &hdr);
encode_cb_sequence4args(xdr, cb, &hdr);
- /*
- * FIXME: get stateid and fh from delegation. Inline the cna_changes
- * buffer, and zero it.
- */
+ p = xdr_reserve_space(xdr, 4);
+ *p = cpu_to_be32(OP_CB_NOTIFY);
+
+ args.cna_stateid.seqid = dp->dl_stid.sc_stateid.si_generation;
+ memcpy(&args.cna_stateid.other, &dp->dl_stid.sc_stateid.si_opaque,
+ ARRAY_SIZE(args.cna_stateid.other));
+ args.cna_fh.len = dp->dl_stid.sc_file->fi_fhandle.fh_size;
+ args.cna_fh.data = dp->dl_stid.sc_file->fi_fhandle.fh_raw;
+ args.cna_changes.count = ncn->ncn_nf_cnt;
+ args.cna_changes.element = ncn->ncn_nf;
WARN_ON_ONCE(!xdrgen_encode_CB_NOTIFY4args(xdr, &args));
hdr.nops++;
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 5d3af33e70e26e59f8bc3d5b44c82beafb4f786b..f3d3e3faf7d5f1b2ee39abb7eabd2fa406bbac21 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -55,6 +55,7 @@
#include "netns.h"
#include "pnfs.h"
#include "filecache.h"
+#include "nfs4xdr_gen.h"
#include "trace.h"
#define NFSDDBG_FACILITY NFSDDBG_PROC
@@ -3333,15 +3334,92 @@ nfsd4_cb_getattr_release(struct nfsd4_callback *cb)
nfs4_put_stid(&dp->dl_stid);
}
+static bool
+nfsd4_cb_notify_prepare(struct nfsd4_callback *cb)
+{
+ struct nfsd4_cb_notify *ncn = container_of(cb, struct nfsd4_cb_notify, ncn_cb);
+ struct nfs4_delegation *dp = container_of(ncn, struct nfs4_delegation, dl_cb_notify);
+ struct nfsd_notify_event *events[NOTIFY4_EVENT_QUEUE_SIZE];
+ struct xdr_buf xdr = { .buflen = PAGE_SIZE * NOTIFY4_PAGE_ARRAY_SIZE,
+ .pages = ncn->ncn_pages };
+ struct xdr_stream stream;
+ int count, i;
+ bool error = false;
+
+ xdr_init_encode_pages(&stream, &xdr);
+
+ spin_lock(&ncn->ncn_lock);
+ count = ncn->ncn_evt_cnt;
+
+ /* spurious queueing? */
+ if (count == 0) {
+ spin_unlock(&ncn->ncn_lock);
+ return false;
+ }
+
+ /* we can't keep up! */
+ if (count > NOTIFY4_EVENT_QUEUE_SIZE) {
+ spin_unlock(&ncn->ncn_lock);
+ goto out_recall;
+ }
+
+ memcpy(events, ncn->ncn_evt, sizeof(*events) * count);
+ ncn->ncn_evt_cnt = 0;
+ spin_unlock(&ncn->ncn_lock);
+
+ for (i = 0; i < count; ++i) {
+ struct nfsd_notify_event *nne = events[i];
+
+ if (!error) {
+ u32 *maskp = (u32 *)xdr_reserve_space(&stream, sizeof(*maskp));
+ u8 *p;
+
+ if (!maskp) {
+ error = true;
+ goto put_event;
+ }
+
+ p = nfsd4_encode_notify_event(&stream, nne, dp, maskp);
+ if (!p) {
+ pr_notice("Count not generate CB_NOTIFY from fsnotify mask 0x%x\n",
+ nne->ne_mask);
+ error = true;
+ goto put_event;
+ }
+
+ ncn->ncn_nf[i].notify_mask.count = 1;
+ ncn->ncn_nf[i].notify_mask.element = maskp;
+ ncn->ncn_nf[i].notify_vals.data = p;
+ ncn->ncn_nf[i].notify_vals.len = (u8 *)stream.p - p;
+ }
+put_event:
+ nfsd_notify_event_put(nne);
+ }
+ if (!error) {
+ ncn->ncn_nf_cnt = count;
+ return true;
+ }
+out_recall:
+ nfsd4_run_cb(&dp->dl_recall);
+ return false;
+}
+
static int
nfsd4_cb_notify_done(struct nfsd4_callback *cb,
struct rpc_task *task)
{
+ struct nfsd4_cb_notify *ncn = container_of(cb, struct nfsd4_cb_notify, ncn_cb);
+ struct nfs4_delegation *dp = container_of(ncn, struct nfs4_delegation, dl_cb_notify);
+
switch (task->tk_status) {
case -NFS4ERR_DELAY:
rpc_delay(task, 2 * HZ);
return 0;
default:
+ /* For any other hard error, recall the deleg */
+ nfsd4_run_cb(&dp->dl_recall);
+ fallthrough;
+ case 0:
return 1;
}
}
@@ -3370,6 +3448,7 @@ static const struct nfsd4_callback_ops nfsd4_cb_getattr_ops = {
};
static const struct nfsd4_callback_ops nfsd4_cb_notify_ops = {
+ .prepare = nfsd4_cb_notify_prepare,
.done = nfsd4_cb_notify_done,
.release = nfsd4_cb_notify_release,
.opcode = OP_CB_NOTIFY,
@@ -9645,3 +9724,109 @@ nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate,
put_deleg_file(fp);
return ERR_PTR(status);
}
+
+static void
+nfsd4_run_cb_notify(struct nfsd4_cb_notify *ncn)
+{
+ struct nfs4_delegation *dp = container_of(ncn, struct nfs4_delegation, dl_cb_notify);
+
+ if (test_and_set_bit(NFSD4_CALLBACK_RUNNING, &ncn->ncn_cb.cb_flags))
+ return;
+
+ if (!refcount_inc_not_zero(&dp->dl_stid.sc_count))
+ clear_bit(NFSD4_CALLBACK_RUNNING, &ncn->ncn_cb.cb_flags);
+ else
+ nfsd4_run_cb(&ncn->ncn_cb);
+}
+
+static struct nfsd_notify_event *
+alloc_nfsd_notify_event(u32 mask, const struct qstr *q, struct dentry *dentry)
+{
+ struct nfsd_notify_event *ne;
+
+ ne = kmalloc(sizeof(*ne) + q->len + 1, GFP_KERNEL);
+ if (!ne)
+ return NULL;
+
+ memcpy(&ne->ne_name, q->name, q->len);
+ refcount_set(&ne->ne_ref, 1);
+ ne->ne_mask = mask;
+ ne->ne_name[q->len + 1] = '\0';
+ ne->ne_namelen = q->len;
+ ne->ne_dentry = dget(dentry);
+ return ne;
+}
+
+static bool
+should_notify_deleg(u32 mask, struct file_lease *fl)
+{
+ /* Only nfsd leases */
+ if (fl->fl_lmops != &nfsd_dir_lease_mng_ops)
+ return false;
+
+ /* Skip if this event wasn't ignored by the lease */
+ if ((mask & FS_DELETE) && !(fl->c.flc_flags & FL_IGN_DIR_DELETE))
+ return false;
+
+ return true;
+}
+
+static void
+nfsd_recall_all_dir_delegs(const struct inode *dir)
+{
+ struct file_lock_context *ctx = locks_inode_context(dir);
+ struct file_lock_core *flc;
+
+ spin_lock(&ctx->flc_lock);
+ list_for_each_entry(flc, &ctx->flc_lease, flc_list) {
+ struct file_lease *fl = container_of(flc, struct file_lease, c);
+
+ if (fl->fl_lmops == &nfsd_dir_lease_mng_ops)
+ nfsd_break_deleg_cb(fl);
+ }
+ spin_unlock(&ctx->flc_lock);
+}
+
+int
+nfsd_handle_dir_event(u32 mask, const struct inode *dir, const void *data,
+ int data_type, const struct qstr *name)
+{
+ struct dentry *dentry = fsnotify_data_dentry(data, data_type);
+ struct file_lock_context *ctx;
+ struct file_lock_core *flc;
+ struct nfsd_notify_event *evt;
+
+ ctx = locks_inode_context(dir);
+ if (!ctx || list_empty(&ctx->flc_lease))
+ return 0;
+
+ evt = alloc_nfsd_notify_event(mask, name, dentry);
+ if (!evt) {
+ nfsd_recall_all_dir_delegs(dir);
+ return 0;
+ }
+
+ spin_lock(&ctx->flc_lock);
+ list_for_each_entry(flc, &ctx->flc_lease, flc_list) {
+ struct file_lease *fl = container_of(flc, struct file_lease, c);
+ struct nfs4_delegation *dp = flc->flc_owner;
+ struct nfsd4_cb_notify *ncn = &dp->dl_cb_notify;
+
+ if (!should_notify_deleg(mask, fl))
+ continue;
+
+ spin_lock(&ncn->ncn_lock);
+ if (ncn->ncn_evt_cnt >= NOTIFY4_EVENT_QUEUE_SIZE) {
+ /* We're generating notifications too fast. Recall. */
+ spin_unlock(&ncn->ncn_lock);
+ nfsd_break_deleg_cb(fl);
+ continue;
+ }
+ ncn->ncn_evt[ncn->ncn_evt_cnt++] = nfsd_notify_event_get(evt);
+ spin_unlock(&ncn->ncn_lock);
+
+ nfsd4_run_cb_notify(ncn);
+ }
+ spin_unlock(&ctx->flc_lock);
+ return 0;
+}
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 97e9e9afa80af50a5f6eda19a6eb2cb3cf013f32..4a40d5e07fa3343a9b645c3b267897a31491e8e9 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3752,6 +3752,101 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
goto out;
}
+static bool
+nfsd4_setup_notify_entry4(struct notify_entry4 *ne, struct xdr_stream *xdr,
+ struct dentry *dentry, struct nfs4_delegation *dp,
+ char *name, u32 namelen)
+{
+ uint32_t *attrmask;
+
+ /* Reserve space for attrmask */
+ attrmask = xdr_reserve_space(xdr, 3 * sizeof(uint32_t));
+ if (!attrmask)
+ return false;
+
+ ne->ne_file.data = name;
+ ne->ne_file.len = namelen;
+ ne->ne_attrs.attrmask.element = attrmask;
+
+ attrmask[0] = 0;
+ attrmask[1] = 0;
+ attrmask[2] = 0;
+ ne->ne_attrs.attr_vals.data = NULL;
+ ne->ne_attrs.attr_vals.len = 0;
+ ne->ne_attrs.attrmask.count = 1;
+ return true;
+}
+
+/**
+ * nfsd4_encode_notify_event - encode a notify
+ * @xdr: stream to which to encode the fattr4
+ * @nne: nfsd_notify_event to encode
+ * @dp: delegation where the event occurred
+ * @notify_mask: pointer to word where notification mask should be set
+ *
+ * Encode @nne into @xdr. Returns a pointer to the start of the event, or NULL if
+ * the event couldn't be encoded. The appropriate bit in the notify_mask will also
+ * be set on success.
+ */
+u8 *nfsd4_encode_notify_event(struct xdr_stream *xdr, struct nfsd_notify_event *nne,
+ struct nfs4_delegation *dp, u32 *notify_mask)
+{
+ u8 *p = NULL;
+
+ *notify_mask = 0;
+
+ if (nne->ne_mask & FS_DELETE) {
+ struct notify_remove4 nr = { };
+
+ if (!nfsd4_setup_notify_entry4(&nr.nrm_old_entry, xdr, nne->ne_dentry, dp,
+ nne->ne_name, nne->ne_namelen))
+ goto out_err;
+ p = (u8 *)xdr->p;
+ if (!xdrgen_encode_notify_remove4(xdr, &nr))
+ goto out_err;
+ *notify_mask |= BIT(NOTIFY4_REMOVE_ENTRY);
+ } else if (nne->ne_mask & FS_CREATE) {
+ struct notify_add4 na = { };
+
+ if (!nfsd4_setup_notify_entry4(&na.nad_new_entry, xdr, nne->ne_dentry, dp,
+ nne->ne_name, nne->ne_namelen))
+ goto out_err;
+
+ p = (u8 *)xdr->p;
+ if (!xdrgen_encode_notify_add4(xdr, &na))
+ goto out_err;
+
+ *notify_mask |= BIT(NOTIFY4_ADD_ENTRY);
+ } else if (nne->ne_mask & FS_RENAME) {
+ struct notify_rename4 nr = { };
+ struct name_snapshot n;
+ bool ret;
+
+ /* Don't send any attributes in the old_entry since they're the same in new */
+ if (!nfsd4_setup_notify_entry4(&nr.nrn_old_entry.nrm_old_entry, xdr,
+ NULL, dp, nne->ne_name,
+ nne->ne_namelen))
+ goto out_err;
+
+ take_dentry_name_snapshot(&n, nne->ne_dentry);
+ ret = nfsd4_setup_notify_entry4(&nr.nrn_new_entry.nad_new_entry, xdr,
+ nne->ne_dentry, dp, (char *)n.name.name,
+ n.name.len);
+ if (ret) {
+ p = (u8 *)xdr->p;
+ ret = xdrgen_encode_notify_rename4(xdr, &nr);
+ }
+ release_dentry_name_snapshot(&n);
+ if (!ret)
+ goto out_err;
+ *notify_mask |= BIT(NOTIFY4_RENAME_ENTRY);
+ }
+ return p;
+out_err:
+ pr_warn("nfsd: unable to marshal notify_rename4 to xdr stream\n");
+ return NULL;
+}
+
static void svcxdr_init_encode_from_buffer(struct xdr_stream *xdr,
struct xdr_buf *buf, __be32 *p, int bytes)
{
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 507ce4c097c8b601eecb040876412fc2fe3033b2..232b64e1d7721d3074364ff788b4f72b02a6c63f 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -877,6 +877,8 @@ bool nfsd4_has_active_async_copies(struct nfs4_client *clp);
extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(struct xdr_netobj name,
struct xdr_netobj princhash, struct nfsd_net *nn);
extern bool nfs4_has_reclaimed_state(struct xdr_netobj name, struct nfsd_net *nn);
+int nfsd_handle_dir_event(u32 mask, const struct inode *dir, const void *data,
+ int data_type, const struct qstr *name);
void put_nfs4_file(struct nfs4_file *fi);
extern void nfs4_put_cpntf_state(struct nfsd_net *nn,
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index d4b48602b2b0c3854473e8a5812652815ab26c12..19f468b5bc54343dca928f0b8286c868f2133241 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -967,6 +967,8 @@ __be32 nfsd4_encode_fattr_to_buf(__be32 **p, int words,
struct svc_fh *fhp, struct svc_export *exp,
struct dentry *dentry,
u32 *bmval, struct svc_rqst *, int ignore_crossmnt);
+u8 *nfsd4_encode_notify_event(struct xdr_stream *xdr, struct nfsd_notify_event *nne,
+ struct nfs4_delegation *dd, u32 *notify_mask);
extern __be32 nfsd4_setclientid(struct svc_rqst *rqstp,
struct nfsd4_compound_state *, union nfsd4_op_u *u);
extern __be32 nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 28/38] nfsd: add tracepoint to dir_event handler
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (26 preceding siblings ...)
2025-09-24 18:06 ` [PATCH v3 27/38] nfsd: add notification handlers for dir events Jeff Layton
@ 2025-09-24 18:06 ` Jeff Layton
2025-09-24 18:06 ` [PATCH v3 29/38] nfsd: apply the notify mask to the delegation when requested Jeff Layton
` (10 subsequent siblings)
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:06 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
Add some extra visibility around the fsnotify handlers.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/nfsd/nfs4state.c | 2 ++
fs/nfsd/trace.h | 20 ++++++++++++++++++++
2 files changed, 22 insertions(+)
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index f3d3e3faf7d5f1b2ee39abb7eabd2fa406bbac21..2147b5f80ca0e650e9dbdb63de7c9af6f4bc7bff 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -9796,6 +9796,8 @@ nfsd_handle_dir_event(u32 mask, const struct inode *dir, const void *data,
struct file_lock_core *flc;
struct nfsd_notify_event *evt;
+ trace_nfsd_file_fsnotify_handle_dir_event(mask, dir, name);
+
ctx = locks_inode_context(dir);
if (!ctx || list_empty(&ctx->flc_lease))
return 0;
diff --git a/fs/nfsd/trace.h b/fs/nfsd/trace.h
index 01380425347b946bbf5a70a2ba10190ae4ec6303..d8b6d87f1ec09ca80fe4218fd79aaa803a316619 100644
--- a/fs/nfsd/trace.h
+++ b/fs/nfsd/trace.h
@@ -1311,6 +1311,26 @@ TRACE_EVENT(nfsd_file_fsnotify_handle_event,
__entry->nlink, __entry->mode, __entry->mask)
);
+TRACE_EVENT(nfsd_file_fsnotify_handle_dir_event,
+ TP_PROTO(u32 mask, const struct inode *dir, const struct qstr *name),
+ TP_ARGS(mask, dir, name),
+ TP_STRUCT__entry(
+ __field(u32, mask)
+ __field(dev_t, s_dev)
+ __field(ino_t, i_ino)
+ __string_len(name, name->name, name->len)
+ ),
+ TP_fast_assign(
+ __entry->mask = mask;
+ __entry->s_dev = dir->i_sb->s_dev;
+ __entry->i_ino = dir->i_ino;
+ __assign_str(name);
+ ),
+ TP_printk("inode=0x%x:0x%x:0x%lx mask=0x%x name=%s",
+ MAJOR(__entry->s_dev), MINOR(__entry->s_dev),
+ __entry->i_ino, __entry->mask, __get_str(name))
+);
+
DECLARE_EVENT_CLASS(nfsd_file_gc_class,
TP_PROTO(
const struct nfsd_file *nf
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 29/38] nfsd: apply the notify mask to the delegation when requested
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (27 preceding siblings ...)
2025-09-24 18:06 ` [PATCH v3 28/38] nfsd: add tracepoint to dir_event handler Jeff Layton
@ 2025-09-24 18:06 ` Jeff Layton
2025-09-24 18:06 ` [PATCH v3 30/38] nfsd: add helper to marshal a fattr4 from completed args Jeff Layton
` (9 subsequent siblings)
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:06 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
If the client requests a directory delegation with notifications
enabled, set the appropriate return mask in gddr_notification[0]. This
will ensure the lease acquisition sets the appropriate ignore mask.
If the client doesn't set NOTIFY4_GFLAG_EXTEND, then don't offer any
notifications, as nfsd won't provide directory offset information, and
"classic" notifications require them.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/nfsd/nfs4proc.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 277022d437cec18e527c836f108f0e97c6844b23..220fc873db8f08a90ac74e51ac9d931fe7edb9e4 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -2335,12 +2335,18 @@ nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
return status == nfserr_same ? nfs_ok : status;
}
+#define SUPPORTED_NOTIFY_MASK (BIT(NOTIFY4_REMOVE_ENTRY) | \
+ BIT(NOTIFY4_ADD_ENTRY) | \
+ BIT(NOTIFY4_RENAME_ENTRY) | \
+ BIT(NOTIFY4_GFLAG_EXTEND))
+
static __be32
nfsd4_get_dir_delegation(struct svc_rqst *rqstp,
struct nfsd4_compound_state *cstate,
union nfsd4_op_u *u)
{
struct nfsd4_get_dir_delegation *gdd = &u->get_dir_delegation;
+ u32 requested = gdd->gdda_notification_types[0];
struct nfs4_delegation *dd;
struct nfsd_file *nf;
__be32 status;
@@ -2349,6 +2355,12 @@ nfsd4_get_dir_delegation(struct svc_rqst *rqstp,
if (status != nfs_ok)
return status;
+ /* No notifications if you don't set NOTIFY4_GFLAG_EXTEND! */
+ if (!(requested & BIT(NOTIFY4_GFLAG_EXTEND)))
+ requested = 0;
+
+ gdd->gddr_notification[0] = requested & SUPPORTED_NOTIFY_MASK;
+
/*
* RFC 8881, section 18.39.3 says:
*
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 30/38] nfsd: add helper to marshal a fattr4 from completed args
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (28 preceding siblings ...)
2025-09-24 18:06 ` [PATCH v3 29/38] nfsd: apply the notify mask to the delegation when requested Jeff Layton
@ 2025-09-24 18:06 ` Jeff Layton
2025-09-24 18:06 ` [PATCH v3 31/38] nfsd: allow nfsd4_encode_fattr4_change() to work with no export Jeff Layton
` (8 subsequent siblings)
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:06 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
Break the loop that encodes the actual attr_vals field into a separate
function.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/nfsd/nfs4xdr.c | 46 ++++++++++++++++++++++++++--------------------
1 file changed, 26 insertions(+), 20 deletions(-)
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 4a40d5e07fa3343a9b645c3b267897a31491e8e9..9a463f9c8a67704d90d1551b7de59e4e89a2a81d 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3565,6 +3565,22 @@ static const nfsd4_enc_attr nfsd4_enc_fattr4_encode_ops[] = {
[FATTR4_OPEN_ARGUMENTS] = nfsd4_encode_fattr4_open_arguments,
};
+static __be32
+nfsd4_encode_attr_vals(struct xdr_stream *xdr, u32 *attrmask, struct nfsd4_fattr_args *args)
+{
+ DECLARE_BITMAP(attr_bitmap, ARRAY_SIZE(nfsd4_enc_fattr4_encode_ops));
+ unsigned long bit;
+ __be32 status;
+
+ bitmap_from_arr32(attr_bitmap, attrmask, ARRAY_SIZE(nfsd4_enc_fattr4_encode_ops));
+ for_each_set_bit(bit, attr_bitmap, ARRAY_SIZE(nfsd4_enc_fattr4_encode_ops)) {
+ status = nfsd4_enc_fattr4_encode_ops[bit](xdr, args);
+ if (status != nfs_ok)
+ return status;
+ }
+ return nfs_ok;
+}
+
/*
* Note: @fhp can be NULL; in this case, we might have to compose the filehandle
* ourselves.
@@ -3575,7 +3591,6 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
struct dentry *dentry, const u32 *bmval,
int ignore_crossmnt)
{
- DECLARE_BITMAP(attr_bitmap, ARRAY_SIZE(nfsd4_enc_fattr4_encode_ops));
struct nfs4_delegation *dp = NULL;
struct nfsd4_fattr_args args;
struct svc_fh *tempfh = NULL;
@@ -3590,7 +3605,6 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
.mnt = exp->ex_path.mnt,
.dentry = dentry,
};
- unsigned long bit;
WARN_ON_ONCE(bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1);
WARN_ON_ONCE(!nfsd_attrs_supported(minorversion, bmval));
@@ -3710,27 +3724,22 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
#endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
/* attrmask */
- status = nfsd4_encode_bitmap4(xdr, attrmask[0], attrmask[1],
- attrmask[2]);
+ status = nfsd4_encode_bitmap4(xdr, attrmask[0], attrmask[1], attrmask[2]);
if (status)
- goto out;
+ return status;
/* attr_vals */
attrlen_offset = xdr->buf->len;
- if (unlikely(!xdr_reserve_space(xdr, XDR_UNIT)))
- goto out_resource;
- bitmap_from_arr32(attr_bitmap, attrmask,
- ARRAY_SIZE(nfsd4_enc_fattr4_encode_ops));
- for_each_set_bit(bit, attr_bitmap,
- ARRAY_SIZE(nfsd4_enc_fattr4_encode_ops)) {
- status = nfsd4_enc_fattr4_encode_ops[bit](xdr, &args);
- if (status != nfs_ok)
- goto out;
+ if (unlikely(!xdr_reserve_space(xdr, XDR_UNIT))) {
+ status = nfserr_resource;
+ goto out;
}
- attrlen = cpu_to_be32(xdr->buf->len - attrlen_offset - XDR_UNIT);
- write_bytes_to_xdr_buf(xdr->buf, attrlen_offset, &attrlen, XDR_UNIT);
- status = nfs_ok;
+ status = nfsd4_encode_attr_vals(xdr, attrmask, &args);
+ if (status == nfs_ok) {
+ attrlen = cpu_to_be32(xdr->buf->len - attrlen_offset - XDR_UNIT);
+ write_bytes_to_xdr_buf(xdr->buf, attrlen_offset, &attrlen, XDR_UNIT);
+ }
out:
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
if (args.context.context)
@@ -3747,9 +3756,6 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
out_nfserr:
status = nfserrno(err);
goto out;
-out_resource:
- status = nfserr_resource;
- goto out;
}
static bool
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 31/38] nfsd: allow nfsd4_encode_fattr4_change() to work with no export
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (29 preceding siblings ...)
2025-09-24 18:06 ` [PATCH v3 30/38] nfsd: add helper to marshal a fattr4 from completed args Jeff Layton
@ 2025-09-24 18:06 ` Jeff Layton
2025-09-24 18:06 ` [PATCH v3 32/38] nfsd: send basic file attributes in CB_NOTIFY Jeff Layton
` (7 subsequent siblings)
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:06 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
In the context of a CB_NOTIFY callback, we may not have easy access to
a svc_export. nfsd will not currently grant a delegation on a the V4 root
however, so this should be safe.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/nfsd/nfs4xdr.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 9a463f9c8a67704d90d1551b7de59e4e89a2a81d..d1eba4bd8be243022f89f6ac58067c5469a8feba 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3027,7 +3027,7 @@ static __be32 nfsd4_encode_fattr4_change(struct xdr_stream *xdr,
{
const struct svc_export *exp = args->exp;
- if (unlikely(exp->ex_flags & NFSEXP_V4ROOT)) {
+ if (exp && unlikely(exp->ex_flags & NFSEXP_V4ROOT)) {
u32 flush_time = convert_to_wallclock(exp->cd->flush_time);
if (xdr_stream_encode_u32(xdr, flush_time) != XDR_UNIT)
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 32/38] nfsd: send basic file attributes in CB_NOTIFY
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (30 preceding siblings ...)
2025-09-24 18:06 ` [PATCH v3 31/38] nfsd: allow nfsd4_encode_fattr4_change() to work with no export Jeff Layton
@ 2025-09-24 18:06 ` Jeff Layton
2025-09-24 18:06 ` [PATCH v3 33/38] nfsd: allow encoding a filehandle into fattr4 without a svc_fh Jeff Layton
` (6 subsequent siblings)
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:06 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
In addition to the filename, send attributes about the inode in a
CB_NOTIFY event. This patch just adds a the basic inode information that
can be acquired via GETATTR.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/nfsd/nfs4xdr.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index d1eba4bd8be243022f89f6ac58067c5469a8feba..1db2bd974bfa9d899f590f4b4e869115ab73aff6 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3758,12 +3758,22 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
goto out;
}
+#define CB_NOTIFY_STATX_REQUEST_MASK (STATX_BASIC_STATS | \
+ STATX_BTIME | \
+ STATX_CHANGE_COOKIE)
+
static bool
nfsd4_setup_notify_entry4(struct notify_entry4 *ne, struct xdr_stream *xdr,
struct dentry *dentry, struct nfs4_delegation *dp,
char *name, u32 namelen)
{
+ struct nfs4_file *fi = dp->dl_stid.sc_file;
+ struct path path = { .mnt = fi->fi_deleg_file->nf_file->f_path.mnt,
+ .dentry = dentry };
+ struct nfsd4_fattr_args args = { };
uint32_t *attrmask;
+ __be32 status;
+ int ret;
/* Reserve space for attrmask */
attrmask = xdr_reserve_space(xdr, 3 * sizeof(uint32_t));
@@ -3774,6 +3784,41 @@ nfsd4_setup_notify_entry4(struct notify_entry4 *ne, struct xdr_stream *xdr,
ne->ne_file.len = namelen;
ne->ne_attrs.attrmask.element = attrmask;
+ /* FIXME: d_find_alias for inode ? */
+ if (!path.dentry || !d_inode(path.dentry))
+ goto noattrs;
+
+ /*
+ * It is possible that the client was granted a delegation when a file
+ * was created. Note that we don't issue a CB_GETATTR here since stale
+ * attributes are presumably ok.
+ */
+ ret = vfs_getattr(&path, &args.stat, CB_NOTIFY_STATX_REQUEST_MASK, AT_STATX_SYNC_AS_STAT);
+ if (ret)
+ goto noattrs;
+
+ args.change_attr = nfsd4_change_attribute(&args.stat);
+
+ attrmask[0] = FATTR4_WORD0_TYPE | FATTR4_WORD0_CHANGE |
+ FATTR4_WORD0_SIZE | FATTR4_WORD0_FILEID;
+ attrmask[1] = FATTR4_WORD1_MODE | FATTR4_WORD1_NUMLINKS | FATTR4_WORD1_RAWDEV |
+ FATTR4_WORD1_SPACE_USED | FATTR4_WORD1_TIME_ACCESS |
+ FATTR4_WORD1_TIME_METADATA | FATTR4_WORD1_TIME_MODIFY;
+ attrmask[2] = 0;
+
+ if (args.stat.result_mask & STATX_BTIME)
+ attrmask[1] |= FATTR4_WORD1_TIME_CREATE;
+
+ ne->ne_attrs.attrmask.count = 2;
+ ne->ne_attrs.attr_vals.data = (u8 *)xdr->p;
+
+ status = nfsd4_encode_attr_vals(xdr, attrmask, &args);
+ if (status != nfs_ok)
+ goto noattrs;
+
+ ne->ne_attrs.attr_vals.len = (u8 *)xdr->p - ne->ne_attrs.attr_vals.data;
+ return true;
+noattrs:
attrmask[0] = 0;
attrmask[1] = 0;
attrmask[2] = 0;
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 33/38] nfsd: allow encoding a filehandle into fattr4 without a svc_fh
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (31 preceding siblings ...)
2025-09-24 18:06 ` [PATCH v3 32/38] nfsd: send basic file attributes in CB_NOTIFY Jeff Layton
@ 2025-09-24 18:06 ` Jeff Layton
2025-09-24 18:06 ` [PATCH v3 34/38] nfsd: add a fi_connectable flag to struct nfs4_file Jeff Layton
` (5 subsequent siblings)
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:06 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
The current fattr4 encoder requires a svc_fh in order to encode the
filehandle. This is not available in a CB_NOTIFY callback. Add a a new
"fhandle" field to struct nfsd4_fattr_args and copy the filehandle into
there from the svc_fh. CB_NOTIFY will populate it via other means.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/nfsd/nfs4xdr.c | 35 ++++++++++++++++++++---------------
1 file changed, 20 insertions(+), 15 deletions(-)
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 1db2bd974bfa9d899f590f4b4e869115ab73aff6..822a9d47dd88df579e4f57f04dd6737653f71c94 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -2561,7 +2561,7 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
}
static __be32 nfsd4_encode_nfs_fh4(struct xdr_stream *xdr,
- struct knfsd_fh *fh_handle)
+ const struct knfsd_fh *fh_handle)
{
return nfsd4_encode_opaque(xdr, fh_handle->fh_raw, fh_handle->fh_size);
}
@@ -2924,6 +2924,7 @@ struct nfsd4_fattr_args {
struct svc_fh *fhp;
struct svc_export *exp;
struct dentry *dentry;
+ struct knfsd_fh fhandle;
struct kstat stat;
struct kstatfs statfs;
struct nfs4_acl *acl;
@@ -3129,7 +3130,7 @@ static __be32 nfsd4_encode_fattr4_acl(struct xdr_stream *xdr,
static __be32 nfsd4_encode_fattr4_filehandle(struct xdr_stream *xdr,
const struct nfsd4_fattr_args *args)
{
- return nfsd4_encode_nfs_fh4(xdr, &args->fhp->fh_handle);
+ return nfsd4_encode_nfs_fh4(xdr, &args->fhandle);
}
static __be32 nfsd4_encode_fattr4_fileid(struct xdr_stream *xdr,
@@ -3678,19 +3679,23 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
if (err)
goto out_nfserr;
}
- if ((attrmask[0] & (FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FSID)) &&
- !fhp) {
- tempfh = kmalloc(sizeof(struct svc_fh), GFP_KERNEL);
- status = nfserr_jukebox;
- if (!tempfh)
- goto out;
- fh_init(tempfh, NFS4_FHSIZE);
- status = fh_compose(tempfh, exp, dentry, NULL);
- if (status)
- goto out;
- args.fhp = tempfh;
- } else
- args.fhp = fhp;
+
+ args.fhp = fhp;
+ if ((attrmask[0] & (FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FSID))) {
+ if (!args.fhp) {
+ tempfh = kmalloc(sizeof(struct svc_fh), GFP_KERNEL);
+ status = nfserr_jukebox;
+ if (!tempfh)
+ goto out;
+ fh_init(tempfh, NFS4_FHSIZE);
+ status = fh_compose(tempfh, exp, dentry, NULL);
+ if (status)
+ goto out;
+ args.fhp = tempfh;
+ }
+ if (args.fhp)
+ fh_copy_shallow(&args.fhandle, &args.fhp->fh_handle);
+ }
if (attrmask[0] & FATTR4_WORD0_ACL) {
err = nfsd4_get_nfs4_acl(rqstp, dentry, &args.acl);
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 34/38] nfsd: add a fi_connectable flag to struct nfs4_file
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (32 preceding siblings ...)
2025-09-24 18:06 ` [PATCH v3 33/38] nfsd: allow encoding a filehandle into fattr4 without a svc_fh Jeff Layton
@ 2025-09-24 18:06 ` Jeff Layton
2025-09-24 18:06 ` [PATCH v3 35/38] nfsd: add the filehandle to returned attributes in CB_NOTIFY Jeff Layton
` (4 subsequent siblings)
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:06 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
When encoding a filehandle for a CB_NOTIFY, there is no svc_export
available, but the server needs to know whether to encode a connectable
filehandle. Add a flag to the nfs4_file that tells whether the
svc_export under which a directory delegation was acquired requires
connectable filehandles.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/nfsd/nfs4state.c | 1 +
fs/nfsd/state.h | 1 +
2 files changed, 2 insertions(+)
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 2147b5f80ca0e650e9dbdb63de7c9af6f4bc7bff..9b5f559ff6125a551f73ac103f8af2e3763dc3e6 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -5031,6 +5031,7 @@ static void nfsd4_file_init(const struct svc_fh *fh, struct nfs4_file *fp)
memset(fp->fi_access, 0, sizeof(fp->fi_access));
fp->fi_aliased = false;
fp->fi_inode = d_inode(fh->fh_dentry);
+ fp->fi_connectable = fh->fh_export->ex_flags & EXPORT_FH_CONNECTABLE;
#ifdef CONFIG_NFSD_PNFS
INIT_LIST_HEAD(&fp->fi_lo_states);
atomic_set(&fp->fi_lo_recalls, 0);
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 232b64e1d7721d3074364ff788b4f72b02a6c63f..6e066f0721e6a48394e182b3c273a44d2fbb652d 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -730,6 +730,7 @@ struct nfs4_file {
int fi_delegees;
struct knfsd_fh fi_fhandle;
bool fi_had_conflict;
+ bool fi_connectable;
#ifdef CONFIG_NFSD_PNFS
struct list_head fi_lo_states;
atomic_t fi_lo_recalls;
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 35/38] nfsd: add the filehandle to returned attributes in CB_NOTIFY
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (33 preceding siblings ...)
2025-09-24 18:06 ` [PATCH v3 34/38] nfsd: add a fi_connectable flag to struct nfs4_file Jeff Layton
@ 2025-09-24 18:06 ` Jeff Layton
2025-09-24 18:06 ` [PATCH v3 36/38] nfsd: properly track requested child attributes Jeff Layton
` (3 subsequent siblings)
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:06 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
nfsd's usual fh_compose routine requires a svc_export and fills out a
svc_fh. In the context of a CB_NOTIFY there is no such export to
consult.
Add a new routine that composes a filehandle with only a parent
filehandle and nfs4_file. Use that to fill out the fhandle field in the
nfsd4_fattr_args.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/nfsd/nfs4xdr.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 822a9d47dd88df579e4f57f04dd6737653f71c94..11b622aca5111502b483f269b1fce6a684804645 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3763,6 +3763,39 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
goto out;
}
+static bool
+setup_notify_fhandle(struct dentry *dentry, struct nfs4_file *fi,
+ struct nfsd4_fattr_args *args)
+{
+ int fileid_type, fsid_len, maxsize, flags = 0;
+ struct knfsd_fh *fhp = &args->fhandle;
+ struct inode *inode = d_inode(dentry);
+ struct inode *parent = NULL;
+ struct fid *fid;
+
+ fsid_len = key_len(fi->fi_fhandle.fh_fsid_type);
+ fhp->fh_size = 4 + fsid_len;
+
+ /* Copy first 4 bytes + fsid */
+ memcpy(&fhp->fh_raw, &fi->fi_fhandle.fh_raw, fhp->fh_size);
+
+ fid = (struct fid *)(fh_fsid(fhp) + fsid_len/4);
+ maxsize = (NFS4_FHSIZE - fhp->fh_size)/4;
+
+ if (fi->fi_connectable && !S_ISDIR(inode->i_mode)) {
+ parent = d_inode(fi->fi_deleg_file->nf_file->f_path.dentry);
+ flags = EXPORT_FH_CONNECTABLE;
+ }
+
+ fileid_type = exportfs_encode_inode_fh(inode, fid, &maxsize, parent, flags);
+ if (fileid_type < 0)
+ return false;
+
+ fhp->fh_fileid_type = fileid_type;
+ fhp->fh_size += maxsize * 4;
+ return true;
+}
+
#define CB_NOTIFY_STATX_REQUEST_MASK (STATX_BASIC_STATS | \
STATX_BTIME | \
STATX_CHANGE_COOKIE)
@@ -3811,6 +3844,9 @@ nfsd4_setup_notify_entry4(struct notify_entry4 *ne, struct xdr_stream *xdr,
FATTR4_WORD1_TIME_METADATA | FATTR4_WORD1_TIME_MODIFY;
attrmask[2] = 0;
+ if (setup_notify_fhandle(dentry, fi, &args))
+ attrmask[0] |= FATTR4_WORD0_FILEHANDLE;
+
if (args.stat.result_mask & STATX_BTIME)
attrmask[1] |= FATTR4_WORD1_TIME_CREATE;
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 36/38] nfsd: properly track requested child attributes
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (34 preceding siblings ...)
2025-09-24 18:06 ` [PATCH v3 35/38] nfsd: add the filehandle to returned attributes in CB_NOTIFY Jeff Layton
@ 2025-09-24 18:06 ` Jeff Layton
2025-09-24 18:06 ` [PATCH v3 37/38] nfsd: track requested dir attributes Jeff Layton
` (2 subsequent siblings)
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:06 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
Track the union of requested and supported child attributes in the
delegation, and only encode the attributes in that union when sending
add/remove/rename updates.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/nfsd/nfs4proc.c | 2 ++
fs/nfsd/nfs4state.c | 18 ++++++++++++++++++
fs/nfsd/nfs4xdr.c | 15 ++++++---------
fs/nfsd/state.h | 3 +++
4 files changed, 29 insertions(+), 9 deletions(-)
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 220fc873db8f08a90ac74e51ac9d931fe7edb9e4..774d18dd2f1a31d299a8426c3462847de6c88115 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -2385,6 +2385,8 @@ nfsd4_get_dir_delegation(struct svc_rqst *rqstp,
gdd->gddrnf_status = GDD4_OK;
memcpy(&gdd->gddr_stateid, &dd->dl_stid.sc_stateid, sizeof(gdd->gddr_stateid));
+ gdd->gddr_child_attributes[0] = dd->dl_child_attrs[0];
+ gdd->gddr_child_attributes[1] = dd->dl_child_attrs[1];
nfs4_put_stid(&dd->dl_stid);
return nfs_ok;
}
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 9b5f559ff6125a551f73ac103f8af2e3763dc3e6..368face4d0b7001914b209b858dc1baa366535f6 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -9643,6 +9643,21 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
return status;
}
+#define GDD_WORD0_CHILD_ATTRS (FATTR4_WORD0_TYPE | \
+ FATTR4_WORD0_CHANGE | \
+ FATTR4_WORD0_SIZE | \
+ FATTR4_WORD0_FILEID | \
+ FATTR4_WORD0_FILEHANDLE)
+
+#define GDD_WORD1_CHILD_ATTRS (FATTR4_WORD1_MODE | \
+ FATTR4_WORD1_NUMLINKS | \
+ FATTR4_WORD1_RAWDEV | \
+ FATTR4_WORD1_SPACE_USED | \
+ FATTR4_WORD1_TIME_ACCESS | \
+ FATTR4_WORD1_TIME_METADATA | \
+ FATTR4_WORD1_TIME_MODIFY | \
+ FATTR4_WORD1_TIME_CREATE)
+
/**
* nfsd_get_dir_deleg - attempt to get a directory delegation
* @cstate: compound state
@@ -9689,6 +9704,9 @@ nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate,
if (!dp)
goto out_delegees;
+ dp->dl_child_attrs[0] = gdd->gdda_child_attributes[0] & GDD_WORD0_CHILD_ATTRS;
+ dp->dl_child_attrs[1] = gdd->gdda_child_attributes[1] & GDD_WORD1_CHILD_ATTRS;
+
fl = nfs4_alloc_init_lease(dp, gdd->gddr_notification[0]);
if (!fl)
goto out_put_stid;
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 11b622aca5111502b483f269b1fce6a684804645..0c411c758279177837c078d393048aaebf31d46f 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3837,18 +3837,15 @@ nfsd4_setup_notify_entry4(struct notify_entry4 *ne, struct xdr_stream *xdr,
args.change_attr = nfsd4_change_attribute(&args.stat);
- attrmask[0] = FATTR4_WORD0_TYPE | FATTR4_WORD0_CHANGE |
- FATTR4_WORD0_SIZE | FATTR4_WORD0_FILEID;
- attrmask[1] = FATTR4_WORD1_MODE | FATTR4_WORD1_NUMLINKS | FATTR4_WORD1_RAWDEV |
- FATTR4_WORD1_SPACE_USED | FATTR4_WORD1_TIME_ACCESS |
- FATTR4_WORD1_TIME_METADATA | FATTR4_WORD1_TIME_MODIFY;
+ attrmask[0] = dp->dl_child_attrs[0];
+ attrmask[1] = dp->dl_child_attrs[1];
attrmask[2] = 0;
- if (setup_notify_fhandle(dentry, fi, &args))
- attrmask[0] |= FATTR4_WORD0_FILEHANDLE;
+ if (!setup_notify_fhandle(dentry, fi, &args))
+ attrmask[0] &= ~FATTR4_WORD0_FILEHANDLE;
- if (args.stat.result_mask & STATX_BTIME)
- attrmask[1] |= FATTR4_WORD1_TIME_CREATE;
+ if (!(args.stat.result_mask & STATX_BTIME))
+ attrmask[1] &= ~FATTR4_WORD1_TIME_CREATE;
ne->ne_attrs.attrmask.count = 2;
ne->ne_attrs.attr_vals.data = (u8 *)xdr->p;
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 6e066f0721e6a48394e182b3c273a44d2fbb652d..73869ae25bcdf63cc29f9ba49bdac20e21a812bd 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -281,6 +281,9 @@ struct nfs4_delegation {
struct timespec64 dl_atime;
struct timespec64 dl_mtime;
struct timespec64 dl_ctime;
+
+ /* For dir delegations */
+ uint32_t dl_child_attrs[2];
};
static inline bool deleg_is_read(u32 dl_type)
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 37/38] nfsd: track requested dir attributes
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (35 preceding siblings ...)
2025-09-24 18:06 ` [PATCH v3 36/38] nfsd: properly track requested child attributes Jeff Layton
@ 2025-09-24 18:06 ` Jeff Layton
2025-09-24 18:06 ` [PATCH v3 38/38] nfsd: add support to CB_NOTIFY for dir attribute changes Jeff Layton
2025-09-25 13:39 ` [PATCH v3 00/38] vfs, nfsd: implement directory delegations Chuck Lever
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:06 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
Track the union of the requested and supported directory attributes in
the delegation.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/nfsd/nfs4proc.c | 7 ++++---
fs/nfsd/nfs4state.c | 14 +++++++++++++-
fs/nfsd/state.h | 2 ++
3 files changed, 19 insertions(+), 4 deletions(-)
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 774d18dd2f1a31d299a8426c3462847de6c88115..187daf86e62f7c94892a3b30bf2eb37bb9863595 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -2335,9 +2335,10 @@ nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
return status == nfserr_same ? nfs_ok : status;
}
-#define SUPPORTED_NOTIFY_MASK (BIT(NOTIFY4_REMOVE_ENTRY) | \
- BIT(NOTIFY4_ADD_ENTRY) | \
- BIT(NOTIFY4_RENAME_ENTRY) | \
+#define SUPPORTED_NOTIFY_MASK (BIT(NOTIFY4_CHANGE_DIR_ATTRS) | \
+ BIT(NOTIFY4_REMOVE_ENTRY) | \
+ BIT(NOTIFY4_ADD_ENTRY) | \
+ BIT(NOTIFY4_RENAME_ENTRY) | \
BIT(NOTIFY4_GFLAG_EXTEND))
static __be32
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 368face4d0b7001914b209b858dc1baa366535f6..2381dbb2e48290debf28bbd35d0b9a4bb677ac07 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -9658,6 +9658,15 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
FATTR4_WORD1_TIME_MODIFY | \
FATTR4_WORD1_TIME_CREATE)
+#define GDD_WORD0_DIR_ATTRS (FATTR4_WORD0_CHANGE | \
+ FATTR4_WORD0_SIZE)
+
+#define GDD_WORD1_DIR_ATTRS (FATTR4_WORD1_NUMLINKS | \
+ FATTR4_WORD1_SPACE_USED | \
+ FATTR4_WORD1_TIME_ACCESS | \
+ FATTR4_WORD1_TIME_METADATA | \
+ FATTR4_WORD1_TIME_MODIFY)
+
/**
* nfsd_get_dir_deleg - attempt to get a directory delegation
* @cstate: compound state
@@ -9704,10 +9713,13 @@ nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate,
if (!dp)
goto out_delegees;
+ dp->dl_notify_mask = gdd->gddr_notification[0];
dp->dl_child_attrs[0] = gdd->gdda_child_attributes[0] & GDD_WORD0_CHILD_ATTRS;
dp->dl_child_attrs[1] = gdd->gdda_child_attributes[1] & GDD_WORD1_CHILD_ATTRS;
+ dp->dl_dir_attrs[0] = gdd->gdda_dir_attributes[0] & GDD_WORD0_DIR_ATTRS;
+ dp->dl_dir_attrs[1] = gdd->gdda_dir_attributes[1] & GDD_WORD1_DIR_ATTRS;
- fl = nfs4_alloc_init_lease(dp, gdd->gddr_notification[0]);
+ fl = nfs4_alloc_init_lease(dp, dp->dl_notify_mask);
if (!fl)
goto out_put_stid;
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 73869ae25bcdf63cc29f9ba49bdac20e21a812bd..946753d3ab6730892ba827151f2008afb46dcb57 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -283,7 +283,9 @@ struct nfs4_delegation {
struct timespec64 dl_ctime;
/* For dir delegations */
+ uint32_t dl_notify_mask;
uint32_t dl_child_attrs[2];
+ uint32_t dl_dir_attrs[2];
};
static inline bool deleg_is_read(u32 dl_type)
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* [PATCH v3 38/38] nfsd: add support to CB_NOTIFY for dir attribute changes
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (36 preceding siblings ...)
2025-09-24 18:06 ` [PATCH v3 37/38] nfsd: track requested dir attributes Jeff Layton
@ 2025-09-24 18:06 ` Jeff Layton
2025-09-25 13:39 ` [PATCH v3 00/38] vfs, nfsd: implement directory delegations Chuck Lever
38 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-24 18:06 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Paulo Alcantara
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel, Jeff Layton
If the client requested dir attribute change notifications, send those
alongside any set of add/remove/rename events. Note that the server will
still recall the delegation on a SETATTR, so these are only sent for
changes to child entries.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/nfsd/nfs4state.c | 25 ++++++++++++++++++++--
fs/nfsd/nfs4xdr.c | 60 +++++++++++++++++++++++++++++++++++++++++++++--------
fs/nfsd/xdr4.h | 1 +
3 files changed, 75 insertions(+), 11 deletions(-)
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 2381dbb2e48290debf28bbd35d0b9a4bb677ac07..a411ffb5f208a2e4d9b55dd77226b4e1a24eaee2 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -3342,9 +3342,14 @@ nfsd4_cb_notify_prepare(struct nfsd4_callback *cb)
struct nfsd_notify_event *events[NOTIFY4_EVENT_QUEUE_SIZE];
struct xdr_buf xdr = { .buflen = PAGE_SIZE * NOTIFY4_PAGE_ARRAY_SIZE,
.pages = ncn->ncn_pages };
+ int limit = NOTIFY4_EVENT_QUEUE_SIZE;
struct xdr_stream stream;
- int count, i;
bool error = false;
+ int count, i;
+
+ /* Save a slot for dir attr update if requested */
+ if (dp->dl_notify_mask & BIT(NOTIFY4_CHANGE_DIR_ATTRS))
+ --limit;
xdr_init_encode_pages(&stream, &xdr);
@@ -3358,7 +3363,7 @@ nfsd4_cb_notify_prepare(struct nfsd4_callback *cb)
}
/* we can't keep up! */
- if (count > NOTIFY4_EVENT_QUEUE_SIZE) {
+ if (count > limit) {
spin_unlock(&ncn->ncn_lock);
goto out_recall;
}
@@ -3396,6 +3401,22 @@ nfsd4_cb_notify_prepare(struct nfsd4_callback *cb)
nfsd_notify_event_put(nne);
}
if (!error) {
+ if (dp->dl_notify_mask & BIT(NOTIFY4_CHANGE_DIR_ATTRS)) {
+ u32 *maskp = (u32 *)xdr_reserve_space(&stream, sizeof(*maskp));
+
+ if (maskp) {
+ u8 *p = nfsd4_encode_dir_attr_change(&stream, dp);
+
+ if (p) {
+ *maskp = BIT(NOTIFY4_CHANGE_DIR_ATTRS);
+ ncn->ncn_nf[count].notify_mask.count = 1;
+ ncn->ncn_nf[count].notify_mask.element = maskp;
+ ncn->ncn_nf[count].notify_vals.data = p;
+ ncn->ncn_nf[count].notify_vals.len = (u8 *)stream.p - p;
+ ++count;
+ }
+ }
+ }
ncn->ncn_nf_cnt = count;
return true;
}
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 0c411c758279177837c078d393048aaebf31d46f..eca2b483e28397166d24756fcc97b5a1e0fdb9aa 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3806,11 +3806,11 @@ nfsd4_setup_notify_entry4(struct notify_entry4 *ne, struct xdr_stream *xdr,
char *name, u32 namelen)
{
struct nfs4_file *fi = dp->dl_stid.sc_file;
- struct path path = { .mnt = fi->fi_deleg_file->nf_file->f_path.mnt,
- .dentry = dentry };
+ struct path path = fi->fi_deleg_file->nf_file->f_path;
struct nfsd4_fattr_args args = { };
uint32_t *attrmask;
__be32 status;
+ bool parent;
int ret;
/* Reserve space for attrmask */
@@ -3822,6 +3822,9 @@ nfsd4_setup_notify_entry4(struct notify_entry4 *ne, struct xdr_stream *xdr,
ne->ne_file.len = namelen;
ne->ne_attrs.attrmask.element = attrmask;
+ parent = (dentry == path.dentry);
+ path.dentry = dentry;
+
/* FIXME: d_find_alias for inode ? */
if (!path.dentry || !d_inode(path.dentry))
goto noattrs;
@@ -3837,15 +3840,20 @@ nfsd4_setup_notify_entry4(struct notify_entry4 *ne, struct xdr_stream *xdr,
args.change_attr = nfsd4_change_attribute(&args.stat);
- attrmask[0] = dp->dl_child_attrs[0];
- attrmask[1] = dp->dl_child_attrs[1];
- attrmask[2] = 0;
+ if (parent) {
+ attrmask[0] = dp->dl_dir_attrs[0];
+ attrmask[1] = dp->dl_dir_attrs[1];
+ } else {
+ attrmask[0] = dp->dl_child_attrs[0];
+ attrmask[1] = dp->dl_child_attrs[1];
- if (!setup_notify_fhandle(dentry, fi, &args))
- attrmask[0] &= ~FATTR4_WORD0_FILEHANDLE;
+ if (!setup_notify_fhandle(dentry, fi, &args))
+ attrmask[0] &= ~FATTR4_WORD0_FILEHANDLE;
- if (!(args.stat.result_mask & STATX_BTIME))
- attrmask[1] &= ~FATTR4_WORD1_TIME_CREATE;
+ if (!(args.stat.result_mask & STATX_BTIME))
+ attrmask[1] &= ~FATTR4_WORD1_TIME_CREATE;
+ }
+ attrmask[2] = 0;
ne->ne_attrs.attrmask.count = 2;
ne->ne_attrs.attr_vals.data = (u8 *)xdr->p;
@@ -3936,6 +3944,40 @@ u8 *nfsd4_encode_notify_event(struct xdr_stream *xdr, struct nfsd_notify_event *
return NULL;
}
+/**
+ * nfsd4_encode_dir_attr_change
+ * @xdr: stream to which to encode the fattr4
+ * @dp: delegation where the event occurred
+ *
+ * Encode a dir attr change event.
+ */
+u8 *nfsd4_encode_dir_attr_change(struct xdr_stream *xdr, struct nfs4_delegation *dp)
+{
+ struct nfs4_file *fi = dp->dl_stid.sc_file;
+ struct dentry *dentry = fi->fi_deleg_file->nf_file->f_path.dentry;
+ struct notify_attr4 na = { };
+ struct name_snapshot n;
+ bool ret;
+ u8 *p = NULL;
+
+ if (!(dp->dl_notify_mask & BIT(NOTIFY4_CHANGE_DIR_ATTRS)))
+ return NULL;
+
+ take_dentry_name_snapshot(&n, dentry);
+ ret = nfsd4_setup_notify_entry4(&na.na_changed_entry, xdr,
+ dentry, dp, (char *)n.name.name,
+ n.name.len);
+
+ /* Don't bother with the event if we're not encoding attrs */
+ if (ret && na.na_changed_entry.ne_attrs.attr_vals.len) {
+ p = (u8 *)xdr->p;
+ if (!xdrgen_encode_notify_attr4(xdr, &na))
+ p = NULL;
+ }
+ release_dentry_name_snapshot(&n);
+ return p;
+}
+
static void svcxdr_init_encode_from_buffer(struct xdr_stream *xdr,
struct xdr_buf *buf, __be32 *p, int bytes)
{
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 19f468b5bc54343dca928f0b8286c868f2133241..93a38b104fb425413f3d7a59fa029c760618559c 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -969,6 +969,7 @@ __be32 nfsd4_encode_fattr_to_buf(__be32 **p, int words,
u32 *bmval, struct svc_rqst *, int ignore_crossmnt);
u8 *nfsd4_encode_notify_event(struct xdr_stream *xdr, struct nfsd_notify_event *nne,
struct nfs4_delegation *dd, u32 *notify_mask);
+u8 *nfsd4_encode_dir_attr_change(struct xdr_stream *xdr, struct nfs4_delegation *dp);
extern __be32 nfsd4_setclientid(struct svc_rqst *rqstp,
struct nfsd4_compound_state *, union nfsd4_op_u *u);
extern __be32 nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
--
2.51.0
^ permalink raw reply related [flat|nested] 53+ messages in thread
* Re: [PATCH v3 00/38] vfs, nfsd: implement directory delegations
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
` (37 preceding siblings ...)
2025-09-24 18:06 ` [PATCH v3 38/38] nfsd: add support to CB_NOTIFY for dir attribute changes Jeff Layton
@ 2025-09-25 13:39 ` Chuck Lever
2025-09-25 17:08 ` Jeff Layton
38 siblings, 1 reply; 53+ messages in thread
From: Chuck Lever @ 2025-09-25 13:39 UTC (permalink / raw)
To: Jeff Layton, Alexander Viro, Christian Brauner, Jan Kara,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel
On 9/24/25 11:05 AM, Jeff Layton wrote:
> This patchset is an update to a patchset that I posted in early June
> this year [1]. This version should be basically feature-complete, with a
> few caveats.
>
> NFSv4.1 adds a GET_DIR_DELEGATION operation, to allow clients
> to request a delegation on a directory. If the client holds a directory
> delegation, then it knows that nothing will change the dentries in it
> until it has been recalled (modulo the case where the client requests
> notifications of directory changes).
>
> In 2023, Rick Macklem gave a talk at the NFS Bakeathon on his
> implementation of directory delegations for FreeBSD [2], and showed that
> it can greatly improve LOOKUP-heavy workloads. There is also some
> earlier work by CITI [3] that showed similar results. The SMB protocol
> also has a similar sort of construct, and they have also seen large
> performance improvements on certain workloads.
>
> This version also starts with support for trivial directory delegations
> that support no notifications. From there it adds VFS support for
> ignoring certain break_lease() events in directories. It then adds
> support for basic CB_NOTIFY calls (with names only). Next, support for
> sending attributes in the notifications is added.
>
> I think that this version should be getting close to merge ready. Anna
> has graciously agreed to work on the client-side pieces for this. I've
> mostly been testing using pynfs tests (which I will submit soon).
>
> The main limitation at this point is that callback requests are
> currently limited to a single page, so we can't send very many in a
> single CB_NOTIFY call. This will make it easy to "get into the weeds" if
> you're changing a directory quickly. The server will just recall the
> delegation in that case, so it's harmless even though it's not ideal.
>
> If this approach looks acceptable I'll see if we can increase that
> limitation (it seems doable).
>
> If anyone wishes to try this out, it's in the "dir-deleg" branch in my
> tree at kernel.org [4].
>
> [1]: https://lore.kernel.org/linux-nfs/20250602-dir-deleg-v2-0-a7919700de86@kernel.org/
> [2]: https://www.youtube.com/watch?v=DdFyH3BN5pI
> [3]: https://linux-nfs.org/wiki/index.php/CITI_Experience_with_Directory_Delegations
> [4]: https://git.kernel.org/pub/scm/linux/kernel/git/jlayton/linux.git/
>
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
> ---
> Changes in v3:
> - Rework to do minimal work in fsnotify callbacks
> - Add support for sending attributes in CB_NOTIFY calls
> - Add support for dir attr change notifications
> - Link to v2: https://lore.kernel.org/r/20250602-dir-deleg-v2-0-a7919700de86@kernel.org
>
> Changes in v2:
> - add support for ignoring certain break_lease() events
> - basic support for CB_NOTIFY
> - Link to v1: https://lore.kernel.org/r/20240315-dir-deleg-v1-0-a1d6209a3654@kernel.org
>
> ---
> Jeff Layton (38):
> filelock: push the S_ISREG check down to ->setlease handlers
> filelock: add a lm_may_setlease lease_manager callback
> vfs: add try_break_deleg calls for parents to vfs_{link,rename,unlink}
> vfs: allow mkdir to wait for delegation break on parent
> vfs: allow rmdir to wait for delegation break on parent
> vfs: break parent dir delegations in open(..., O_CREAT) codepath
> vfs: make vfs_create break delegations on parent directory
> vfs: make vfs_mknod break delegations on parent directory
> filelock: lift the ban on directory leases in generic_setlease
> nfsd: allow filecache to hold S_IFDIR files
> nfsd: allow DELEGRETURN on directories
> nfsd: check for delegation conflicts vs. the same client
> nfsd: wire up GET_DIR_DELEGATION handling
> filelock: rework the __break_lease API to use flags
> filelock: add struct delegated_inode
> filelock: add support for ignoring deleg breaks for dir change events
> filelock: add a tracepoint to start of break_lease()
> filelock: add an inode_lease_ignore_mask helper
> nfsd: add protocol support for CB_NOTIFY
> nfs_common: add new NOTIFY4_* flags proposed in RFC8881bis
> nfsd: allow nfsd to get a dir lease with an ignore mask
> vfs: add fsnotify_modify_mark_mask()
> nfsd: update the fsnotify mark when setting or removing a dir delegation
> nfsd: make nfsd4_callback_ops->prepare operation bool return
> nfsd: add callback encoding and decoding linkages for CB_NOTIFY
> nfsd: add data structures for handling CB_NOTIFY to directory delegation
> nfsd: add notification handlers for dir events
> nfsd: add tracepoint to dir_event handler
> nfsd: apply the notify mask to the delegation when requested
> nfsd: add helper to marshal a fattr4 from completed args
> nfsd: allow nfsd4_encode_fattr4_change() to work with no export
> nfsd: send basic file attributes in CB_NOTIFY
> nfsd: allow encoding a filehandle into fattr4 without a svc_fh
> nfsd: add a fi_connectable flag to struct nfs4_file
> nfsd: add the filehandle to returned attributes in CB_NOTIFY
> nfsd: properly track requested child attributes
> nfsd: track requested dir attributes
> nfsd: add support to CB_NOTIFY for dir attribute changes
>
> Documentation/sunrpc/xdr/nfs4_1.x | 267 +++++++++++++++++-
> drivers/base/devtmpfs.c | 2 +-
> fs/attr.c | 4 +-
> fs/cachefiles/namei.c | 2 +-
> fs/ecryptfs/inode.c | 2 +-
> fs/fuse/dir.c | 1 +
> fs/init.c | 2 +-
> fs/locks.c | 122 ++++++--
> fs/namei.c | 253 +++++++++++------
> fs/nfs/nfs4file.c | 2 +
> fs/nfsd/filecache.c | 101 +++++--
> fs/nfsd/filecache.h | 2 +
> fs/nfsd/nfs4callback.c | 60 +++-
> fs/nfsd/nfs4layouts.c | 3 +-
> fs/nfsd/nfs4proc.c | 36 ++-
> fs/nfsd/nfs4recover.c | 2 +-
> fs/nfsd/nfs4state.c | 531 +++++++++++++++++++++++++++++++++--
> fs/nfsd/nfs4xdr.c | 298 +++++++++++++++++---
> fs/nfsd/nfs4xdr_gen.c | 506 ++++++++++++++++++++++++++++++++-
> fs/nfsd/nfs4xdr_gen.h | 20 +-
> fs/nfsd/state.h | 73 ++++-
> fs/nfsd/trace.h | 21 ++
> fs/nfsd/vfs.c | 7 +-
> fs/nfsd/vfs.h | 2 +-
> fs/nfsd/xdr4.h | 3 +
> fs/nfsd/xdr4cb.h | 12 +
> fs/notify/mark.c | 29 ++
> fs/open.c | 8 +-
> fs/overlayfs/overlayfs.h | 2 +-
> fs/posix_acl.c | 12 +-
> fs/smb/client/cifsfs.c | 3 +
> fs/smb/server/vfs.c | 2 +-
> fs/utimes.c | 4 +-
> fs/xattr.c | 16 +-
> fs/xfs/scrub/orphanage.c | 2 +-
> include/linux/filelock.h | 143 +++++++---
> include/linux/fs.h | 11 +-
> include/linux/fsnotify_backend.h | 1 +
> include/linux/nfs4.h | 127 ---------
> include/linux/sunrpc/xdrgen/nfs4_1.h | 304 +++++++++++++++++++-
> include/linux/xattr.h | 4 +-
> include/trace/events/filelock.h | 38 ++-
> include/uapi/linux/nfs4.h | 2 -
> 43 files changed, 2636 insertions(+), 406 deletions(-)
> ---
> base-commit: 36c204d169319562eed170f266c58460d5dad635
> change-id: 20240215-dir-deleg-e212210ba9d4
>
> Best regards,
Series is clean and easy to read, thanks for your hard work! I agree
that the NFSD portions appear to be complete and ready to accept.
Because the series is cross-subsystem, we will need to discuss a merge
plan. So I'll hold off on R-b or Acked until that is nailed down.
--
Chuck Lever
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH v3 22/38] vfs: add fsnotify_modify_mark_mask()
2025-09-24 18:06 ` [PATCH v3 22/38] vfs: add fsnotify_modify_mark_mask() Jeff Layton
@ 2025-09-25 15:50 ` Jan Kara
0 siblings, 0 replies; 53+ messages in thread
From: Jan Kara @ 2025-09-25 15:50 UTC (permalink / raw)
To: Jeff Layton
Cc: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Rick Macklem, linux-fsdevel,
linux-kernel, linux-nfs, linux-cifs, samba-technical, linux-doc,
netfs, ecryptfs, linux-unionfs, linux-xfs, linux-trace-kernel
On Wed 24-09-25 14:06:08, Jeff Layton wrote:
> nfsd needs to be able to modify the mask on an existing mark when new
> directory delegations are set or unset. Add an exported function that
> allows the caller to set and clear bits in the mark->mask, and does
> the recalculation if something changed.
>
> Suggested-by: Jan Kara <jack@suse.cz>
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
Looks good. Feel free to add:
Acked-by: Jan Kara <jack@suse.cz>
Honza
> ---
> fs/notify/mark.c | 29 +++++++++++++++++++++++++++++
> include/linux/fsnotify_backend.h | 1 +
> 2 files changed, 30 insertions(+)
>
> diff --git a/fs/notify/mark.c b/fs/notify/mark.c
> index 798340db69d761dd05c1b361c251818dee89b9cf..5ed42b24df7f6aa3812a7069b4c37f0c6b3414fa 100644
> --- a/fs/notify/mark.c
> +++ b/fs/notify/mark.c
> @@ -309,6 +309,35 @@ void fsnotify_recalc_mask(struct fsnotify_mark_connector *conn)
> fsnotify_conn_set_children_dentry_flags(conn);
> }
>
> +/**
> + * fsnotify_modify_mark_mask - set and/or clear flags in a mark's mask
> + * @mark: mark to be modified
> + * @set: bits to be set in mask
> + * @clear: bits to be cleared in mask
> + *
> + * Modify a fsnotify_mark mask as directed, and update its associated conn.
> + * The caller is expected to hold a reference to the mark.
> + */
> +void fsnotify_modify_mark_mask(struct fsnotify_mark *mark, u32 set, u32 clear)
> +{
> + bool recalc = false;
> + u32 mask;
> +
> + WARN_ON_ONCE(clear & set);
> +
> + spin_lock(&mark->lock);
> + mask = mark->mask;
> + mark->mask |= set;
> + mark->mask &= ~clear;
> + if (mark->mask != mask)
> + recalc = true;
> + spin_unlock(&mark->lock);
> +
> + if (recalc)
> + fsnotify_recalc_mask(mark->connector);
> +}
> +EXPORT_SYMBOL_GPL(fsnotify_modify_mark_mask);
> +
> /* Free all connectors queued for freeing once SRCU period ends */
> static void fsnotify_connector_destroy_workfn(struct work_struct *work)
> {
> diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
> index d4034ddaf3926bf98d8801997e50ba7ddf776292..8d50e6aad3c62c67a9bf73a8d9aab78565668c5f 100644
> --- a/include/linux/fsnotify_backend.h
> +++ b/include/linux/fsnotify_backend.h
> @@ -912,6 +912,7 @@ extern void fsnotify_get_mark(struct fsnotify_mark *mark);
> extern void fsnotify_put_mark(struct fsnotify_mark *mark);
> extern void fsnotify_finish_user_wait(struct fsnotify_iter_info *iter_info);
> extern bool fsnotify_prepare_user_wait(struct fsnotify_iter_info *iter_info);
> +extern void fsnotify_modify_mark_mask(struct fsnotify_mark *mark, u32 set, u32 clear);
>
> static inline void fsnotify_init_event(struct fsnotify_event *event)
> {
>
> --
> 2.51.0
>
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH v3 03/38] vfs: add try_break_deleg calls for parents to vfs_{link,rename,unlink}
2025-09-24 18:05 ` [PATCH v3 03/38] vfs: add try_break_deleg calls for parents to vfs_{link,rename,unlink} Jeff Layton
@ 2025-09-25 15:52 ` Jan Kara
0 siblings, 0 replies; 53+ messages in thread
From: Jan Kara @ 2025-09-25 15:52 UTC (permalink / raw)
To: Jeff Layton
Cc: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Rick Macklem, linux-fsdevel,
linux-kernel, linux-nfs, linux-cifs, samba-technical, linux-doc,
netfs, ecryptfs, linux-unionfs, linux-xfs, linux-trace-kernel
On Wed 24-09-25 14:05:49, Jeff Layton wrote:
> In order to add directory delegation support, we need to break
> delegations on the parent whenever there is going to be a change in the
> directory.
>
> vfs_link, vfs_unlink, and vfs_rename all have existing delegation break
> handling for the children in the rename. Add the necessary calls for
> breaking delegations in the parent(s) as well.
>
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
Looks good. Feel free to add:
Reviewed-by: Jan Kara <jack@suse.cz>
Honza
> ---
> fs/namei.c | 15 ++++++++++++++-
> 1 file changed, 14 insertions(+), 1 deletion(-)
>
> diff --git a/fs/namei.c b/fs/namei.c
> index cd43ff89fbaa38206db2aec4f097ca119819f92e..cd517eb232317d326e6d2fc5a60cb4c7569a137d 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -4580,6 +4580,9 @@ int vfs_unlink(struct mnt_idmap *idmap, struct inode *dir,
> else {
> error = security_inode_unlink(dir, dentry);
> if (!error) {
> + error = try_break_deleg(dir, delegated_inode);
> + if (error)
> + goto out;
> error = try_break_deleg(target, delegated_inode);
> if (error)
> goto out;
> @@ -4849,7 +4852,9 @@ int vfs_link(struct dentry *old_dentry, struct mnt_idmap *idmap,
> else if (max_links && inode->i_nlink >= max_links)
> error = -EMLINK;
> else {
> - error = try_break_deleg(inode, delegated_inode);
> + error = try_break_deleg(dir, delegated_inode);
> + if (!error)
> + error = try_break_deleg(inode, delegated_inode);
> if (!error)
> error = dir->i_op->link(old_dentry, dir, new_dentry);
> }
> @@ -5116,6 +5121,14 @@ int vfs_rename(struct renamedata *rd)
> old_dir->i_nlink >= max_links)
> goto out;
> }
> + error = try_break_deleg(old_dir, delegated_inode);
> + if (error)
> + goto out;
> + if (new_dir != old_dir) {
> + error = try_break_deleg(new_dir, delegated_inode);
> + if (error)
> + goto out;
> + }
> if (!is_dir) {
> error = try_break_deleg(source, delegated_inode);
> if (error)
>
> --
> 2.51.0
>
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH v3 04/38] vfs: allow mkdir to wait for delegation break on parent
2025-09-24 18:05 ` [PATCH v3 04/38] vfs: allow mkdir to wait for delegation break on parent Jeff Layton
@ 2025-09-25 15:58 ` Jan Kara
2025-09-25 17:12 ` Jeff Layton
0 siblings, 1 reply; 53+ messages in thread
From: Jan Kara @ 2025-09-25 15:58 UTC (permalink / raw)
To: Jeff Layton
Cc: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Rick Macklem, linux-fsdevel,
linux-kernel, linux-nfs, linux-cifs, samba-technical, linux-doc,
netfs, ecryptfs, linux-unionfs, linux-xfs, linux-trace-kernel
On Wed 24-09-25 14:05:50, Jeff Layton wrote:
> In order to add directory delegation support, we need to break
> delegations on the parent whenever there is going to be a change in the
> directory.
>
> Rename the existing vfs_mkdir to __vfs_mkdir, make it static and add a
> new delegated_inode parameter. Add a new exported vfs_mkdir wrapper
> around it that passes a NULL pointer for delegated_inode.
>
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
The changelog looks stale (__vfs_mkdir() doesn't exist anymore) but
otherwise the patch looks good. Feel free to add:
Reviewed-by: Jan Kara <jack@suse.cz>
Honza
> ---
> drivers/base/devtmpfs.c | 2 +-
> fs/cachefiles/namei.c | 2 +-
> fs/ecryptfs/inode.c | 2 +-
> fs/init.c | 2 +-
> fs/namei.c | 24 ++++++++++++++++++------
> fs/nfsd/nfs4recover.c | 2 +-
> fs/nfsd/vfs.c | 2 +-
> fs/overlayfs/overlayfs.h | 2 +-
> fs/smb/server/vfs.c | 2 +-
> fs/xfs/scrub/orphanage.c | 2 +-
> include/linux/fs.h | 2 +-
> 11 files changed, 28 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c
> index 31bfb3194b4c29a1d6a002449045bf4e4141911d..a57da600ce7523e9e2755b78f75342bf4fa56ef6 100644
> --- a/drivers/base/devtmpfs.c
> +++ b/drivers/base/devtmpfs.c
> @@ -180,7 +180,7 @@ static int dev_mkdir(const char *name, umode_t mode)
> if (IS_ERR(dentry))
> return PTR_ERR(dentry);
>
> - dentry = vfs_mkdir(&nop_mnt_idmap, d_inode(path.dentry), dentry, mode);
> + dentry = vfs_mkdir(&nop_mnt_idmap, d_inode(path.dentry), dentry, mode, NULL);
> if (!IS_ERR(dentry))
> /* mark as kernel-created inode */
> d_inode(dentry)->i_private = &thread;
> diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c
> index 91dfd02318772fa63050ecf40fa5625ab48ad589..b3dac91efec622261186fbba8e704ae9e782bea0 100644
> --- a/fs/cachefiles/namei.c
> +++ b/fs/cachefiles/namei.c
> @@ -130,7 +130,7 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
> goto mkdir_error;
> ret = cachefiles_inject_write_error();
> if (ret == 0)
> - subdir = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), subdir, 0700);
> + subdir = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), subdir, 0700, NULL);
> else
> subdir = ERR_PTR(ret);
> if (IS_ERR(subdir)) {
> diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
> index 72fbe1316ab8831bb4228d573278f32fe52b6b25..00f54c125b102856c33ffff24627475f40dcbc7b 100644
> --- a/fs/ecryptfs/inode.c
> +++ b/fs/ecryptfs/inode.c
> @@ -517,7 +517,7 @@ static struct dentry *ecryptfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
> goto out;
>
> lower_dentry = vfs_mkdir(&nop_mnt_idmap, lower_dir,
> - lower_dentry, mode);
> + lower_dentry, mode, NULL);
> rc = PTR_ERR(lower_dentry);
> if (IS_ERR(lower_dentry))
> goto out;
> diff --git a/fs/init.c b/fs/init.c
> index eef5124885e372ac020d2923692116c5e884b3cf..dd5240ce8ad41f02367a54ddf1b6ac0aa28e9721 100644
> --- a/fs/init.c
> +++ b/fs/init.c
> @@ -232,7 +232,7 @@ int __init init_mkdir(const char *pathname, umode_t mode)
> error = security_path_mkdir(&path, dentry, mode);
> if (!error) {
> dentry = vfs_mkdir(mnt_idmap(path.mnt), path.dentry->d_inode,
> - dentry, mode);
> + dentry, mode, NULL);
> if (IS_ERR(dentry))
> error = PTR_ERR(dentry);
> }
> diff --git a/fs/namei.c b/fs/namei.c
> index cd517eb232317d326e6d2fc5a60cb4c7569a137d..c939a58f16f9c4edded424475aff52f2c423d301 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -4320,10 +4320,11 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d
>
> /**
> * vfs_mkdir - create directory returning correct dentry if possible
> - * @idmap: idmap of the mount the inode was found from
> - * @dir: inode of the parent directory
> - * @dentry: dentry of the child directory
> - * @mode: mode of the child directory
> + * @idmap: idmap of the mount the inode was found from
> + * @dir: inode of the parent directory
> + * @dentry: dentry of the child directory
> + * @mode: mode of the child directory
> + * @delegated_inode: returns victim inode, if the inode is delegated.
> *
> * Create a directory.
> *
> @@ -4340,7 +4341,8 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d
> * In case of an error the dentry is dput() and an ERR_PTR() is returned.
> */
> struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
> - struct dentry *dentry, umode_t mode)
> + struct dentry *dentry, umode_t mode,
> + struct inode **delegated_inode)
> {
> int error;
> unsigned max_links = dir->i_sb->s_max_links;
> @@ -4363,6 +4365,10 @@ struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
> if (max_links && dir->i_nlink >= max_links)
> goto err;
>
> + error = try_break_deleg(dir, delegated_inode);
> + if (error)
> + goto err;
> +
> de = dir->i_op->mkdir(idmap, dir, dentry, mode);
> error = PTR_ERR(de);
> if (IS_ERR(de))
> @@ -4386,6 +4392,7 @@ int do_mkdirat(int dfd, struct filename *name, umode_t mode)
> struct path path;
> int error;
> unsigned int lookup_flags = LOOKUP_DIRECTORY;
> + struct inode *delegated_inode = NULL;
>
> retry:
> dentry = filename_create(dfd, name, &path, lookup_flags);
> @@ -4397,11 +4404,16 @@ int do_mkdirat(int dfd, struct filename *name, umode_t mode)
> mode_strip_umask(path.dentry->d_inode, mode));
> if (!error) {
> dentry = vfs_mkdir(mnt_idmap(path.mnt), path.dentry->d_inode,
> - dentry, mode);
> + dentry, mode, &delegated_inode);
> if (IS_ERR(dentry))
> error = PTR_ERR(dentry);
> }
> done_path_create(&path, dentry);
> + if (delegated_inode) {
> + error = break_deleg_wait(&delegated_inode);
> + if (!error)
> + goto retry;
> + }
> if (retry_estale(error, lookup_flags)) {
> lookup_flags |= LOOKUP_REVAL;
> goto retry;
> diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c
> index b1005abcb9035b2cf743200808a251b00af7e3f4..423dd102b51198ea7c447be2b9a0a5020c950dba 100644
> --- a/fs/nfsd/nfs4recover.c
> +++ b/fs/nfsd/nfs4recover.c
> @@ -202,7 +202,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
> * as well be forgiving and just succeed silently.
> */
> goto out_put;
> - dentry = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), dentry, S_IRWXU);
> + dentry = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), dentry, 0700, NULL);
> if (IS_ERR(dentry))
> status = PTR_ERR(dentry);
> out_put:
> diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
> index 2026431500ecbc0cf5fb5d4af1a7632c611ce4f4..6f1275fdc8ac831aa0ea8da588f751eddff88df1 100644
> --- a/fs/nfsd/vfs.c
> +++ b/fs/nfsd/vfs.c
> @@ -1560,7 +1560,7 @@ nfsd_create_locked(struct svc_rqst *rqstp, struct svc_fh *fhp,
> nfsd_check_ignore_resizing(iap);
> break;
> case S_IFDIR:
> - dchild = vfs_mkdir(&nop_mnt_idmap, dirp, dchild, iap->ia_mode);
> + dchild = vfs_mkdir(&nop_mnt_idmap, dirp, dchild, iap->ia_mode, NULL);
> if (IS_ERR(dchild)) {
> host_err = PTR_ERR(dchild);
> } else if (d_is_negative(dchild)) {
> diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
> index bb0d7ded8e763a4a7a6fc506d966ed2f3bdb4f06..4a3a22f422c37d45e49a762cd3c9957aa2c6a485 100644
> --- a/fs/overlayfs/overlayfs.h
> +++ b/fs/overlayfs/overlayfs.h
> @@ -248,7 +248,7 @@ static inline struct dentry *ovl_do_mkdir(struct ovl_fs *ofs,
> {
> struct dentry *ret;
>
> - ret = vfs_mkdir(ovl_upper_mnt_idmap(ofs), dir, dentry, mode);
> + ret = vfs_mkdir(ovl_upper_mnt_idmap(ofs), dir, dentry, mode, NULL);
> pr_debug("mkdir(%pd2, 0%o) = %i\n", dentry, mode, PTR_ERR_OR_ZERO(ret));
> return ret;
> }
> diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
> index 04539037108c93e285f4e9d6aa61f93a507ae5da..b0fb73b277876a56797f5cc8a5aa53f156bb7a26 100644
> --- a/fs/smb/server/vfs.c
> +++ b/fs/smb/server/vfs.c
> @@ -229,7 +229,7 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode)
> idmap = mnt_idmap(path.mnt);
> mode |= S_IFDIR;
> d = dentry;
> - dentry = vfs_mkdir(idmap, d_inode(path.dentry), dentry, mode);
> + dentry = vfs_mkdir(idmap, d_inode(path.dentry), dentry, mode, NULL);
> if (IS_ERR(dentry))
> err = PTR_ERR(dentry);
> else if (d_is_negative(dentry))
> diff --git a/fs/xfs/scrub/orphanage.c b/fs/xfs/scrub/orphanage.c
> index 9c12cb8442311ca26b169e4d1567939ae44a5be0..91c9d07b97f306f57aebb9b69ba564b0c2cb8c17 100644
> --- a/fs/xfs/scrub/orphanage.c
> +++ b/fs/xfs/scrub/orphanage.c
> @@ -167,7 +167,7 @@ xrep_orphanage_create(
> */
> if (d_really_is_negative(orphanage_dentry)) {
> orphanage_dentry = vfs_mkdir(&nop_mnt_idmap, root_inode,
> - orphanage_dentry, 0750);
> + orphanage_dentry, 0750, NULL);
> error = PTR_ERR(orphanage_dentry);
> if (IS_ERR(orphanage_dentry))
> goto out_unlock_root;
> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index 74f2bfc519263c6411a8e3427e1bd6680a1121db..24a091509f12ce65a2c8343d438fccf423d3062b 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -1997,7 +1997,7 @@ bool inode_owner_or_capable(struct mnt_idmap *idmap,
> int vfs_create(struct mnt_idmap *, struct inode *,
> struct dentry *, umode_t, bool);
> struct dentry *vfs_mkdir(struct mnt_idmap *, struct inode *,
> - struct dentry *, umode_t);
> + struct dentry *, umode_t, struct inode **);
> int vfs_mknod(struct mnt_idmap *, struct inode *, struct dentry *,
> umode_t, dev_t);
> int vfs_symlink(struct mnt_idmap *, struct inode *,
>
> --
> 2.51.0
>
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH v3 00/38] vfs, nfsd: implement directory delegations
2025-09-25 13:39 ` [PATCH v3 00/38] vfs, nfsd: implement directory delegations Chuck Lever
@ 2025-09-25 17:08 ` Jeff Layton
0 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-25 17:08 UTC (permalink / raw)
To: Chuck Lever, Alexander Viro, Christian Brauner, Jan Kara,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel
On Thu, 2025-09-25 at 09:39 -0400, Chuck Lever wrote:
> On 9/24/25 11:05 AM, Jeff Layton wrote:
> > This patchset is an update to a patchset that I posted in early June
> > this year [1]. This version should be basically feature-complete, with a
> > few caveats.
> >
> > NFSv4.1 adds a GET_DIR_DELEGATION operation, to allow clients
> > to request a delegation on a directory. If the client holds a directory
> > delegation, then it knows that nothing will change the dentries in it
> > until it has been recalled (modulo the case where the client requests
> > notifications of directory changes).
> >
> > In 2023, Rick Macklem gave a talk at the NFS Bakeathon on his
> > implementation of directory delegations for FreeBSD [2], and showed that
> > it can greatly improve LOOKUP-heavy workloads. There is also some
> > earlier work by CITI [3] that showed similar results. The SMB protocol
> > also has a similar sort of construct, and they have also seen large
> > performance improvements on certain workloads.
> >
> > This version also starts with support for trivial directory delegations
> > that support no notifications. From there it adds VFS support for
> > ignoring certain break_lease() events in directories. It then adds
> > support for basic CB_NOTIFY calls (with names only). Next, support for
> > sending attributes in the notifications is added.
> >
> > I think that this version should be getting close to merge ready. Anna
> > has graciously agreed to work on the client-side pieces for this. I've
> > mostly been testing using pynfs tests (which I will submit soon).
> >
> > The main limitation at this point is that callback requests are
> > currently limited to a single page, so we can't send very many in a
> > single CB_NOTIFY call. This will make it easy to "get into the weeds" if
> > you're changing a directory quickly. The server will just recall the
> > delegation in that case, so it's harmless even though it's not ideal.
> >
> > If this approach looks acceptable I'll see if we can increase that
> > limitation (it seems doable).
> >
> > If anyone wishes to try this out, it's in the "dir-deleg" branch in my
> > tree at kernel.org [4].
> >
> > [1]: https://lore.kernel.org/linux-nfs/20250602-dir-deleg-v2-0-a7919700de86@kernel.org/
> > [2]: https://www.youtube.com/watch?v=DdFyH3BN5pI
> > [3]: https://linux-nfs.org/wiki/index.php/CITI_Experience_with_Directory_Delegations
> > [4]: https://git.kernel.org/pub/scm/linux/kernel/git/jlayton/linux.git/
> >
> > Signed-off-by: Jeff Layton <jlayton@kernel.org>
> > ---
> > Changes in v3:
> > - Rework to do minimal work in fsnotify callbacks
> > - Add support for sending attributes in CB_NOTIFY calls
> > - Add support for dir attr change notifications
> > - Link to v2: https://lore.kernel.org/r/20250602-dir-deleg-v2-0-a7919700de86@kernel.org
> >
> > Changes in v2:
> > - add support for ignoring certain break_lease() events
> > - basic support for CB_NOTIFY
> > - Link to v1: https://lore.kernel.org/r/20240315-dir-deleg-v1-0-a1d6209a3654@kernel.org
> >
> > ---
> > Jeff Layton (38):
> > filelock: push the S_ISREG check down to ->setlease handlers
> > filelock: add a lm_may_setlease lease_manager callback
> > vfs: add try_break_deleg calls for parents to vfs_{link,rename,unlink}
> > vfs: allow mkdir to wait for delegation break on parent
> > vfs: allow rmdir to wait for delegation break on parent
> > vfs: break parent dir delegations in open(..., O_CREAT) codepath
> > vfs: make vfs_create break delegations on parent directory
> > vfs: make vfs_mknod break delegations on parent directory
> > filelock: lift the ban on directory leases in generic_setlease
> > nfsd: allow filecache to hold S_IFDIR files
> > nfsd: allow DELEGRETURN on directories
> > nfsd: check for delegation conflicts vs. the same client
> > nfsd: wire up GET_DIR_DELEGATION handling
> > filelock: rework the __break_lease API to use flags
> > filelock: add struct delegated_inode
> > filelock: add support for ignoring deleg breaks for dir change events
> > filelock: add a tracepoint to start of break_lease()
> > filelock: add an inode_lease_ignore_mask helper
> > nfsd: add protocol support for CB_NOTIFY
> > nfs_common: add new NOTIFY4_* flags proposed in RFC8881bis
> > nfsd: allow nfsd to get a dir lease with an ignore mask
> > vfs: add fsnotify_modify_mark_mask()
> > nfsd: update the fsnotify mark when setting or removing a dir delegation
> > nfsd: make nfsd4_callback_ops->prepare operation bool return
> > nfsd: add callback encoding and decoding linkages for CB_NOTIFY
> > nfsd: add data structures for handling CB_NOTIFY to directory delegation
> > nfsd: add notification handlers for dir events
> > nfsd: add tracepoint to dir_event handler
> > nfsd: apply the notify mask to the delegation when requested
> > nfsd: add helper to marshal a fattr4 from completed args
> > nfsd: allow nfsd4_encode_fattr4_change() to work with no export
> > nfsd: send basic file attributes in CB_NOTIFY
> > nfsd: allow encoding a filehandle into fattr4 without a svc_fh
> > nfsd: add a fi_connectable flag to struct nfs4_file
> > nfsd: add the filehandle to returned attributes in CB_NOTIFY
> > nfsd: properly track requested child attributes
> > nfsd: track requested dir attributes
> > nfsd: add support to CB_NOTIFY for dir attribute changes
> >
> > Documentation/sunrpc/xdr/nfs4_1.x | 267 +++++++++++++++++-
> > drivers/base/devtmpfs.c | 2 +-
> > fs/attr.c | 4 +-
> > fs/cachefiles/namei.c | 2 +-
> > fs/ecryptfs/inode.c | 2 +-
> > fs/fuse/dir.c | 1 +
> > fs/init.c | 2 +-
> > fs/locks.c | 122 ++++++--
> > fs/namei.c | 253 +++++++++++------
> > fs/nfs/nfs4file.c | 2 +
> > fs/nfsd/filecache.c | 101 +++++--
> > fs/nfsd/filecache.h | 2 +
> > fs/nfsd/nfs4callback.c | 60 +++-
> > fs/nfsd/nfs4layouts.c | 3 +-
> > fs/nfsd/nfs4proc.c | 36 ++-
> > fs/nfsd/nfs4recover.c | 2 +-
> > fs/nfsd/nfs4state.c | 531 +++++++++++++++++++++++++++++++++--
> > fs/nfsd/nfs4xdr.c | 298 +++++++++++++++++---
> > fs/nfsd/nfs4xdr_gen.c | 506 ++++++++++++++++++++++++++++++++-
> > fs/nfsd/nfs4xdr_gen.h | 20 +-
> > fs/nfsd/state.h | 73 ++++-
> > fs/nfsd/trace.h | 21 ++
> > fs/nfsd/vfs.c | 7 +-
> > fs/nfsd/vfs.h | 2 +-
> > fs/nfsd/xdr4.h | 3 +
> > fs/nfsd/xdr4cb.h | 12 +
> > fs/notify/mark.c | 29 ++
> > fs/open.c | 8 +-
> > fs/overlayfs/overlayfs.h | 2 +-
> > fs/posix_acl.c | 12 +-
> > fs/smb/client/cifsfs.c | 3 +
> > fs/smb/server/vfs.c | 2 +-
> > fs/utimes.c | 4 +-
> > fs/xattr.c | 16 +-
> > fs/xfs/scrub/orphanage.c | 2 +-
> > include/linux/filelock.h | 143 +++++++---
> > include/linux/fs.h | 11 +-
> > include/linux/fsnotify_backend.h | 1 +
> > include/linux/nfs4.h | 127 ---------
> > include/linux/sunrpc/xdrgen/nfs4_1.h | 304 +++++++++++++++++++-
> > include/linux/xattr.h | 4 +-
> > include/trace/events/filelock.h | 38 ++-
> > include/uapi/linux/nfs4.h | 2 -
> > 43 files changed, 2636 insertions(+), 406 deletions(-)
> > ---
> > base-commit: 36c204d169319562eed170f266c58460d5dad635
> > change-id: 20240215-dir-deleg-e212210ba9d4
> >
> > Best regards,
>
> Series is clean and easy to read, thanks for your hard work! I agree
> that the NFSD portions appear to be complete and ready to accept.
>
> Because the series is cross-subsystem, we will need to discuss a merge
> plan. So I'll hold off on R-b or Acked until that is nailed down.
>
Thanks. It's sensible to hold off for a bit. There is at least one leak
that I found earlier today, and a few cleanups that I have queued up.
We also have a bake-a-thon in another couple of weeks where I hope to
test this more extensively. After that, I'm hoping we'll be in
reasonable shape to take it into linux-next.
What I may do is reorder the vfs patches to the front of the queue and
plead to Christian and Al to take them into a branch that feeds into
linux-next. That way we can at least get some feedback and testing with
those bits in place, and a foundation on which we can merge the nfsd
bits.
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH v3 04/38] vfs: allow mkdir to wait for delegation break on parent
2025-09-25 15:58 ` Jan Kara
@ 2025-09-25 17:12 ` Jeff Layton
0 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-25 17:12 UTC (permalink / raw)
To: Jan Kara
Cc: Alexander Viro, Christian Brauner, Chuck Lever, Alexander Aring,
Trond Myklebust, Anna Schumaker, Steve French, Ronnie Sahlberg,
Shyam Prasad N, Tom Talpey, Bharath SM, NeilBrown,
Olga Kornievskaia, Dai Ngo, Jonathan Corbet, Amir Goldstein,
Miklos Szeredi, Paulo Alcantara, Greg Kroah-Hartman,
Rafael J. Wysocki, Danilo Krummrich, David Howells, Tyler Hicks,
Namjae Jeon, Steve French, Sergey Senozhatsky, Carlos Maiolino,
Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers, Rick Macklem,
linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel
On Thu, 2025-09-25 at 17:58 +0200, Jan Kara wrote:
> On Wed 24-09-25 14:05:50, Jeff Layton wrote:
> > In order to add directory delegation support, we need to break
> > delegations on the parent whenever there is going to be a change in the
> > directory.
> >
> > Rename the existing vfs_mkdir to __vfs_mkdir, make it static and add a
> > new delegated_inode parameter. Add a new exported vfs_mkdir wrapper
> > around it that passes a NULL pointer for delegated_inode.
> >
> > Signed-off-by: Jeff Layton <jlayton@kernel.org>
>
> The changelog looks stale (__vfs_mkdir() doesn't exist anymore) but
> otherwise the patch looks good. Feel free to add:
>
> Reviewed-by: Jan Kara <jack@suse.cz>
>
Thanks, I realized that I had forgotten to fix it after I sent it.
> Honza
>
> > ---
> > drivers/base/devtmpfs.c | 2 +-
> > fs/cachefiles/namei.c | 2 +-
> > fs/ecryptfs/inode.c | 2 +-
> > fs/init.c | 2 +-
> > fs/namei.c | 24 ++++++++++++++++++------
> > fs/nfsd/nfs4recover.c | 2 +-
> > fs/nfsd/vfs.c | 2 +-
> > fs/overlayfs/overlayfs.h | 2 +-
> > fs/smb/server/vfs.c | 2 +-
> > fs/xfs/scrub/orphanage.c | 2 +-
> > include/linux/fs.h | 2 +-
> > 11 files changed, 28 insertions(+), 16 deletions(-)
> >
> > diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c
> > index 31bfb3194b4c29a1d6a002449045bf4e4141911d..a57da600ce7523e9e2755b78f75342bf4fa56ef6 100644
> > --- a/drivers/base/devtmpfs.c
> > +++ b/drivers/base/devtmpfs.c
> > @@ -180,7 +180,7 @@ static int dev_mkdir(const char *name, umode_t mode)
> > if (IS_ERR(dentry))
> > return PTR_ERR(dentry);
> >
> > - dentry = vfs_mkdir(&nop_mnt_idmap, d_inode(path.dentry), dentry, mode);
> > + dentry = vfs_mkdir(&nop_mnt_idmap, d_inode(path.dentry), dentry, mode, NULL);
> > if (!IS_ERR(dentry))
> > /* mark as kernel-created inode */
> > d_inode(dentry)->i_private = &thread;
> > diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c
> > index 91dfd02318772fa63050ecf40fa5625ab48ad589..b3dac91efec622261186fbba8e704ae9e782bea0 100644
> > --- a/fs/cachefiles/namei.c
> > +++ b/fs/cachefiles/namei.c
> > @@ -130,7 +130,7 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
> > goto mkdir_error;
> > ret = cachefiles_inject_write_error();
> > if (ret == 0)
> > - subdir = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), subdir, 0700);
> > + subdir = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), subdir, 0700, NULL);
> > else
> > subdir = ERR_PTR(ret);
> > if (IS_ERR(subdir)) {
> > diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
> > index 72fbe1316ab8831bb4228d573278f32fe52b6b25..00f54c125b102856c33ffff24627475f40dcbc7b 100644
> > --- a/fs/ecryptfs/inode.c
> > +++ b/fs/ecryptfs/inode.c
> > @@ -517,7 +517,7 @@ static struct dentry *ecryptfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
> > goto out;
> >
> > lower_dentry = vfs_mkdir(&nop_mnt_idmap, lower_dir,
> > - lower_dentry, mode);
> > + lower_dentry, mode, NULL);
> > rc = PTR_ERR(lower_dentry);
> > if (IS_ERR(lower_dentry))
> > goto out;
> > diff --git a/fs/init.c b/fs/init.c
> > index eef5124885e372ac020d2923692116c5e884b3cf..dd5240ce8ad41f02367a54ddf1b6ac0aa28e9721 100644
> > --- a/fs/init.c
> > +++ b/fs/init.c
> > @@ -232,7 +232,7 @@ int __init init_mkdir(const char *pathname, umode_t mode)
> > error = security_path_mkdir(&path, dentry, mode);
> > if (!error) {
> > dentry = vfs_mkdir(mnt_idmap(path.mnt), path.dentry->d_inode,
> > - dentry, mode);
> > + dentry, mode, NULL);
> > if (IS_ERR(dentry))
> > error = PTR_ERR(dentry);
> > }
> > diff --git a/fs/namei.c b/fs/namei.c
> > index cd517eb232317d326e6d2fc5a60cb4c7569a137d..c939a58f16f9c4edded424475aff52f2c423d301 100644
> > --- a/fs/namei.c
> > +++ b/fs/namei.c
> > @@ -4320,10 +4320,11 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d
> >
> > /**
> > * vfs_mkdir - create directory returning correct dentry if possible
> > - * @idmap: idmap of the mount the inode was found from
> > - * @dir: inode of the parent directory
> > - * @dentry: dentry of the child directory
> > - * @mode: mode of the child directory
> > + * @idmap: idmap of the mount the inode was found from
> > + * @dir: inode of the parent directory
> > + * @dentry: dentry of the child directory
> > + * @mode: mode of the child directory
> > + * @delegated_inode: returns victim inode, if the inode is delegated.
> > *
> > * Create a directory.
> > *
> > @@ -4340,7 +4341,8 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d
> > * In case of an error the dentry is dput() and an ERR_PTR() is returned.
> > */
> > struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
> > - struct dentry *dentry, umode_t mode)
> > + struct dentry *dentry, umode_t mode,
> > + struct inode **delegated_inode)
> > {
> > int error;
> > unsigned max_links = dir->i_sb->s_max_links;
> > @@ -4363,6 +4365,10 @@ struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
> > if (max_links && dir->i_nlink >= max_links)
> > goto err;
> >
> > + error = try_break_deleg(dir, delegated_inode);
> > + if (error)
> > + goto err;
> > +
> > de = dir->i_op->mkdir(idmap, dir, dentry, mode);
> > error = PTR_ERR(de);
> > if (IS_ERR(de))
> > @@ -4386,6 +4392,7 @@ int do_mkdirat(int dfd, struct filename *name, umode_t mode)
> > struct path path;
> > int error;
> > unsigned int lookup_flags = LOOKUP_DIRECTORY;
> > + struct inode *delegated_inode = NULL;
> >
> > retry:
> > dentry = filename_create(dfd, name, &path, lookup_flags);
> > @@ -4397,11 +4404,16 @@ int do_mkdirat(int dfd, struct filename *name, umode_t mode)
> > mode_strip_umask(path.dentry->d_inode, mode));
> > if (!error) {
> > dentry = vfs_mkdir(mnt_idmap(path.mnt), path.dentry->d_inode,
> > - dentry, mode);
> > + dentry, mode, &delegated_inode);
> > if (IS_ERR(dentry))
> > error = PTR_ERR(dentry);
> > }
> > done_path_create(&path, dentry);
> > + if (delegated_inode) {
> > + error = break_deleg_wait(&delegated_inode);
> > + if (!error)
> > + goto retry;
> > + }
> > if (retry_estale(error, lookup_flags)) {
> > lookup_flags |= LOOKUP_REVAL;
> > goto retry;
> > diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c
> > index b1005abcb9035b2cf743200808a251b00af7e3f4..423dd102b51198ea7c447be2b9a0a5020c950dba 100644
> > --- a/fs/nfsd/nfs4recover.c
> > +++ b/fs/nfsd/nfs4recover.c
> > @@ -202,7 +202,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
> > * as well be forgiving and just succeed silently.
> > */
> > goto out_put;
> > - dentry = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), dentry, S_IRWXU);
> > + dentry = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), dentry, 0700, NULL);
> > if (IS_ERR(dentry))
> > status = PTR_ERR(dentry);
> > out_put:
> > diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
> > index 2026431500ecbc0cf5fb5d4af1a7632c611ce4f4..6f1275fdc8ac831aa0ea8da588f751eddff88df1 100644
> > --- a/fs/nfsd/vfs.c
> > +++ b/fs/nfsd/vfs.c
> > @@ -1560,7 +1560,7 @@ nfsd_create_locked(struct svc_rqst *rqstp, struct svc_fh *fhp,
> > nfsd_check_ignore_resizing(iap);
> > break;
> > case S_IFDIR:
> > - dchild = vfs_mkdir(&nop_mnt_idmap, dirp, dchild, iap->ia_mode);
> > + dchild = vfs_mkdir(&nop_mnt_idmap, dirp, dchild, iap->ia_mode, NULL);
> > if (IS_ERR(dchild)) {
> > host_err = PTR_ERR(dchild);
> > } else if (d_is_negative(dchild)) {
> > diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
> > index bb0d7ded8e763a4a7a6fc506d966ed2f3bdb4f06..4a3a22f422c37d45e49a762cd3c9957aa2c6a485 100644
> > --- a/fs/overlayfs/overlayfs.h
> > +++ b/fs/overlayfs/overlayfs.h
> > @@ -248,7 +248,7 @@ static inline struct dentry *ovl_do_mkdir(struct ovl_fs *ofs,
> > {
> > struct dentry *ret;
> >
> > - ret = vfs_mkdir(ovl_upper_mnt_idmap(ofs), dir, dentry, mode);
> > + ret = vfs_mkdir(ovl_upper_mnt_idmap(ofs), dir, dentry, mode, NULL);
> > pr_debug("mkdir(%pd2, 0%o) = %i\n", dentry, mode, PTR_ERR_OR_ZERO(ret));
> > return ret;
> > }
> > diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
> > index 04539037108c93e285f4e9d6aa61f93a507ae5da..b0fb73b277876a56797f5cc8a5aa53f156bb7a26 100644
> > --- a/fs/smb/server/vfs.c
> > +++ b/fs/smb/server/vfs.c
> > @@ -229,7 +229,7 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode)
> > idmap = mnt_idmap(path.mnt);
> > mode |= S_IFDIR;
> > d = dentry;
> > - dentry = vfs_mkdir(idmap, d_inode(path.dentry), dentry, mode);
> > + dentry = vfs_mkdir(idmap, d_inode(path.dentry), dentry, mode, NULL);
> > if (IS_ERR(dentry))
> > err = PTR_ERR(dentry);
> > else if (d_is_negative(dentry))
> > diff --git a/fs/xfs/scrub/orphanage.c b/fs/xfs/scrub/orphanage.c
> > index 9c12cb8442311ca26b169e4d1567939ae44a5be0..91c9d07b97f306f57aebb9b69ba564b0c2cb8c17 100644
> > --- a/fs/xfs/scrub/orphanage.c
> > +++ b/fs/xfs/scrub/orphanage.c
> > @@ -167,7 +167,7 @@ xrep_orphanage_create(
> > */
> > if (d_really_is_negative(orphanage_dentry)) {
> > orphanage_dentry = vfs_mkdir(&nop_mnt_idmap, root_inode,
> > - orphanage_dentry, 0750);
> > + orphanage_dentry, 0750, NULL);
> > error = PTR_ERR(orphanage_dentry);
> > if (IS_ERR(orphanage_dentry))
> > goto out_unlock_root;
> > diff --git a/include/linux/fs.h b/include/linux/fs.h
> > index 74f2bfc519263c6411a8e3427e1bd6680a1121db..24a091509f12ce65a2c8343d438fccf423d3062b 100644
> > --- a/include/linux/fs.h
> > +++ b/include/linux/fs.h
> > @@ -1997,7 +1997,7 @@ bool inode_owner_or_capable(struct mnt_idmap *idmap,
> > int vfs_create(struct mnt_idmap *, struct inode *,
> > struct dentry *, umode_t, bool);
> > struct dentry *vfs_mkdir(struct mnt_idmap *, struct inode *,
> > - struct dentry *, umode_t);
> > + struct dentry *, umode_t, struct inode **);
> > int vfs_mknod(struct mnt_idmap *, struct inode *, struct dentry *,
> > umode_t, dev_t);
> > int vfs_symlink(struct mnt_idmap *, struct inode *,
> >
> > --
> > 2.51.0
> >
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH v3 05/38] vfs: allow rmdir to wait for delegation break on parent
2025-09-24 18:05 ` [PATCH v3 05/38] vfs: allow rmdir " Jeff Layton
@ 2025-09-25 17:13 ` Jeff Layton
0 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-25 17:13 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel
On Wed, 2025-09-24 at 14:05 -0400, Jeff Layton wrote:
> In order to add directory delegation support, we need to break
> delegations on the parent whenever there is going to be a change in the
> directory.
>
> Rename vfs_rmdir as __vfs_rmdir, make it static and add a new
> delegated_inode parameter. Add a vfs_rmdir wrapper that passes in a NULL
> pointer for it. Add the necessary try_break_deleg calls to
> __vfs_rmdir(). Convert do_rmdir to use __vfs_rmdir and wait for the
> delegation break to complete before proceeding.
>
I've fixed this patch along the lines of the vfs_mkdir() patch
(eliminating the wrapper and just adding the extra parameter). The
updated patch is in my dir-deleg branch.
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
> ---
> fs/namei.c | 51 ++++++++++++++++++++++++++++++++++-----------------
> 1 file changed, 34 insertions(+), 17 deletions(-)
>
> diff --git a/fs/namei.c b/fs/namei.c
> index c939a58f16f9c4edded424475aff52f2c423d301..4e058b00208c1663ba828c6f8ed1f82c26a4f136 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -4433,22 +4433,8 @@ SYSCALL_DEFINE2(mkdir, const char __user *, pathname, umode_t, mode)
> return do_mkdirat(AT_FDCWD, getname(pathname), mode);
> }
>
> -/**
> - * vfs_rmdir - remove directory
> - * @idmap: idmap of the mount the inode was found from
> - * @dir: inode of the parent directory
> - * @dentry: dentry of the child directory
> - *
> - * Remove a directory.
> - *
> - * If the inode has been found through an idmapped mount the idmap of
> - * the vfsmount must be passed through @idmap. This function will then take
> - * care to map the inode according to @idmap before checking permissions.
> - * On non-idmapped mounts or if permission checking is to be performed on the
> - * raw inode simply pass @nop_mnt_idmap.
> - */
> -int vfs_rmdir(struct mnt_idmap *idmap, struct inode *dir,
> - struct dentry *dentry)
> +static int __vfs_rmdir(struct mnt_idmap *idmap, struct inode *dir,
> + struct dentry *dentry, struct inode **delegated_inode)
> {
> int error = may_delete(idmap, dir, dentry, 1);
>
> @@ -4470,6 +4456,10 @@ int vfs_rmdir(struct mnt_idmap *idmap, struct inode *dir,
> if (error)
> goto out;
>
> + error = try_break_deleg(dir, delegated_inode);
> + if (error)
> + goto out;
> +
> error = dir->i_op->rmdir(dir, dentry);
> if (error)
> goto out;
> @@ -4486,6 +4476,26 @@ int vfs_rmdir(struct mnt_idmap *idmap, struct inode *dir,
> d_delete_notify(dir, dentry);
> return error;
> }
> +
> +/**
> + * vfs_rmdir - remove directory
> + * @idmap: idmap of the mount the inode was found from
> + * @dir: inode of the parent directory
> + * @dentry: dentry of the child directory
> + *
> + * Remove a directory.
> + *
> + * If the inode has been found through an idmapped mount the idmap of
> + * the vfsmount must be passed through @idmap. This function will then take
> + * care to map the inode according to @idmap before checking permissions.
> + * On non-idmapped mounts or if permission checking is to be performed on the
> + * raw inode simply pass @nop_mnt_idmap.
> + */
> +int vfs_rmdir(struct mnt_idmap *idmap, struct inode *dir,
> + struct dentry *dentry)
> +{
> + return __vfs_rmdir(idmap, dir, dentry, NULL);
> +}
> EXPORT_SYMBOL(vfs_rmdir);
>
> int do_rmdir(int dfd, struct filename *name)
> @@ -4496,6 +4506,7 @@ int do_rmdir(int dfd, struct filename *name)
> struct qstr last;
> int type;
> unsigned int lookup_flags = 0;
> + struct inode *delegated_inode = NULL;
> retry:
> error = filename_parentat(dfd, name, lookup_flags, &path, &last, &type);
> if (error)
> @@ -4525,7 +4536,8 @@ int do_rmdir(int dfd, struct filename *name)
> error = security_path_rmdir(&path, dentry);
> if (error)
> goto exit4;
> - error = vfs_rmdir(mnt_idmap(path.mnt), path.dentry->d_inode, dentry);
> + error = __vfs_rmdir(mnt_idmap(path.mnt), path.dentry->d_inode,
> + dentry, &delegated_inode);
> exit4:
> dput(dentry);
> exit3:
> @@ -4533,6 +4545,11 @@ int do_rmdir(int dfd, struct filename *name)
> mnt_drop_write(path.mnt);
> exit2:
> path_put(&path);
> + if (delegated_inode) {
> + error = break_deleg_wait(&delegated_inode);
> + if (!error)
> + goto retry;
> + }
> if (retry_estale(error, lookup_flags)) {
> lookup_flags |= LOOKUP_REVAL;
> goto retry;
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH v3 27/38] nfsd: add notification handlers for dir events
2025-09-24 18:06 ` [PATCH v3 27/38] nfsd: add notification handlers for dir events Jeff Layton
@ 2025-09-25 17:15 ` Jeff Layton
0 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-25 17:15 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers
Cc: Rick Macklem, linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel
On Wed, 2025-09-24 at 14:06 -0400, Jeff Layton wrote:
> Add the necessary parts to accept a fsnotify callback for directory
> change event and create a CB_NOTIFY request for it. When a dir nfsd_file
> is created set a handle_event callback to handle the notification.
>
> Use that to allocate a nfsd_notify_event object and then hand off a
> reference to each delegation's CB_NOTIFY. If anything fails along the
> way, recall any affected delegations.
>
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
> ---
> fs/nfsd/filecache.c | 51 ++++++++++----
> fs/nfsd/nfs4callback.c | 19 +++--
> fs/nfsd/nfs4state.c | 185 +++++++++++++++++++++++++++++++++++++++++++++++++
> fs/nfsd/nfs4xdr.c | 95 +++++++++++++++++++++++++
> fs/nfsd/state.h | 2 +
> fs/nfsd/xdr4.h | 2 +
> 6 files changed, 337 insertions(+), 17 deletions(-)
>
[...]
> +
> +int
> +nfsd_handle_dir_event(u32 mask, const struct inode *dir, const void *data,
> + int data_type, const struct qstr *name)
> +{
> + struct dentry *dentry = fsnotify_data_dentry(data, data_type);
> + struct file_lock_context *ctx;
> + struct file_lock_core *flc;
> + struct nfsd_notify_event *evt;
> +
> + ctx = locks_inode_context(dir);
> + if (!ctx || list_empty(&ctx->flc_lease))
> + return 0;
> +
> + evt = alloc_nfsd_notify_event(mask, name, dentry);
> + if (!evt) {
> + nfsd_recall_all_dir_delegs(dir);
> + return 0;
> + }
> +
> + spin_lock(&ctx->flc_lock);
> + list_for_each_entry(flc, &ctx->flc_lease, flc_list) {
> + struct file_lease *fl = container_of(flc, struct file_lease, c);
> + struct nfs4_delegation *dp = flc->flc_owner;
> + struct nfsd4_cb_notify *ncn = &dp->dl_cb_notify;
> +
> + if (!should_notify_deleg(mask, fl))
> + continue;
> +
> + spin_lock(&ncn->ncn_lock);
> + if (ncn->ncn_evt_cnt >= NOTIFY4_EVENT_QUEUE_SIZE) {
> + /* We're generating notifications too fast. Recall. */
> + spin_unlock(&ncn->ncn_lock);
> + nfsd_break_deleg_cb(fl);
> + continue;
> + }
> + ncn->ncn_evt[ncn->ncn_evt_cnt++] = nfsd_notify_event_get(evt);
The above nfsd_notify_event_get() causes a refcount leak. Fixed in
tree.
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH v3 07/38] vfs: make vfs_create break delegations on parent directory
2025-09-24 18:05 ` [PATCH v3 07/38] vfs: make vfs_create break delegations on parent directory Jeff Layton
@ 2025-09-26 15:21 ` Jan Kara
2025-09-26 15:23 ` Jan Kara
1 sibling, 0 replies; 53+ messages in thread
From: Jan Kara @ 2025-09-26 15:21 UTC (permalink / raw)
To: Jeff Layton
Cc: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Rick Macklem, linux-fsdevel,
linux-kernel, linux-nfs, linux-cifs, samba-technical, linux-doc,
netfs, ecryptfs, linux-unionfs, linux-xfs, linux-trace-kernel
On Wed 24-09-25 14:05:53, Jeff Layton wrote:
> In order to add directory delegation support, we need to break
> delegations on the parent whenever there is going to be a change in the
> directory.
>
> Rename vfs_create as __vfs_create, make it static, and add a new
> delegated_inode parameter. Fix do_mknodat to call __vfs_create and wait
> for a delegation break if there is one. Add a new exported vfs_create
> wrapper that passes in NULL for delegated_inode.
>
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
Looks good. Feel free to add:
Reviewed-by: Jan Kara <jack@suse.cz>
Honza
> ---
> fs/namei.c | 55 ++++++++++++++++++++++++++++++++++++-------------------
> 1 file changed, 36 insertions(+), 19 deletions(-)
>
> diff --git a/fs/namei.c b/fs/namei.c
> index 903b70a82530938a0fdf10508529a1b7cc38136d..d4b8330a3eb97e205dc2e71766fed1e45503323b 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -3370,6 +3370,32 @@ static inline umode_t vfs_prepare_mode(struct mnt_idmap *idmap,
> return mode;
> }
>
> +static int __vfs_create(struct mnt_idmap *idmap, struct inode *dir,
> + struct dentry *dentry, umode_t mode, bool want_excl,
> + struct inode **delegated_inode)
> +{
> + int error;
> +
> + error = may_create(idmap, dir, dentry);
> + if (error)
> + return error;
> +
> + if (!dir->i_op->create)
> + return -EACCES; /* shouldn't it be ENOSYS? */
> +
> + mode = vfs_prepare_mode(idmap, dir, mode, S_IALLUGO, S_IFREG);
> + error = security_inode_create(dir, dentry, mode);
> + if (error)
> + return error;
> + error = try_break_deleg(dir, delegated_inode);
> + if (error)
> + return error;
> + error = dir->i_op->create(idmap, dir, dentry, mode, want_excl);
> + if (!error)
> + fsnotify_create(dir, dentry);
> + return error;
> +}
> +
> /**
> * vfs_create - create new file
> * @idmap: idmap of the mount the inode was found from
> @@ -3389,23 +3415,7 @@ static inline umode_t vfs_prepare_mode(struct mnt_idmap *idmap,
> int vfs_create(struct mnt_idmap *idmap, struct inode *dir,
> struct dentry *dentry, umode_t mode, bool want_excl)
> {
> - int error;
> -
> - error = may_create(idmap, dir, dentry);
> - if (error)
> - return error;
> -
> - if (!dir->i_op->create)
> - return -EACCES; /* shouldn't it be ENOSYS? */
> -
> - mode = vfs_prepare_mode(idmap, dir, mode, S_IALLUGO, S_IFREG);
> - error = security_inode_create(dir, dentry, mode);
> - if (error)
> - return error;
> - error = dir->i_op->create(idmap, dir, dentry, mode, want_excl);
> - if (!error)
> - fsnotify_create(dir, dentry);
> - return error;
> + return __vfs_create(idmap, dir, dentry, mode, want_excl, NULL);
> }
> EXPORT_SYMBOL(vfs_create);
>
> @@ -4278,6 +4288,7 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode,
> struct path path;
> int error;
> unsigned int lookup_flags = 0;
> + struct inode *delegated_inode = NULL;
>
> error = may_mknod(mode);
> if (error)
> @@ -4296,8 +4307,9 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode,
> idmap = mnt_idmap(path.mnt);
> switch (mode & S_IFMT) {
> case 0: case S_IFREG:
> - error = vfs_create(idmap, path.dentry->d_inode,
> - dentry, mode, true);
> + error = __vfs_create(idmap, path.dentry->d_inode,
> + dentry, mode, true,
> + &delegated_inode);
> if (!error)
> security_path_post_mknod(idmap, dentry);
> break;
> @@ -4312,6 +4324,11 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode,
> }
> out2:
> done_path_create(&path, dentry);
> + if (delegated_inode) {
> + error = break_deleg_wait(&delegated_inode);
> + if (!error)
> + goto retry;
> + }
> if (retry_estale(error, lookup_flags)) {
> lookup_flags |= LOOKUP_REVAL;
> goto retry;
>
> --
> 2.51.0
>
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH v3 07/38] vfs: make vfs_create break delegations on parent directory
2025-09-24 18:05 ` [PATCH v3 07/38] vfs: make vfs_create break delegations on parent directory Jeff Layton
2025-09-26 15:21 ` Jan Kara
@ 2025-09-26 15:23 ` Jan Kara
2025-09-26 15:33 ` Jan Kara
1 sibling, 1 reply; 53+ messages in thread
From: Jan Kara @ 2025-09-26 15:23 UTC (permalink / raw)
To: Jeff Layton
Cc: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Rick Macklem, linux-fsdevel,
linux-kernel, linux-nfs, linux-cifs, samba-technical, linux-doc,
netfs, ecryptfs, linux-unionfs, linux-xfs, linux-trace-kernel
On Wed 24-09-25 14:05:53, Jeff Layton wrote:
> In order to add directory delegation support, we need to break
> delegations on the parent whenever there is going to be a change in the
> directory.
>
> Rename vfs_create as __vfs_create, make it static, and add a new
> delegated_inode parameter. Fix do_mknodat to call __vfs_create and wait
> for a delegation break if there is one. Add a new exported vfs_create
> wrapper that passes in NULL for delegated_inode.
>
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
Looks good. Feel free to add:
Reviewed-by: Jan Kara <jack@suse.cz>
Honza
> ---
> fs/namei.c | 55 ++++++++++++++++++++++++++++++++++++-------------------
> 1 file changed, 36 insertions(+), 19 deletions(-)
>
> diff --git a/fs/namei.c b/fs/namei.c
> index 903b70a82530938a0fdf10508529a1b7cc38136d..d4b8330a3eb97e205dc2e71766fed1e45503323b 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -3370,6 +3370,32 @@ static inline umode_t vfs_prepare_mode(struct mnt_idmap *idmap,
> return mode;
> }
>
> +static int __vfs_create(struct mnt_idmap *idmap, struct inode *dir,
> + struct dentry *dentry, umode_t mode, bool want_excl,
> + struct inode **delegated_inode)
> +{
> + int error;
> +
> + error = may_create(idmap, dir, dentry);
> + if (error)
> + return error;
> +
> + if (!dir->i_op->create)
> + return -EACCES; /* shouldn't it be ENOSYS? */
> +
> + mode = vfs_prepare_mode(idmap, dir, mode, S_IALLUGO, S_IFREG);
> + error = security_inode_create(dir, dentry, mode);
> + if (error)
> + return error;
> + error = try_break_deleg(dir, delegated_inode);
> + if (error)
> + return error;
> + error = dir->i_op->create(idmap, dir, dentry, mode, want_excl);
> + if (!error)
> + fsnotify_create(dir, dentry);
> + return error;
> +}
> +
> /**
> * vfs_create - create new file
> * @idmap: idmap of the mount the inode was found from
> @@ -3389,23 +3415,7 @@ static inline umode_t vfs_prepare_mode(struct mnt_idmap *idmap,
> int vfs_create(struct mnt_idmap *idmap, struct inode *dir,
> struct dentry *dentry, umode_t mode, bool want_excl)
> {
> - int error;
> -
> - error = may_create(idmap, dir, dentry);
> - if (error)
> - return error;
> -
> - if (!dir->i_op->create)
> - return -EACCES; /* shouldn't it be ENOSYS? */
> -
> - mode = vfs_prepare_mode(idmap, dir, mode, S_IALLUGO, S_IFREG);
> - error = security_inode_create(dir, dentry, mode);
> - if (error)
> - return error;
> - error = dir->i_op->create(idmap, dir, dentry, mode, want_excl);
> - if (!error)
> - fsnotify_create(dir, dentry);
> - return error;
> + return __vfs_create(idmap, dir, dentry, mode, want_excl, NULL);
> }
> EXPORT_SYMBOL(vfs_create);
>
> @@ -4278,6 +4288,7 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode,
> struct path path;
> int error;
> unsigned int lookup_flags = 0;
> + struct inode *delegated_inode = NULL;
>
> error = may_mknod(mode);
> if (error)
> @@ -4296,8 +4307,9 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode,
> idmap = mnt_idmap(path.mnt);
> switch (mode & S_IFMT) {
> case 0: case S_IFREG:
> - error = vfs_create(idmap, path.dentry->d_inode,
> - dentry, mode, true);
> + error = __vfs_create(idmap, path.dentry->d_inode,
> + dentry, mode, true,
> + &delegated_inode);
> if (!error)
> security_path_post_mknod(idmap, dentry);
> break;
> @@ -4312,6 +4324,11 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode,
> }
> out2:
> done_path_create(&path, dentry);
> + if (delegated_inode) {
> + error = break_deleg_wait(&delegated_inode);
> + if (!error)
> + goto retry;
> + }
> if (retry_estale(error, lookup_flags)) {
> lookup_flags |= LOOKUP_REVAL;
> goto retry;
>
> --
> 2.51.0
>
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH v3 06/38] vfs: break parent dir delegations in open(..., O_CREAT) codepath
2025-09-24 18:05 ` [PATCH v3 06/38] vfs: break parent dir delegations in open(..., O_CREAT) codepath Jeff Layton
@ 2025-09-26 15:32 ` Jan Kara
0 siblings, 0 replies; 53+ messages in thread
From: Jan Kara @ 2025-09-26 15:32 UTC (permalink / raw)
To: Jeff Layton
Cc: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Rick Macklem, linux-fsdevel,
linux-kernel, linux-nfs, linux-cifs, samba-technical, linux-doc,
netfs, ecryptfs, linux-unionfs, linux-xfs, linux-trace-kernel
On Wed 24-09-25 14:05:52, Jeff Layton wrote:
> In order to add directory delegation support, we need to break
> delegations on the parent whenever there is going to be a change in the
> directory.
>
> Add a delegated_inode parameter to lookup_open and have it break the
> delegation. Then, open_last_lookups can wait for the delegation break
> and retry the call to lookup_open once it's done.
>
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
Looks good. Feel free to add:
Reviewed-by: Jan Kara <jack@suse.cz>
Honza
> ---
> fs/namei.c | 22 ++++++++++++++++++----
> 1 file changed, 18 insertions(+), 4 deletions(-)
>
> diff --git a/fs/namei.c b/fs/namei.c
> index 4e058b00208c1663ba828c6f8ed1f82c26a4f136..903b70a82530938a0fdf10508529a1b7cc38136d 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -3609,7 +3609,7 @@ static struct dentry *atomic_open(struct nameidata *nd, struct dentry *dentry,
> */
> static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
> const struct open_flags *op,
> - bool got_write)
> + bool got_write, struct inode **delegated_inode)
> {
> struct mnt_idmap *idmap;
> struct dentry *dir = nd->path.dentry;
> @@ -3698,6 +3698,11 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
>
> /* Negative dentry, just create the file */
> if (!dentry->d_inode && (open_flag & O_CREAT)) {
> + /* but break the directory lease first! */
> + error = try_break_deleg(dir_inode, delegated_inode);
> + if (error)
> + goto out_dput;
> +
> file->f_mode |= FMODE_CREATED;
> audit_inode_child(dir_inode, dentry, AUDIT_TYPE_CHILD_CREATE);
> if (!dir_inode->i_op->create) {
> @@ -3761,6 +3766,7 @@ static const char *open_last_lookups(struct nameidata *nd,
> struct file *file, const struct open_flags *op)
> {
> struct dentry *dir = nd->path.dentry;
> + struct inode *delegated_inode = NULL;
> int open_flag = op->open_flag;
> bool got_write = false;
> struct dentry *dentry;
> @@ -3791,7 +3797,7 @@ static const char *open_last_lookups(struct nameidata *nd,
> return ERR_PTR(-ECHILD);
> }
> }
> -
> +retry:
> if (open_flag & (O_CREAT | O_TRUNC | O_WRONLY | O_RDWR)) {
> got_write = !mnt_want_write(nd->path.mnt);
> /*
> @@ -3804,7 +3810,7 @@ static const char *open_last_lookups(struct nameidata *nd,
> inode_lock(dir->d_inode);
> else
> inode_lock_shared(dir->d_inode);
> - dentry = lookup_open(nd, file, op, got_write);
> + dentry = lookup_open(nd, file, op, got_write, &delegated_inode);
> if (!IS_ERR(dentry)) {
> if (file->f_mode & FMODE_CREATED)
> fsnotify_create(dir->d_inode, dentry);
> @@ -3819,8 +3825,16 @@ static const char *open_last_lookups(struct nameidata *nd,
> if (got_write)
> mnt_drop_write(nd->path.mnt);
>
> - if (IS_ERR(dentry))
> + if (IS_ERR(dentry)) {
> + if (delegated_inode) {
> + int error = break_deleg_wait(&delegated_inode);
> +
> + if (!error)
> + goto retry;
> + return ERR_PTR(error);
> + }
> return ERR_CAST(dentry);
> + }
>
> if (file->f_mode & (FMODE_OPENED | FMODE_CREATED)) {
> dput(nd->path.dentry);
>
> --
> 2.51.0
>
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH v3 08/38] vfs: make vfs_mknod break delegations on parent directory
2025-09-24 18:05 ` [PATCH v3 08/38] vfs: make vfs_mknod " Jeff Layton
@ 2025-09-26 15:32 ` Jan Kara
2025-09-26 16:27 ` Jeff Layton
0 siblings, 1 reply; 53+ messages in thread
From: Jan Kara @ 2025-09-26 15:32 UTC (permalink / raw)
To: Jeff Layton
Cc: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Rick Macklem, linux-fsdevel,
linux-kernel, linux-nfs, linux-cifs, samba-technical, linux-doc,
netfs, ecryptfs, linux-unionfs, linux-xfs, linux-trace-kernel
On Wed 24-09-25 14:05:54, Jeff Layton wrote:
> In order to add directory delegation support, we need to break
> delegations on the parent whenever there is going to be a change in the
> directory.
>
> Rename vfs_mknod as __vfs_mknod, make it static, and add a new
> delegated_inode parameter. Make do_mknodat call __vfs_mknod and wait
> synchronously for delegation breaks to complete. Add a new exported
> vfs_mknod wrapper that calls __vfs_mknod with a NULL delegated_inode
> pointer.
>
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
Looks good. Feel free to add:
Reviewed-by: Jan Kara <jack@suse.cz>
Honza
> ---
> fs/namei.c | 57 +++++++++++++++++++++++++++++++++++----------------------
> 1 file changed, 35 insertions(+), 22 deletions(-)
>
> diff --git a/fs/namei.c b/fs/namei.c
> index d4b8330a3eb97e205dc2e71766fed1e45503323b..7bcd898c84138061030f1f8b91273261cdf2a9b4 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -4215,24 +4215,9 @@ inline struct dentry *user_path_create(int dfd, const char __user *pathname,
> }
> EXPORT_SYMBOL(user_path_create);
>
> -/**
> - * vfs_mknod - create device node or file
> - * @idmap: idmap of the mount the inode was found from
> - * @dir: inode of the parent directory
> - * @dentry: dentry of the child device node
> - * @mode: mode of the child device node
> - * @dev: device number of device to create
> - *
> - * Create a device node or file.
> - *
> - * If the inode has been found through an idmapped mount the idmap of
> - * the vfsmount must be passed through @idmap. This function will then take
> - * care to map the inode according to @idmap before checking permissions.
> - * On non-idmapped mounts or if permission checking is to be performed on the
> - * raw inode simply pass @nop_mnt_idmap.
> - */
> -int vfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
> - struct dentry *dentry, umode_t mode, dev_t dev)
> +static int __vfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
> + struct dentry *dentry, umode_t mode, dev_t dev,
> + struct inode **delegated_inode)
> {
> bool is_whiteout = S_ISCHR(mode) && dev == WHITEOUT_DEV;
> int error = may_create(idmap, dir, dentry);
> @@ -4256,11 +4241,37 @@ int vfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
> if (error)
> return error;
>
> + error = try_break_deleg(dir, delegated_inode);
> + if (error)
> + return error;
> +
> error = dir->i_op->mknod(idmap, dir, dentry, mode, dev);
> if (!error)
> fsnotify_create(dir, dentry);
> return error;
> }
> +
> +/**
> + * vfs_mknod - create device node or file
> + * @idmap: idmap of the mount the inode was found from
> + * @dir: inode of the parent directory
> + * @dentry: dentry of the child device node
> + * @mode: mode of the child device node
> + * @dev: device number of device to create
> + *
> + * Create a device node or file.
> + *
> + * If the inode has been found through an idmapped mount the idmap of
> + * the vfsmount must be passed through @idmap. This function will then take
> + * care to map the inode according to @idmap before checking permissions.
> + * On non-idmapped mounts or if permission checking is to be performed on the
> + * raw inode simply pass @nop_mnt_idmap.
> + */
> +int vfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
> + struct dentry *dentry, umode_t mode, dev_t dev)
> +{
> + return __vfs_mknod(idmap, dir, dentry, mode, dev, NULL);
> +}
> EXPORT_SYMBOL(vfs_mknod);
>
> static int may_mknod(umode_t mode)
> @@ -4314,12 +4325,14 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode,
> security_path_post_mknod(idmap, dentry);
> break;
> case S_IFCHR: case S_IFBLK:
> - error = vfs_mknod(idmap, path.dentry->d_inode,
> - dentry, mode, new_decode_dev(dev));
> + error = __vfs_mknod(idmap, path.dentry->d_inode,
> + dentry, mode, new_decode_dev(dev),
> + &delegated_inode);
> break;
> case S_IFIFO: case S_IFSOCK:
> - error = vfs_mknod(idmap, path.dentry->d_inode,
> - dentry, mode, 0);
> + error = __vfs_mknod(idmap, path.dentry->d_inode,
> + dentry, mode, 0,
> + &delegated_inode);
> break;
> }
> out2:
>
> --
> 2.51.0
>
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH v3 07/38] vfs: make vfs_create break delegations on parent directory
2025-09-26 15:23 ` Jan Kara
@ 2025-09-26 15:33 ` Jan Kara
0 siblings, 0 replies; 53+ messages in thread
From: Jan Kara @ 2025-09-26 15:33 UTC (permalink / raw)
To: Jeff Layton
Cc: Alexander Viro, Christian Brauner, Jan Kara, Chuck Lever,
Alexander Aring, Trond Myklebust, Anna Schumaker, Steve French,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
NeilBrown, Olga Kornievskaia, Dai Ngo, Jonathan Corbet,
Amir Goldstein, Miklos Szeredi, Paulo Alcantara,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
David Howells, Tyler Hicks, Namjae Jeon, Steve French,
Sergey Senozhatsky, Carlos Maiolino, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Rick Macklem, linux-fsdevel,
linux-kernel, linux-nfs, linux-cifs, samba-technical, linux-doc,
netfs, ecryptfs, linux-unionfs, linux-xfs, linux-trace-kernel
On Fri 26-09-25 17:23:41, Jan Kara wrote:
> On Wed 24-09-25 14:05:53, Jeff Layton wrote:
> > In order to add directory delegation support, we need to break
> > delegations on the parent whenever there is going to be a change in the
> > directory.
> >
> > Rename vfs_create as __vfs_create, make it static, and add a new
> > delegated_inode parameter. Fix do_mknodat to call __vfs_create and wait
> > for a delegation break if there is one. Add a new exported vfs_create
> > wrapper that passes in NULL for delegated_inode.
> >
> > Signed-off-by: Jeff Layton <jlayton@kernel.org>
>
> Looks good. Feel free to add:
>
> Reviewed-by: Jan Kara <jack@suse.cz>
Sorry, I've sent this twise by mistake.
Honza
> > ---
> > fs/namei.c | 55 ++++++++++++++++++++++++++++++++++++-------------------
> > 1 file changed, 36 insertions(+), 19 deletions(-)
> >
> > diff --git a/fs/namei.c b/fs/namei.c
> > index 903b70a82530938a0fdf10508529a1b7cc38136d..d4b8330a3eb97e205dc2e71766fed1e45503323b 100644
> > --- a/fs/namei.c
> > +++ b/fs/namei.c
> > @@ -3370,6 +3370,32 @@ static inline umode_t vfs_prepare_mode(struct mnt_idmap *idmap,
> > return mode;
> > }
> >
> > +static int __vfs_create(struct mnt_idmap *idmap, struct inode *dir,
> > + struct dentry *dentry, umode_t mode, bool want_excl,
> > + struct inode **delegated_inode)
> > +{
> > + int error;
> > +
> > + error = may_create(idmap, dir, dentry);
> > + if (error)
> > + return error;
> > +
> > + if (!dir->i_op->create)
> > + return -EACCES; /* shouldn't it be ENOSYS? */
> > +
> > + mode = vfs_prepare_mode(idmap, dir, mode, S_IALLUGO, S_IFREG);
> > + error = security_inode_create(dir, dentry, mode);
> > + if (error)
> > + return error;
> > + error = try_break_deleg(dir, delegated_inode);
> > + if (error)
> > + return error;
> > + error = dir->i_op->create(idmap, dir, dentry, mode, want_excl);
> > + if (!error)
> > + fsnotify_create(dir, dentry);
> > + return error;
> > +}
> > +
> > /**
> > * vfs_create - create new file
> > * @idmap: idmap of the mount the inode was found from
> > @@ -3389,23 +3415,7 @@ static inline umode_t vfs_prepare_mode(struct mnt_idmap *idmap,
> > int vfs_create(struct mnt_idmap *idmap, struct inode *dir,
> > struct dentry *dentry, umode_t mode, bool want_excl)
> > {
> > - int error;
> > -
> > - error = may_create(idmap, dir, dentry);
> > - if (error)
> > - return error;
> > -
> > - if (!dir->i_op->create)
> > - return -EACCES; /* shouldn't it be ENOSYS? */
> > -
> > - mode = vfs_prepare_mode(idmap, dir, mode, S_IALLUGO, S_IFREG);
> > - error = security_inode_create(dir, dentry, mode);
> > - if (error)
> > - return error;
> > - error = dir->i_op->create(idmap, dir, dentry, mode, want_excl);
> > - if (!error)
> > - fsnotify_create(dir, dentry);
> > - return error;
> > + return __vfs_create(idmap, dir, dentry, mode, want_excl, NULL);
> > }
> > EXPORT_SYMBOL(vfs_create);
> >
> > @@ -4278,6 +4288,7 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode,
> > struct path path;
> > int error;
> > unsigned int lookup_flags = 0;
> > + struct inode *delegated_inode = NULL;
> >
> > error = may_mknod(mode);
> > if (error)
> > @@ -4296,8 +4307,9 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode,
> > idmap = mnt_idmap(path.mnt);
> > switch (mode & S_IFMT) {
> > case 0: case S_IFREG:
> > - error = vfs_create(idmap, path.dentry->d_inode,
> > - dentry, mode, true);
> > + error = __vfs_create(idmap, path.dentry->d_inode,
> > + dentry, mode, true,
> > + &delegated_inode);
> > if (!error)
> > security_path_post_mknod(idmap, dentry);
> > break;
> > @@ -4312,6 +4324,11 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode,
> > }
> > out2:
> > done_path_create(&path, dentry);
> > + if (delegated_inode) {
> > + error = break_deleg_wait(&delegated_inode);
> > + if (!error)
> > + goto retry;
> > + }
> > if (retry_estale(error, lookup_flags)) {
> > lookup_flags |= LOOKUP_REVAL;
> > goto retry;
> >
> > --
> > 2.51.0
> >
> --
> Jan Kara <jack@suse.com>
> SUSE Labs, CR
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [PATCH v3 08/38] vfs: make vfs_mknod break delegations on parent directory
2025-09-26 15:32 ` Jan Kara
@ 2025-09-26 16:27 ` Jeff Layton
0 siblings, 0 replies; 53+ messages in thread
From: Jeff Layton @ 2025-09-26 16:27 UTC (permalink / raw)
To: Jan Kara
Cc: Alexander Viro, Christian Brauner, Chuck Lever, Alexander Aring,
Trond Myklebust, Anna Schumaker, Steve French, Ronnie Sahlberg,
Shyam Prasad N, Tom Talpey, Bharath SM, NeilBrown,
Olga Kornievskaia, Dai Ngo, Jonathan Corbet, Amir Goldstein,
Miklos Szeredi, Paulo Alcantara, Greg Kroah-Hartman,
Rafael J. Wysocki, Danilo Krummrich, David Howells, Tyler Hicks,
Namjae Jeon, Steve French, Sergey Senozhatsky, Carlos Maiolino,
Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers, Rick Macklem,
linux-fsdevel, linux-kernel, linux-nfs, linux-cifs,
samba-technical, linux-doc, netfs, ecryptfs, linux-unionfs,
linux-xfs, linux-trace-kernel
On Fri, 2025-09-26 at 17:32 +0200, Jan Kara wrote:
> On Wed 24-09-25 14:05:54, Jeff Layton wrote:
> > In order to add directory delegation support, we need to break
> > delegations on the parent whenever there is going to be a change in the
> > directory.
> >
> > Rename vfs_mknod as __vfs_mknod, make it static, and add a new
> > delegated_inode parameter. Make do_mknodat call __vfs_mknod and wait
> > synchronously for delegation breaks to complete. Add a new exported
> > vfs_mknod wrapper that calls __vfs_mknod with a NULL delegated_inode
> > pointer.
> >
> > Signed-off-by: Jeff Layton <jlayton@kernel.org>
>
> Looks good. Feel free to add:
>
> Reviewed-by: Jan Kara <jack@suse.cz>
>
Thanks.
FYI, I've revised this and the rmdir patches to get rid of the wrapper,
and just have the callers pass in NULL directly. I think that's more
along the lines of what Christian preferred.
>
> > ---
> > fs/namei.c | 57 +++++++++++++++++++++++++++++++++++----------------------
> > 1 file changed, 35 insertions(+), 22 deletions(-)
> >
> > diff --git a/fs/namei.c b/fs/namei.c
> > index d4b8330a3eb97e205dc2e71766fed1e45503323b..7bcd898c84138061030f1f8b91273261cdf2a9b4 100644
> > --- a/fs/namei.c
> > +++ b/fs/namei.c
> > @@ -4215,24 +4215,9 @@ inline struct dentry *user_path_create(int dfd, const char __user *pathname,
> > }
> > EXPORT_SYMBOL(user_path_create);
> >
> > -/**
> > - * vfs_mknod - create device node or file
> > - * @idmap: idmap of the mount the inode was found from
> > - * @dir: inode of the parent directory
> > - * @dentry: dentry of the child device node
> > - * @mode: mode of the child device node
> > - * @dev: device number of device to create
> > - *
> > - * Create a device node or file.
> > - *
> > - * If the inode has been found through an idmapped mount the idmap of
> > - * the vfsmount must be passed through @idmap. This function will then take
> > - * care to map the inode according to @idmap before checking permissions.
> > - * On non-idmapped mounts or if permission checking is to be performed on the
> > - * raw inode simply pass @nop_mnt_idmap.
> > - */
> > -int vfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
> > - struct dentry *dentry, umode_t mode, dev_t dev)
> > +static int __vfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
> > + struct dentry *dentry, umode_t mode, dev_t dev,
> > + struct inode **delegated_inode)
> > {
> > bool is_whiteout = S_ISCHR(mode) && dev == WHITEOUT_DEV;
> > int error = may_create(idmap, dir, dentry);
> > @@ -4256,11 +4241,37 @@ int vfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
> > if (error)
> > return error;
> >
> > + error = try_break_deleg(dir, delegated_inode);
> > + if (error)
> > + return error;
> > +
> > error = dir->i_op->mknod(idmap, dir, dentry, mode, dev);
> > if (!error)
> > fsnotify_create(dir, dentry);
> > return error;
> > }
> > +
> > +/**
> > + * vfs_mknod - create device node or file
> > + * @idmap: idmap of the mount the inode was found from
> > + * @dir: inode of the parent directory
> > + * @dentry: dentry of the child device node
> > + * @mode: mode of the child device node
> > + * @dev: device number of device to create
> > + *
> > + * Create a device node or file.
> > + *
> > + * If the inode has been found through an idmapped mount the idmap of
> > + * the vfsmount must be passed through @idmap. This function will then take
> > + * care to map the inode according to @idmap before checking permissions.
> > + * On non-idmapped mounts or if permission checking is to be performed on the
> > + * raw inode simply pass @nop_mnt_idmap.
> > + */
> > +int vfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
> > + struct dentry *dentry, umode_t mode, dev_t dev)
> > +{
> > + return __vfs_mknod(idmap, dir, dentry, mode, dev, NULL);
> > +}
> > EXPORT_SYMBOL(vfs_mknod);
> >
> > static int may_mknod(umode_t mode)
> > @@ -4314,12 +4325,14 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode,
> > security_path_post_mknod(idmap, dentry);
> > break;
> > case S_IFCHR: case S_IFBLK:
> > - error = vfs_mknod(idmap, path.dentry->d_inode,
> > - dentry, mode, new_decode_dev(dev));
> > + error = __vfs_mknod(idmap, path.dentry->d_inode,
> > + dentry, mode, new_decode_dev(dev),
> > + &delegated_inode);
> > break;
> > case S_IFIFO: case S_IFSOCK:
> > - error = vfs_mknod(idmap, path.dentry->d_inode,
> > - dentry, mode, 0);
> > + error = __vfs_mknod(idmap, path.dentry->d_inode,
> > + dentry, mode, 0,
> > + &delegated_inode);
> > break;
> > }
> > out2:
> >
> > --
> > 2.51.0
> >
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 53+ messages in thread
end of thread, other threads:[~2025-09-26 16:27 UTC | newest]
Thread overview: 53+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-24 18:05 [PATCH v3 00/38] vfs, nfsd: implement directory delegations Jeff Layton
2025-09-24 18:05 ` [PATCH v3 01/38] filelock: push the S_ISREG check down to ->setlease handlers Jeff Layton
2025-09-24 18:05 ` [PATCH v3 02/38] filelock: add a lm_may_setlease lease_manager callback Jeff Layton
2025-09-24 18:05 ` [PATCH v3 03/38] vfs: add try_break_deleg calls for parents to vfs_{link,rename,unlink} Jeff Layton
2025-09-25 15:52 ` Jan Kara
2025-09-24 18:05 ` [PATCH v3 04/38] vfs: allow mkdir to wait for delegation break on parent Jeff Layton
2025-09-25 15:58 ` Jan Kara
2025-09-25 17:12 ` Jeff Layton
2025-09-24 18:05 ` [PATCH v3 05/38] vfs: allow rmdir " Jeff Layton
2025-09-25 17:13 ` Jeff Layton
2025-09-24 18:05 ` [PATCH v3 06/38] vfs: break parent dir delegations in open(..., O_CREAT) codepath Jeff Layton
2025-09-26 15:32 ` Jan Kara
2025-09-24 18:05 ` [PATCH v3 07/38] vfs: make vfs_create break delegations on parent directory Jeff Layton
2025-09-26 15:21 ` Jan Kara
2025-09-26 15:23 ` Jan Kara
2025-09-26 15:33 ` Jan Kara
2025-09-24 18:05 ` [PATCH v3 08/38] vfs: make vfs_mknod " Jeff Layton
2025-09-26 15:32 ` Jan Kara
2025-09-26 16:27 ` Jeff Layton
2025-09-24 18:05 ` [PATCH v3 09/38] filelock: lift the ban on directory leases in generic_setlease Jeff Layton
2025-09-24 18:05 ` [PATCH v3 10/38] nfsd: allow filecache to hold S_IFDIR files Jeff Layton
2025-09-24 18:05 ` [PATCH v3 11/38] nfsd: allow DELEGRETURN on directories Jeff Layton
2025-09-24 18:05 ` [PATCH v3 12/38] nfsd: check for delegation conflicts vs. the same client Jeff Layton
2025-09-24 18:05 ` [PATCH v3 13/38] nfsd: wire up GET_DIR_DELEGATION handling Jeff Layton
2025-09-24 18:06 ` [PATCH v3 14/38] filelock: rework the __break_lease API to use flags Jeff Layton
2025-09-24 18:06 ` [PATCH v3 15/38] filelock: add struct delegated_inode Jeff Layton
2025-09-24 18:06 ` [PATCH v3 16/38] filelock: add support for ignoring deleg breaks for dir change events Jeff Layton
2025-09-24 18:06 ` [PATCH v3 17/38] filelock: add a tracepoint to start of break_lease() Jeff Layton
2025-09-24 18:06 ` [PATCH v3 18/38] filelock: add an inode_lease_ignore_mask helper Jeff Layton
2025-09-24 18:06 ` [PATCH v3 19/38] nfsd: add protocol support for CB_NOTIFY Jeff Layton
2025-09-24 18:06 ` [PATCH v3 20/38] nfs_common: add new NOTIFY4_* flags proposed in RFC8881bis Jeff Layton
2025-09-24 18:06 ` [PATCH v3 21/38] nfsd: allow nfsd to get a dir lease with an ignore mask Jeff Layton
2025-09-24 18:06 ` [PATCH v3 22/38] vfs: add fsnotify_modify_mark_mask() Jeff Layton
2025-09-25 15:50 ` Jan Kara
2025-09-24 18:06 ` [PATCH v3 23/38] nfsd: update the fsnotify mark when setting or removing a dir delegation Jeff Layton
2025-09-24 18:06 ` [PATCH v3 24/38] nfsd: make nfsd4_callback_ops->prepare operation bool return Jeff Layton
2025-09-24 18:06 ` [PATCH v3 25/38] nfsd: add callback encoding and decoding linkages for CB_NOTIFY Jeff Layton
2025-09-24 18:06 ` [PATCH v3 26/38] nfsd: add data structures for handling CB_NOTIFY to directory delegation Jeff Layton
2025-09-24 18:06 ` [PATCH v3 27/38] nfsd: add notification handlers for dir events Jeff Layton
2025-09-25 17:15 ` Jeff Layton
2025-09-24 18:06 ` [PATCH v3 28/38] nfsd: add tracepoint to dir_event handler Jeff Layton
2025-09-24 18:06 ` [PATCH v3 29/38] nfsd: apply the notify mask to the delegation when requested Jeff Layton
2025-09-24 18:06 ` [PATCH v3 30/38] nfsd: add helper to marshal a fattr4 from completed args Jeff Layton
2025-09-24 18:06 ` [PATCH v3 31/38] nfsd: allow nfsd4_encode_fattr4_change() to work with no export Jeff Layton
2025-09-24 18:06 ` [PATCH v3 32/38] nfsd: send basic file attributes in CB_NOTIFY Jeff Layton
2025-09-24 18:06 ` [PATCH v3 33/38] nfsd: allow encoding a filehandle into fattr4 without a svc_fh Jeff Layton
2025-09-24 18:06 ` [PATCH v3 34/38] nfsd: add a fi_connectable flag to struct nfs4_file Jeff Layton
2025-09-24 18:06 ` [PATCH v3 35/38] nfsd: add the filehandle to returned attributes in CB_NOTIFY Jeff Layton
2025-09-24 18:06 ` [PATCH v3 36/38] nfsd: properly track requested child attributes Jeff Layton
2025-09-24 18:06 ` [PATCH v3 37/38] nfsd: track requested dir attributes Jeff Layton
2025-09-24 18:06 ` [PATCH v3 38/38] nfsd: add support to CB_NOTIFY for dir attribute changes Jeff Layton
2025-09-25 13:39 ` [PATCH v3 00/38] vfs, nfsd: implement directory delegations Chuck Lever
2025-09-25 17:08 ` Jeff Layton
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).