diff options
| author | Christian Brauner <brauner@kernel.org> | 2026-05-15 20:24:42 +0300 |
|---|---|---|
| committer | Christian Brauner <brauner@kernel.org> | 2026-05-15 20:24:42 +0300 |
| commit | 1e325f7debec9bb22c71bc7336135bf8f87b730a (patch) | |
| tree | 8fcf938f7319e27b31b8f6707e518350df4c305e /include | |
| parent | 254f49634ee16a731174d2ae34bc50bd5f45e731 (diff) | |
| parent | 010043003c0c81c0cedde267076cbe1e0911db49 (diff) | |
| download | linux-1e325f7debec9bb22c71bc7336135bf8f87b730a.tar.xz | |
Merge patch series "VFS changes for nfsd CB_NOTIFY callbacks in directory delegations"
The series starts with patches to allow the vfs to ignore certain types
of events on directories. nfsd can then request these sorts of
delegations on directories, and then set up inotify watches on the
directory to trigger sending CB_NOTIFY events.
* patches from https://patch.msgid.link/20260428-dir-deleg-v3-0-5a0780ba9def@kernel.org:
fsnotify: add FSNOTIFY_EVENT_RENAME data type
fsnotify: add fsnotify_modify_mark_mask()
fsnotify: new tracepoint in fsnotify()
filelock: add an inode_lease_ignore_mask helper
filelock: add a tracepoint to start of break_lease()
filelock: add support for ignoring deleg breaks for dir change events
filelock: pass current blocking lease to trace_break_lease_block() rather than "new_fl"
Link: https://patch.msgid.link/20260428-dir-deleg-v3-0-5a0780ba9def@kernel.org
Signed-off-by: Christian Brauner <brauner@kernel.org>
Diffstat (limited to 'include')
| -rw-r--r-- | include/linux/filelock.h | 54 | ||||
| -rw-r--r-- | include/linux/fsnotify.h | 8 | ||||
| -rw-r--r-- | include/linux/fsnotify_backend.h | 21 | ||||
| -rw-r--r-- | include/trace/events/filelock.h | 38 | ||||
| -rw-r--r-- | include/trace/events/fsnotify.h | 51 | ||||
| -rw-r--r-- | include/trace/misc/fsnotify.h | 35 |
6 files changed, 188 insertions, 19 deletions
diff --git a/include/linux/filelock.h b/include/linux/filelock.h index 5f0a2fb31450..6e125902c58a 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_DELEG BIT(2) /* NFSv4 delegation */ +#define FL_ACCESS BIT(3) /* not trying to lock, just looking */ +#define FL_EXISTS BIT(4) /* when unlocking, test for existence */ +#define FL_LEASE BIT(5) /* file lease */ +#define FL_CLOSE BIT(6) /* unlock on close */ +#define FL_SLEEP BIT(7) /* A blocking lock */ +#define FL_DOWNGRADE_PENDING BIT(8) /* Lease is being downgraded */ +#define FL_UNLOCK_PENDING BIT(9) /* Lease is being broken */ +#define FL_OFDLCK BIT(10) /* POSIX lock "owned" by struct file */ +#define FL_LAYOUT BIT(11) /* outstanding pNFS layout */ +#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) @@ -222,6 +225,10 @@ struct file_lease *locks_alloc_lease(void); #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(5) // dir deleg create event +#define LEASE_BREAK_DIR_DELETE BIT(6) // dir deleg delete event +#define LEASE_BREAK_DIR_RENAME BIT(7) // dir deleg rename event + int __break_lease(struct inode *inode, unsigned int flags); void lease_get_mtime(struct inode *, struct timespec64 *time); @@ -229,6 +236,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 *); @@ -516,12 +524,26 @@ static inline bool is_delegated(struct delegated_inode *di) return di->di_inode; } -static inline int try_break_deleg(struct inode *inode, +/** + * try_break_deleg - do a non-blocking delegation break + * @inode: inode that should have its delegations broken + * @flags: extra LEASE_BREAK_* flags to pass to break_deleg() + * @di: returns pointer to delegated inode (may be NULL) + * + * Break delegations in a non-blocking fashion. If there are + * outstanding delegations and @di is set, then an extra reference + * will be taken on @inode and @di->di_inode will be populated so + * that it may be waited upon. + * + * Returns 0 if there is no need to wait or an error. If -EWOULDBLOCK + * is returned, then @di will be populated (if non-NULL). + */ +static inline int try_break_deleg(struct inode *inode, unsigned int flags, struct delegated_inode *di) { int ret; - ret = break_deleg(inode, LEASE_BREAK_NONBLOCK); + ret = break_deleg(inode, flags | LEASE_BREAK_NONBLOCK); if (ret == -EWOULDBLOCK && di) { di->di_inode = inode; ihold(inode); @@ -574,7 +596,7 @@ static inline int break_deleg(struct inode *inode, unsigned int flags) return 0; } -static inline int try_break_deleg(struct inode *inode, +static inline int try_break_deleg(struct inode *inode, unsigned int flags, struct delegated_inode *delegated_inode) { return 0; diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index 079c18bcdbde..bda798bc67bc 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -257,6 +257,10 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir, __u32 new_dir_mask = FS_MOVED_TO; __u32 rename_mask = FS_RENAME; const struct qstr *new_name = &moved->d_name; + struct fsnotify_rename_data rd = { + .moved = moved, + .target = target, + }; if (isdir) { old_dir_mask |= FS_ISDIR; @@ -265,12 +269,12 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir, } /* Event with information about both old and new parent+name */ - fsnotify_name(rename_mask, moved, FSNOTIFY_EVENT_DENTRY, + fsnotify_name(rename_mask, &rd, FSNOTIFY_EVENT_RENAME, old_dir, old_name, 0); fsnotify_name(old_dir_mask, source, FSNOTIFY_EVENT_INODE, old_dir, old_name, fs_cookie); - fsnotify_name(new_dir_mask, source, FSNOTIFY_EVENT_INODE, + fsnotify_name(new_dir_mask, &rd, FSNOTIFY_EVENT_RENAME, new_dir, new_name, fs_cookie); if (target) diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 95985400d3d8..f8c8fb7f34ae 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -311,6 +311,7 @@ enum fsnotify_data_type { FSNOTIFY_EVENT_DENTRY, FSNOTIFY_EVENT_MNT, FSNOTIFY_EVENT_ERROR, + FSNOTIFY_EVENT_RENAME, }; struct fs_error_report { @@ -335,6 +336,11 @@ struct fsnotify_mnt { u64 mnt_id; }; +struct fsnotify_rename_data { + struct dentry *moved; /* the dentry that was renamed */ + struct inode *target; /* inode overwritten by rename, or NULL */ +}; + static inline struct inode *fsnotify_data_inode(const void *data, int data_type) { switch (data_type) { @@ -348,6 +354,8 @@ static inline struct inode *fsnotify_data_inode(const void *data, int data_type) return d_inode(file_range_path(data)->dentry); case FSNOTIFY_EVENT_ERROR: return ((struct fs_error_report *)data)->inode; + case FSNOTIFY_EVENT_RENAME: + return d_inode(((const struct fsnotify_rename_data *)data)->moved); default: return NULL; } @@ -363,6 +371,8 @@ static inline struct dentry *fsnotify_data_dentry(const void *data, int data_typ return ((const struct path *)data)->dentry; case FSNOTIFY_EVENT_FILE_RANGE: return file_range_path(data)->dentry; + case FSNOTIFY_EVENT_RENAME: + return ((struct fsnotify_rename_data *)data)->moved; default: return NULL; } @@ -395,6 +405,8 @@ static inline struct super_block *fsnotify_data_sb(const void *data, return file_range_path(data)->dentry->d_sb; case FSNOTIFY_EVENT_ERROR: return ((struct fs_error_report *) data)->sb; + case FSNOTIFY_EVENT_RENAME: + return ((const struct fsnotify_rename_data *)data)->moved->d_sb; default: return NULL; } @@ -430,6 +442,14 @@ static inline struct fs_error_report *fsnotify_data_error_report( } } +static inline struct inode *fsnotify_data_rename_target(const void *data, + int data_type) +{ + if (data_type == FSNOTIFY_EVENT_RENAME) + return ((const struct fsnotify_rename_data *)data)->target; + return NULL; +} + static inline const struct file_range *fsnotify_data_file_range( const void *data, int data_type) @@ -917,6 +937,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) { diff --git a/include/trace/events/filelock.h b/include/trace/events/filelock.h index 116774886244..f2bb6b7aa281 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, \ @@ -117,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(__entry->flags)) +); + DECLARE_EVENT_CLASS(filelock_lease, TP_PROTO(struct inode *inode, struct file_lease *fl), diff --git a/include/trace/events/fsnotify.h b/include/trace/events/fsnotify.h new file mode 100644 index 000000000000..341bbd57a39b --- /dev/null +++ b/include/trace/events/fsnotify.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM fsnotify + +#if !defined(_TRACE_FSNOTIFY_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_FSNOTIFY_H + +#include <linux/tracepoint.h> + +#include <trace/misc/fsnotify.h> + +TRACE_EVENT(fsnotify, + TP_PROTO(__u32 mask, const void *data, int data_type, + struct inode *dir, const struct qstr *file_name, + struct inode *inode, u32 cookie), + + TP_ARGS(mask, data, data_type, dir, file_name, inode, cookie), + + TP_STRUCT__entry( + __field(__u32, mask) + __field(unsigned long, dir_ino) + __field(unsigned long, ino) + __field(dev_t, s_dev) + __field(int, data_type) + __field(u32, cookie) + __string(file_name, file_name ? (const char *)file_name->name : "") + ), + + TP_fast_assign( + __entry->mask = mask; + __entry->dir_ino = dir ? dir->i_ino : 0; + __entry->ino = inode ? inode->i_ino : 0; + __entry->s_dev = dir ? dir->i_sb->s_dev : + inode ? inode->i_sb->s_dev : 0; + __entry->data_type = data_type; + __entry->cookie = cookie; + __assign_str(file_name); + ), + + TP_printk("dev=%d:%d dir=%lu ino=%lu data_type=%d cookie=0x%x mask=0x%x %s name=%s", + MAJOR(__entry->s_dev), MINOR(__entry->s_dev), + __entry->dir_ino, __entry->ino, + __entry->data_type, __entry->cookie, + __entry->mask, show_fsnotify_mask(__entry->mask), + __get_str(file_name)) +); + +#endif /* _TRACE_FSNOTIFY_H */ + +/* This part must be outside protection */ +#include <trace/define_trace.h> diff --git a/include/trace/misc/fsnotify.h b/include/trace/misc/fsnotify.h new file mode 100644 index 000000000000..a201e1bd6d8c --- /dev/null +++ b/include/trace/misc/fsnotify.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Display helpers for fsnotify events + */ + +#include <linux/fsnotify_backend.h> + +#define show_fsnotify_mask(mask) \ + __print_flags(mask, "|", \ + { FS_ACCESS, "ACCESS" }, \ + { FS_MODIFY, "MODIFY" }, \ + { FS_ATTRIB, "ATTRIB" }, \ + { FS_CLOSE_WRITE, "CLOSE_WRITE" }, \ + { FS_CLOSE_NOWRITE, "CLOSE_NOWRITE" }, \ + { FS_OPEN, "OPEN" }, \ + { FS_MOVED_FROM, "MOVED_FROM" }, \ + { FS_MOVED_TO, "MOVED_TO" }, \ + { FS_CREATE, "CREATE" }, \ + { FS_DELETE, "DELETE" }, \ + { FS_DELETE_SELF, "DELETE_SELF" }, \ + { FS_MOVE_SELF, "MOVE_SELF" }, \ + { FS_OPEN_EXEC, "OPEN_EXEC" }, \ + { FS_UNMOUNT, "UNMOUNT" }, \ + { FS_Q_OVERFLOW, "Q_OVERFLOW" }, \ + { FS_ERROR, "ERROR" }, \ + { FS_OPEN_PERM, "OPEN_PERM" }, \ + { FS_ACCESS_PERM, "ACCESS_PERM" }, \ + { FS_OPEN_EXEC_PERM, "OPEN_EXEC_PERM" }, \ + { FS_PRE_ACCESS, "PRE_ACCESS" }, \ + { FS_MNT_ATTACH, "MNT_ATTACH" }, \ + { FS_MNT_DETACH, "MNT_DETACH" }, \ + { FS_EVENT_ON_CHILD, "EVENT_ON_CHILD" }, \ + { FS_RENAME, "RENAME" }, \ + { FS_DN_MULTISHOT, "DN_MULTISHOT" }, \ + { FS_ISDIR, "ISDIR" }) |
