* [PATCH 0/1] fsnotify: Check parent watches without child dentry flags
@ 2026-10-07 15:20 Partha Sarathi Satapathy
2026-10-07 15:20 ` [PATCH 1/1] " Partha Sarathi Satapathy
2026-10-08 9:42 ` [PATCH 0/1] " Jan Kara
0 siblings, 2 replies; 6+ messages in thread
From: Partha Sarathi Satapathy @ 2026-10-07 15:20 UTC (permalink / raw)
To: partha.satapathy, jack, amir73il, viro, brauner, linux-fsdevel,
linux-kernel
This patch removes the cached-child dentry walk performed when a directory
first starts watching child events. Instead, the event path takes a reference
to the parent dentry and checks its child-watch mask. It also removes the
DCACHE_FSNOTIFY_PARENT_WATCHED flag and its update sites.
On the test host, 12 million files were populated and their positive dentries
were warmed before each run. With eight concurrent unlink/recreate workers
and 100 sole-watch add/close cycles, inotify_add_watch() measured:
Before After
run 1 average 107.420 ms 0.004 ms
run 2 average 125.433 ms 0.004 ms
run 1 maximum 122.811 ms 0.113 ms
run 2 maximum 141.198 ms 0.095 ms
The displayed 0.004 ms averages are rounded to three decimal places.
Maximum unlink/recreate latency was 777.852 and 770.569 ms before, versus
8.525 and 15.624 ms after. Those mutation maxima are supporting observations:
workers ran only during the watch-add loops, so the pre-change and post-change
mutation measurement windows had very different lengths.
One 100,000-iteration, single-worker open/close event-path run per mode gave:
Before After
none: p50/p99 ns 9564 / 15703 9684 / 15023
throughput 102221 ops/s 101432 ops/s
other: p50/p99 ns 9554 / 15573 9684 / 14962
throughput 99442 ops/s 101497 ops/s
parent: p50/p99 ns 12529 / 19179 12499 / 17716
throughput 78196 ops/s 78655 ops/s
The 'other' mode watches a different directory on the same filesystem, so it
exercises the superblock watcher path without target-file event delivery.
These are single runs; they do not establish a small event-path cost or its
absence. No open/close regression is apparent at this measurement resolution.
The test used worker CPU 2 and listener CPU 4 on kernels
6.19.0-rc8.V_fsn0.el9.omm0.x86_64 and 6.19.0-rc8.fsn3.el9.omm3.x86_64,
respectively. The test host reported XFS for its working directory.
The inotify correctness test and broader fsnotify functional test passed on
both kernels. The fanotify permission subtest skipped with EPERM in both runs,
so FAN_OPEN_PERM remains unvalidated by these results.
Partha Sarathi Satapathy (1):
fsnotify: Check parent watches without child dentry flags
fs/dcache.c | 4 --
fs/notify/fsnotify.c | 99 ++++++++------------------------
fs/notify/fsnotify.h | 6 --
fs/notify/mark.c | 35 -----------
include/linux/dcache.h | 1 -
include/linux/fsnotify.h | 7 +--
include/linux/fsnotify_backend.h | 29 ++--------
7 files changed, 29 insertions(+), 152 deletions(-)
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 1/1] fsnotify: Check parent watches without child dentry flags 2026-10-07 15:20 [PATCH 0/1] fsnotify: Check parent watches without child dentry flags Partha Sarathi Satapathy @ 2026-10-07 15:20 ` Partha Sarathi Satapathy 2026-10-08 9:42 ` [PATCH 0/1] " Jan Kara 1 sibling, 0 replies; 6+ messages in thread From: Partha Sarathi Satapathy @ 2026-10-07 15:20 UTC (permalink / raw) To: partha.satapathy, jack, amir73il, viro, brauner, linux-fsdevel, linux-kernel Enabling child watches currently walks all cached children of a directory to set DCACHE_FSNOTIFY_PARENT_WATCHED. This makes watch installation proportional to the number of cached dentries and takes each child's d_lock. Instead, obtain a referenced parent and read its child-watch mask in __fsnotify_parent() when deciding whether to include parent and name information. Check the parent directly in fsnotify_open_perm_and_set_mode() so permission events are handled before the normal open notification. Remove the bulk walk, the dentry flag, and its dcache updates. This moves work from watch installation to events: events on a superblock with watchers now acquire and release a parent dentry reference. Signed-off-by: Partha Sarathi Satapathy <partha.satapathy@oracle.com> --- Tested on a 12-million-file directory with eight mutation workers and 100 watch-add cycles: the two before/after average watch-add latencies were 107.420/0.004 ms and 125.433/0.004 ms. Inotify functional tests passed on both kernels; the fanotify permission test skipped with EPERM. See the cover letter for event-path measurements and their single-run limitation. fs/dcache.c | 4 -- fs/notify/fsnotify.c | 99 ++++++++------------------------ fs/notify/fsnotify.h | 6 -- fs/notify/mark.c | 35 ----------- include/linux/dcache.h | 1 - include/linux/fsnotify.h | 7 +-- include/linux/fsnotify_backend.h | 29 ++-------- 7 files changed, 29 insertions(+), 152 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index 66dd1bb830d1..2262620e49f3 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1984,7 +1984,6 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode) raw_write_seqcount_begin(&dentry->d_seq); __d_set_inode_and_type(dentry, inode, add_flags); raw_write_seqcount_end(&dentry->d_seq); - fsnotify_update_flags(dentry); } /** @@ -2764,7 +2763,6 @@ static inline void __d_add(struct dentry *dentry, struct inode *inode, raw_write_seqcount_begin(&dentry->d_seq); __d_set_inode_and_type(dentry, inode, add_flags); raw_write_seqcount_end(&dentry->d_seq); - fsnotify_update_flags(dentry); } __d_rehash(dentry); if (dir) @@ -2939,13 +2937,11 @@ static void __d_move(struct dentry *dentry, struct dentry *target, __hlist_del(&target->d_sib); hlist_add_head(&target->d_sib, &target->d_parent->d_children); __d_rehash(target); - fsnotify_update_flags(target); } if (!hlist_unhashed(&dentry->d_sib)) __hlist_del(&dentry->d_sib); hlist_add_head(&dentry->d_sib, &dentry->d_parent->d_children); __d_rehash(dentry); - fsnotify_update_flags(dentry); fscrypt_handle_d_move(dentry); write_seqcount_end(&target->d_seq); diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 71bd44e5ab6d..88ea931148c9 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -115,61 +115,6 @@ void fsnotify_sb_free(struct super_block *sb) kfree(sb->s_fsnotify_info); } -/* - * Given an inode, first check if we care what happens to our children. Inotify - * and dnotify both tell their parents about events. If we care about any event - * on a child we run all of our children and set a dentry flag saying that the - * parent cares. Thus when an event happens on a child it can quickly tell - * if there is a need to find a parent and send the event to the parent. - */ -void fsnotify_set_children_dentry_flags(struct inode *inode) -{ - struct dentry *alias; - - if (!S_ISDIR(inode->i_mode)) - return; - - spin_lock(&inode->i_lock); - /* run all of the dentries associated with this inode. Since this is a - * directory, there damn well better only be one item on this list */ - hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) { - struct dentry *child; - - /* run all of the children of the original inode and fix their - * d_flags to indicate parental interest (their parent is the - * original inode) */ - spin_lock(&alias->d_lock); - hlist_for_each_entry(child, &alias->d_children, d_sib) { - if (!child->d_inode) - continue; - - spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED); - child->d_flags |= DCACHE_FSNOTIFY_PARENT_WATCHED; - spin_unlock(&child->d_lock); - } - spin_unlock(&alias->d_lock); - } - spin_unlock(&inode->i_lock); -} - -/* - * Lazily clear false positive PARENT_WATCHED flag for child whose parent had - * stopped watching children. - */ -static void fsnotify_clear_child_dentry_flag(struct inode *pinode, - struct dentry *dentry) -{ - spin_lock(&dentry->d_lock); - /* - * d_lock is a sufficient barrier to prevent observing a non-watched - * parent state from before the fsnotify_set_children_dentry_flags() - * or fsnotify_update_flags() call that had set PARENT_WATCHED. - */ - if (!fsnotify_inode_watches_children(pinode)) - dentry->d_flags &= ~DCACHE_FSNOTIFY_PARENT_WATCHED; - spin_unlock(&dentry->d_lock); -} - /* Are inode/sb/mount interested in parent and name info with this event? */ static bool fsnotify_event_needs_parent(struct inode *inode, __u32 mnt_mask, __u32 mask) @@ -237,35 +182,37 @@ int fsnotify_pre_content(const struct path *path, const loff_t *ppos, int __fsnotify_parent(struct dentry *dentry, __u32 mask, const void *data, int data_type) { - const struct path *path = fsnotify_data_path(data, data_type); - __u32 mnt_mask = path ? - READ_ONCE(real_mount(path->mnt)->mnt_fsnotify_mask) : 0; struct inode *inode = d_inode(dentry); - struct dentry *parent; - bool parent_watched = dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED; + struct dentry *parent = dget_parent(dentry); + struct inode *p_inode = d_inode(parent); + __u32 parent_mask = fsnotify_inode_watches_children(p_inode); + const struct path *path; + __u32 mnt_mask; bool parent_needed, parent_interested; - __u32 p_mask; - struct inode *p_inode = NULL; struct name_snapshot name; struct qstr *file_name = NULL; int ret = 0; + /* sb/mount marks are not interested in the name of a directory. */ + if (S_ISDIR(inode->i_mode) && !parent_mask) { + ret = fsnotify(mask, data, data_type, NULL, NULL, inode, 0); + goto out; + } + + path = fsnotify_data_path(data, data_type); + mnt_mask = path ? + READ_ONCE(real_mount(path->mnt)->mnt_fsnotify_mask) : 0; + /* Optimize the likely case of nobody watching this path */ - if (likely(!parent_watched && + if (likely(!parent_mask && !fsnotify_object_watched(inode, mnt_mask, mask))) - return 0; + goto out; - parent = NULL; parent_needed = fsnotify_event_needs_parent(inode, mnt_mask, mask); - if (!parent_watched && !parent_needed) + if (!parent_mask && !parent_needed) { + p_inode = NULL; goto notify; - - /* Does parent inode care about events on children? */ - parent = dget_parent(dentry); - p_inode = parent->d_inode; - p_mask = fsnotify_inode_watches_children(p_inode); - if (unlikely(parent_watched && !p_mask)) - fsnotify_clear_child_dentry_flag(p_inode, dentry); + } /* * Include parent/name in notification either if some notification @@ -275,7 +222,7 @@ int __fsnotify_parent(struct dentry *dentry, __u32 mask, const void *data, * events can provide an undesirable side-channel for information * exfiltration. */ - parent_interested = mask & p_mask & ALL_FSNOTIFY_EVENTS && + parent_interested = mask & parent_mask & ALL_FSNOTIFY_EVENTS && !(data_type == FSNOTIFY_EVENT_PATH && d_is_special(dentry) && (mask & (FS_ACCESS | FS_MODIFY))); @@ -295,8 +242,8 @@ int __fsnotify_parent(struct dentry *dentry, __u32 mask, const void *data, if (file_name) release_dentry_name_snapshot(&name); +out: dput(parent); - return ret; } EXPORT_SYMBOL_GPL(__fsnotify_parent); @@ -696,7 +643,7 @@ int fsnotify_open_perm_and_set_mode(struct file *file) mnt_mask = READ_ONCE(real_mount(file->f_path.mnt)->mnt_fsnotify_mask); p_mask = fsnotify_object_watched(d_inode(dentry), mnt_mask, ALL_FSNOTIFY_PERM_EVENTS); - if (dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED) { + if (!IS_ROOT(dentry)) { parent = dget_parent(dentry); p_mask |= fsnotify_inode_watches_children(d_inode(parent)); dput(parent); diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h index 5950c7a67f41..5bdf37bc97f2 100644 --- a/fs/notify/fsnotify.h +++ b/fs/notify/fsnotify.h @@ -100,12 +100,6 @@ static inline void fsnotify_clear_marks_by_mntns(struct mnt_namespace *mntns) fsnotify_destroy_marks(&mntns->n_fsnotify_marks); } -/* - * update the dentry->d_flags of all of inode's children to indicate if inode cares - * about events that happen to its children. - */ -extern void fsnotify_set_children_dentry_flags(struct inode *inode); - extern struct kmem_cache *fsnotify_mark_connector_cachep; #endif /* __FS_NOTIFY_FSNOTIFY_H_ */ diff --git a/fs/notify/mark.c b/fs/notify/mark.c index 55a03bb05aa1..3fe71cfd4fe9 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -264,24 +264,6 @@ static void *__fsnotify_recalc_mask(struct fsnotify_mark_connector *conn) return fsnotify_update_iref(conn, want_iref); } -static bool fsnotify_conn_watches_children( - struct fsnotify_mark_connector *conn) -{ - if (conn->type != FSNOTIFY_OBJ_TYPE_INODE) - return false; - - return fsnotify_inode_watches_children(fsnotify_conn_inode(conn)); -} - -static void fsnotify_conn_set_children_dentry_flags( - struct fsnotify_mark_connector *conn) -{ - if (conn->type != FSNOTIFY_OBJ_TYPE_INODE) - return; - - fsnotify_set_children_dentry_flags(fsnotify_conn_inode(conn)); -} - /* * Calculate mask of events for a list of marks. The caller must make sure * connector and connector->obj cannot disappear under us. Callers achieve @@ -290,23 +272,12 @@ static void fsnotify_conn_set_children_dentry_flags( */ void fsnotify_recalc_mask(struct fsnotify_mark_connector *conn) { - bool update_children; - if (!conn) return; spin_lock(&conn->lock); - update_children = !fsnotify_conn_watches_children(conn); __fsnotify_recalc_mask(conn); - update_children &= fsnotify_conn_watches_children(conn); spin_unlock(&conn->lock); - /* - * Set children's PARENT_WATCHED flags only if parent started watching. - * When parent stops watching, we clear false positive PARENT_WATCHED - * flags lazily in __fsnotify_parent(). - */ - if (update_children) - fsnotify_conn_set_children_dentry_flags(conn); } /* Free all connectors queued for freeing once SRCU period ends */ @@ -430,12 +401,6 @@ void fsnotify_put_mark(struct fsnotify_mark *mark) spin_unlock(&destroy_lock); queue_work(system_dfl_wq, &connector_reaper_work); } - /* - * Note that we didn't update flags telling whether inode cares about - * what's happening with children. We update these flags from - * __fsnotify_parent() lazily when next event happens on one of our - * children. - */ spin_lock(&destroy_lock); list_add(&mark->g_list, &destroy_list); spin_unlock(&destroy_lock); diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 898c60d21c92..cd23b819cca9 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -205,7 +205,6 @@ enum dentry_flags { * last dput() */ DCACHE_NFSFS_RENAMED = BIT(12), - DCACHE_FSNOTIFY_PARENT_WATCHED = BIT(13), /* Parent inode is watched by some fsnotify listener */ DCACHE_DENTRY_KILLED = BIT(14), DCACHE_MOUNTED = BIT(15), /* is a mountpoint */ DCACHE_NEED_AUTOMOUNT = BIT(16), /* handle automount on this dir */ diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index 28a9cb13fbfa..a664c2f8f4a5 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -81,14 +81,9 @@ static inline int fsnotify_parent(struct dentry *dentry, __u32 mask, if (!fsnotify_sb_has_watchers(inode->i_sb)) return 0; - if (S_ISDIR(inode->i_mode)) { + if (S_ISDIR(inode->i_mode)) mask |= FS_ISDIR; - /* sb/mount marks are not interested in name of directory */ - if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED)) - goto notify_child; - } - /* disconnected dentry cannot notify parent */ if (IS_ROOT(dentry)) goto notify_child; diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 0d954ea7b179..faa7f3818491 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -680,27 +680,6 @@ static inline int fsnotify_inode_watches_children(struct inode *inode) return parent_mask & FS_EVENTS_POSS_ON_CHILD; } -/* - * Update the dentry with a flag indicating the interest of its parent to receive - * filesystem events when those events happens to this dentry->d_inode. - */ -static inline void fsnotify_update_flags(struct dentry *dentry) -{ - assert_spin_locked(&dentry->d_lock); - - /* - * Serialisation of setting PARENT_WATCHED on the dentries is provided - * by d_lock. If inotify_inode_watched changes after we have taken - * d_lock, the following fsnotify_set_children_dentry_flags call will - * find our entry, so it will spin until we complete here, and update - * us with the new state. - */ - if (fsnotify_inode_watches_children(dentry->d_parent->d_inode)) - dentry->d_flags |= DCACHE_FSNOTIFY_PARENT_WATCHED; - else - dentry->d_flags &= ~DCACHE_FSNOTIFY_PARENT_WATCHED; -} - /* called from fsnotify listeners, such as fanotify or dnotify */ /* create a new group */ @@ -943,6 +922,11 @@ static inline int __fsnotify_parent(struct dentry *dentry, __u32 mask, return 0; } +static inline int fsnotify_inode_watches_children(struct inode *inode) +{ + return 0; +} + static inline void __fsnotify_inode_delete(struct inode *inode) {} @@ -958,9 +942,6 @@ static inline void __fsnotify_mntns_delete(struct mnt_namespace *mntns) static inline void fsnotify_sb_free(struct super_block *sb) {} -static inline void fsnotify_update_flags(struct dentry *dentry) -{} - static inline u32 fsnotify_get_cookie(void) { return 0; ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH 0/1] fsnotify: Check parent watches without child dentry flags 2026-10-07 15:20 [PATCH 0/1] fsnotify: Check parent watches without child dentry flags Partha Sarathi Satapathy 2026-10-07 15:20 ` [PATCH 1/1] " Partha Sarathi Satapathy @ 2026-10-08 9:42 ` Jan Kara 2026-10-09 11:48 ` [External] : " Partha Satapathy 1 sibling, 1 reply; 6+ messages in thread From: Jan Kara @ 2026-10-08 9:42 UTC (permalink / raw) To: Partha Sarathi Satapathy Cc: jack, amir73il, viro, brauner, linux-fsdevel, linux-kernel On Wed 07-10-26 15:20:50, Partha Sarathi Satapathy wrote: > This patch removes the cached-child dentry walk performed when a directory > first starts watching child events. Instead, the event path takes a reference > to the parent dentry and checks its child-watch mask. It also removes the > DCACHE_FSNOTIFY_PARENT_WATCHED flag and its update sites. If we can get rid of it, it would be a nice simplification but I have doubts this is feasible. > On the test host, 12 million files were populated and their positive dentries > were warmed before each run. With eight concurrent unlink/recreate workers > and 100 sole-watch add/close cycles, inotify_add_watch() measured: > > Before After > run 1 average 107.420 ms 0.004 ms > run 2 average 125.433 ms 0.004 ms > run 1 maximum 122.811 ms 0.113 ms > run 2 maximum 141.198 ms 0.095 ms > > The displayed 0.004 ms averages are rounded to three decimal places. > Maximum unlink/recreate latency was 777.852 and 770.569 ms before, versus > 8.525 and 15.624 ms after. Those mutation maxima are supporting observations: > workers ran only during the watch-add loops, so the pre-change and post-change > mutation measurement windows had very different lengths. This looks good and is kind of expected. We don't have to traverse the huge children list. > One 100,000-iteration, single-worker open/close event-path run per mode gave: > > Before After > none: p50/p99 ns 9564 / 15703 9684 / 15023 > throughput 102221 ops/s 101432 ops/s > other: p50/p99 ns 9554 / 15573 9684 / 14962 > throughput 99442 ops/s 101497 ops/s > parent: p50/p99 ns 12529 / 19179 12499 / 17716 > throughput 78196 ops/s 78655 ops/s > > The 'other' mode watches a different directory on the same filesystem, so it > exercises the superblock watcher path without target-file event delivery. > These are single runs; they do not establish a small event-path cost or its > absence. No open/close regression is apparent at this measurement resolution. > The test used worker CPU 2 and listener CPU 4 on kernels > 6.19.0-rc8.V_fsn0.el9.omm0.x86_64 and 6.19.0-rc8.fsn3.el9.omm3.x86_64, > respectively. The test host reported XFS for its working directory. > > The inotify correctness test and broader fsnotify functional test passed on > both kernels. The fanotify permission subtest skipped with EPERM in both runs, > so FAN_OPEN_PERM remains unvalidated by these results. This isn't a load for which the DCACHE_FSNOTIFY_PARENT_WATCHED optimization is that interesting. Try the following: Create a directory on tmpfs with say 1024 files, each 4k large. Place IN_MODIFY inotify watch on the directory. Start X clients (where X can be 1,2,4,8,...,1024 to see the scaling) The client will open the file based on its number (so different clients work on different file) Do 1000000 writes of 1 byte at offset 0 to the file => measure time for this Close the file I would bet you would see the difference already at 1 client and as the number of clients grows and the cacheline contention on the parent's refcount increases, it will get progressively worse. In particular if you try on a NUMA machine where the cacheline will ping-pong across NUMA nodes. We've got performance regression reports for similar loads already when we added one cacheline load to fsnotify_parent(). Doing the "grab parent refcount" dance is much more expensive than that. Honza > > Partha Sarathi Satapathy (1): > fsnotify: Check parent watches without child dentry flags > > fs/dcache.c | 4 -- > fs/notify/fsnotify.c | 99 ++++++++------------------------ > fs/notify/fsnotify.h | 6 -- > fs/notify/mark.c | 35 ----------- > include/linux/dcache.h | 1 - > include/linux/fsnotify.h | 7 +-- > include/linux/fsnotify_backend.h | 29 ++-------- > 7 files changed, 29 insertions(+), 152 deletions(-) -- Jan Kara <jack@suse.com> SUSE Labs, CR ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [External] : Re: [PATCH 0/1] fsnotify: Check parent watches without child dentry flags 2026-10-08 9:42 ` [PATCH 0/1] " Jan Kara @ 2026-10-09 11:48 ` Partha Satapathy 2026-10-12 8:40 ` Jan Kara 0 siblings, 1 reply; 6+ messages in thread From: Partha Satapathy @ 2026-10-09 11:48 UTC (permalink / raw) To: Jan Kara; +Cc: amir73il, viro, brauner, linux-fsdevel, linux-kernel On 08-10-2026 15:12, Jan Kara wrote: > On Wed 07-10-26 15:20:50, Partha Sarathi Satapathy wrote: >> This patch removes the cached-child dentry walk performed when a directory >> first starts watching child events. Instead, the event path takes a reference >> to the parent dentry and checks its child-watch mask. It also removes the >> DCACHE_FSNOTIFY_PARENT_WATCHED flag and its update sites. > > If we can get rid of it, it would be a nice simplification but I have > doubts this is feasible. > >> On the test host, 12 million files were populated and their positive dentries >> were warmed before each run. With eight concurrent unlink/recreate workers >> and 100 sole-watch add/close cycles, inotify_add_watch() measured: >> >> Before After >> run 1 average 107.420 ms 0.004 ms >> run 2 average 125.433 ms 0.004 ms >> run 1 maximum 122.811 ms 0.113 ms >> run 2 maximum 141.198 ms 0.095 ms >> >> The displayed 0.004 ms averages are rounded to three decimal places. >> Maximum unlink/recreate latency was 777.852 and 770.569 ms before, versus >> 8.525 and 15.624 ms after. Those mutation maxima are supporting observations: >> workers ran only during the watch-add loops, so the pre-change and post-change >> mutation measurement windows had very different lengths. > > This looks good and is kind of expected. We don't have to traverse the huge > children list. > >> One 100,000-iteration, single-worker open/close event-path run per mode gave: >> >> Before After >> none: p50/p99 ns 9564 / 15703 9684 / 15023 >> throughput 102221 ops/s 101432 ops/s >> other: p50/p99 ns 9554 / 15573 9684 / 14962 >> throughput 99442 ops/s 101497 ops/s >> parent: p50/p99 ns 12529 / 19179 12499 / 17716 >> throughput 78196 ops/s 78655 ops/s >> >> The 'other' mode watches a different directory on the same filesystem, so it >> exercises the superblock watcher path without target-file event delivery. >> These are single runs; they do not establish a small event-path cost or its >> absence. No open/close regression is apparent at this measurement resolution. >> The test used worker CPU 2 and listener CPU 4 on kernels >> 6.19.0-rc8.V_fsn0.el9.omm0.x86_64 and 6.19.0-rc8.fsn3.el9.omm3.x86_64, >> respectively. The test host reported XFS for its working directory. >> >> The inotify correctness test and broader fsnotify functional test passed on >> both kernels. The fanotify permission subtest skipped with EPERM in both runs, >> so FAN_OPEN_PERM remains unvalidated by these results. > > This isn't a load for which the DCACHE_FSNOTIFY_PARENT_WATCHED optimization > is that interesting. Try the following: > > Create a directory on tmpfs with say 1024 files, each 4k large. > Place IN_MODIFY inotify watch on the directory. > Start X clients (where X can be 1,2,4,8,...,1024 to see the scaling) > The client will open the file based on its number (so different clients > work on different file) > Do 1000000 writes of 1 byte at offset 0 to the file => measure time for > this > Close the file > > I would bet you would see the difference already at 1 client and as the > number of clients grows and the cacheline contention on the parent's > refcount increases, it will get progressively worse. In particular if you > try on a NUMA machine where the cacheline will ping-pong across NUMA nodes. > > We've got performance regression reports for similar loads already when we > added one cacheline load to fsnotify_parent(). Doing the "grab parent > refcount" dance is much more expensive than that. > > Honza > Hi Honza, Thanks for suggesting this workload. The setup and before/after timing comparisons are below. Architecture: x86_64 CPU(s): 512 On-line CPU(s) list: 0-511 NUMA: NUMA node(s): 2 NUMA node0 CPU(s): 0-127,256-383 NUMA node1 CPU(s): 128-255,384-511 Testcase : Using /dev/shm tmpfs to create private test directory. Create 1,024 files each fully populated with 4kb. Add IN_MODIFY inotify watch to the directory. Start a listener thread to read notifications and count queue overflows. Start X(1,2,4..1024) client threads; each opens a different file once. Wait until every client is ready, then release them together. Each client starts its timer and performs 1000000 one-byte pwrite() at offset0 on its own file. Each client stops its timer and closes its file. Wait for all clients to finish, drain remaining notifications, and stop the listener. Print write times, throughput, notification count, and queue-overflow count. Close inotify and delete the test files and directory. Note : File creation, open, close, and cleanup are outside the timed write loops. Run 1: ------- | Clients | Before average time | After average time | Time change | |---:|---:|---:|---:| | 1 | 1.392 s | 1.484 s | +6.6% | | 2 | 2.205 s | 2.402 s | +8.9% | | 4 | 3.969 s | 3.644 s | −8.2% | | 8 | 5.851 s | 5.804 s | −0.8% | | 16 | 10.607 s | 11.538 s | +8.8% | | 32 | 21.093 s | 22.492 s | +6.6% | | 64 | 48.698 s | 49.576 s | +1.8% | | 128 | 100.208 s | 100.467 s | +0.3% | | 256 | 198.773 s | 197.669 s | −0.6% | | 512 | 400.056 s | 399.550 s | −0.1% | Run2 : -------- | Clients | Before average time | After average time | Time change | |---:|---:|---:|---:| | 1 | 1.491 s | 1.484 s | −0.4% | | 2 | 2.391 s | 2.402 s | +0.5% | | 4 | 4.050 s | 3.644 s | −10.0% | | 8 | 6.630 s | 5.804 s | −12.5% | | 16 | 11.931 s | 11.538 s | −3.3% | | 32 | 22.513 s | 22.492 s | −0.1% | | 64 | 50.231 s | 49.576 s | −1.3% | | 128 | 100.699 s | 100.467 s | −0.2% | | 256 | 198.771 s | 197.669 s | −0.6% | | 512 | 401.860 s | 399.550 s | −0.6% | | 1,024 | 803.812 s | 806.593 s | +0.3% | Positive means After Fix took longer; negative means faster. The measured write performance is broadly comparable before and after the fix, with no consistent substantial regression or progressively worsening slowdown as concurrency increases. The differences vary between baseline batches: the increases at 16 and 32 clients in the first comparison disappear or reverse in the second. At 128–1024 clients, average write times differ by approximately 0.6% or less. These shifts are consistent with run-to-run variation; the results do not show a persistent substantial slowdown from the fix. Thanks, Partha >> >> Partha Sarathi Satapathy (1): >> fsnotify: Check parent watches without child dentry flags >> >> fs/dcache.c | 4 -- >> fs/notify/fsnotify.c | 99 ++++++++------------------------ >> fs/notify/fsnotify.h | 6 -- >> fs/notify/mark.c | 35 ----------- >> include/linux/dcache.h | 1 - >> include/linux/fsnotify.h | 7 +-- >> include/linux/fsnotify_backend.h | 29 ++-------- >> 7 files changed, 29 insertions(+), 152 deletions(-) > -- > Jan Kara <jack@suse.com> > SUSE Labs, CR > ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [External] : Re: [PATCH 0/1] fsnotify: Check parent watches without child dentry flags 2026-10-09 11:48 ` [External] : " Partha Satapathy @ 2026-10-12 8:40 ` Jan Kara 2026-10-13 6:38 ` Partha Satapathy 0 siblings, 1 reply; 6+ messages in thread From: Jan Kara @ 2026-10-12 8:40 UTC (permalink / raw) To: Partha Satapathy Cc: Jan Kara, amir73il, viro, brauner, linux-fsdevel, linux-kernel On Fri 09-10-26 17:18:17, Partha Satapathy wrote: > Hi Honza, > > Thanks for suggesting this workload. > The setup and before/after timing comparisons are below. > > Architecture: x86_64 > CPU(s): 512 > On-line CPU(s) list: 0-511 > > NUMA: > NUMA node(s): 2 > NUMA node0 CPU(s): 0-127,256-383 > NUMA node1 CPU(s): 128-255,384-511 > > Testcase : > Using /dev/shm tmpfs to create private test directory. > Create 1,024 files each fully populated with 4kb. > Add IN_MODIFY inotify watch to the directory. > Start a listener thread to read notifications and count queue overflows. > Start X(1,2,4..1024) client threads; each opens a different file once. > Wait until every client is ready, then release them together. > Each client starts its timer and performs 1000000 > one-byte pwrite() at offset0 on its own file. > Each client stops its timer and closes its file. > Wait for all clients to finish, drain remaining notifications, > and stop the listener. > Print write times, throughput, notification count, and queue-overflow count. > Close inotify and delete the test files and directory. > > Note : File creation, open, close, and cleanup are outside the timed > write loops. > > Run 1: > ------- > > | Clients | Before average time | After average time | Time change | > |---:|---:|---:|---:| > | 1 | 1.392 s | 1.484 s | +6.6% | > | 2 | 2.205 s | 2.402 s | +8.9% | > | 4 | 3.969 s | 3.644 s | −8.2% | > | 8 | 5.851 s | 5.804 s | −0.8% | > | 16 | 10.607 s | 11.538 s | +8.8% | > | 32 | 21.093 s | 22.492 s | +6.6% | > | 64 | 48.698 s | 49.576 s | +1.8% | > | 128 | 100.208 s | 100.467 s | +0.3% | > | 256 | 198.773 s | 197.669 s | −0.6% | > | 512 | 400.056 s | 399.550 s | −0.1% | > > > Run2 : > -------- > > | Clients | Before average time | After average time | Time change | > |---:|---:|---:|---:| > | 1 | 1.491 s | 1.484 s | −0.4% | > | 2 | 2.391 s | 2.402 s | +0.5% | > | 4 | 4.050 s | 3.644 s | −10.0% | > | 8 | 6.630 s | 5.804 s | −12.5% | > | 16 | 11.931 s | 11.538 s | −3.3% | > | 32 | 22.513 s | 22.492 s | −0.1% | > | 64 | 50.231 s | 49.576 s | −1.3% | > | 128 | 100.699 s | 100.467 s | −0.2% | > | 256 | 198.771 s | 197.669 s | −0.6% | > | 512 | 401.860 s | 399.550 s | −0.6% | > | 1,024 | 803.812 s | 806.593 s | +0.3% | > > > Positive means After Fix took longer; negative means faster. > > > The measured write performance is broadly comparable before and after > the fix, with no consistent substantial regression or progressively > worsening slowdown as concurrency increases. > > The differences vary between baseline batches: the increases at 16 > and 32 clients in the first comparison disappear or reverse in the > second. At 128–1024 clients, average write times differ by approximately > 0.6% or less. These shifts are consistent with run-to-run variation; > the results do not show a persistent substantial slowdown from the fix. Thanks for running these tests. Results are very interesting. Can you please share your benchmarking program with me? I'll check it and try to play with it on my end. Thanks! Honza -- Jan Kara <jack@suse.com> SUSE Labs, CR ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [External] : Re: [PATCH 0/1] fsnotify: Check parent watches without child dentry flags 2026-10-12 8:40 ` Jan Kara @ 2026-10-13 6:38 ` Partha Satapathy 0 siblings, 0 replies; 6+ messages in thread From: Partha Satapathy @ 2026-10-13 6:38 UTC (permalink / raw) To: Jan Kara; +Cc: amir73il, viro, brauner, linux-fsdevel, linux-kernel On 12-10-2026 14:10, Jan Kara wrote: > On Fri 09-10-26 17:18:17, Partha Satapathy wrote: >> Hi Honza, >> >> Thanks for suggesting this workload. >> The setup and before/after timing comparisons are below. >> >> Architecture: x86_64 >> CPU(s): 512 >> On-line CPU(s) list: 0-511 >> >> NUMA: >> NUMA node(s): 2 >> NUMA node0 CPU(s): 0-127,256-383 >> NUMA node1 CPU(s): 128-255,384-511 >> >> Testcase : >> Using /dev/shm tmpfs to create private test directory. >> Create 1,024 files each fully populated with 4kb. >> Add IN_MODIFY inotify watch to the directory. >> Start a listener thread to read notifications and count queue overflows. >> Start X(1,2,4..1024) client threads; each opens a different file once. >> Wait until every client is ready, then release them together. >> Each client starts its timer and performs 1000000 >> one-byte pwrite() at offset0 on its own file. >> Each client stops its timer and closes its file. >> Wait for all clients to finish, drain remaining notifications, >> and stop the listener. >> Print write times, throughput, notification count, and queue-overflow count. >> Close inotify and delete the test files and directory. >> >> Note : File creation, open, close, and cleanup are outside the timed >> write loops. >> >> Run 1: >> ------- >> >> | Clients | Before average time | After average time | Time change | >> |---:|---:|---:|---:| >> | 1 | 1.392 s | 1.484 s | +6.6% | >> | 2 | 2.205 s | 2.402 s | +8.9% | >> | 4 | 3.969 s | 3.644 s | −8.2% | >> | 8 | 5.851 s | 5.804 s | −0.8% | >> | 16 | 10.607 s | 11.538 s | +8.8% | >> | 32 | 21.093 s | 22.492 s | +6.6% | >> | 64 | 48.698 s | 49.576 s | +1.8% | >> | 128 | 100.208 s | 100.467 s | +0.3% | >> | 256 | 198.773 s | 197.669 s | −0.6% | >> | 512 | 400.056 s | 399.550 s | −0.1% | >> >> >> Run2 : >> -------- >> >> | Clients | Before average time | After average time | Time change | >> |---:|---:|---:|---:| >> | 1 | 1.491 s | 1.484 s | −0.4% | >> | 2 | 2.391 s | 2.402 s | +0.5% | >> | 4 | 4.050 s | 3.644 s | −10.0% | >> | 8 | 6.630 s | 5.804 s | −12.5% | >> | 16 | 11.931 s | 11.538 s | −3.3% | >> | 32 | 22.513 s | 22.492 s | −0.1% | >> | 64 | 50.231 s | 49.576 s | −1.3% | >> | 128 | 100.699 s | 100.467 s | −0.2% | >> | 256 | 198.771 s | 197.669 s | −0.6% | >> | 512 | 401.860 s | 399.550 s | −0.6% | >> | 1,024 | 803.812 s | 806.593 s | +0.3% | >> >> >> Positive means After Fix took longer; negative means faster. >> >> >> The measured write performance is broadly comparable before and after >> the fix, with no consistent substantial regression or progressively >> worsening slowdown as concurrency increases. >> >> The differences vary between baseline batches: the increases at 16 >> and 32 clients in the first comparison disappear or reverse in the >> second. At 128–1024 clients, average write times differ by approximately >> 0.6% or less. These shifts are consistent with run-to-run variation; >> the results do not show a persistent substantial slowdown from the fix. > > Thanks for running these tests. Results are very interesting. Can you > please share your benchmarking program with me? I'll check it and try to > play with it on my end. Thanks! > > Honza > -- > Jan Kara <jack@suse.com> > SUSE Labs, CR > $ cat run_parent_ref_scale.sh #!/usr/bin/env bash set -u -o pipefail bench=./fsnotify_parent_ref_bench if [[ ! -x "$bench" ]]; then printf 'Build %s in the current directory first.\n' "$bench" >&2 exit 1 fi results_dir=$(mktemp -d ./parent-ref-results-XXXXXXXX) || exit 1 printf 'kernel=%s\nbenchmark=%s\ntmpfs=/dev/shm\nwrites_per_client=1000000\nmode=watch\n' \ "$(uname -r)" "$bench" > "$results_dir/run-info.txt" printf 'Results: %s\n' "$results_dir" for i in 1 2 3 4 5; do for clients in 1 2 4 8 16 32 64 128 256 512 1024; do log="$results_dir/clients-${clients}.log" printf '\n=== clients=%d ===\n' "$clients" "$bench" /dev/shm "$clients" 1000000 watch 2>&1 | tee "$log" status=${PIPESTATUS[0]} printf 'exit_status=%d\n' "$status" | tee -a "$log" done done printf '\nAll results saved in %s\n' "$results_dir" $ cat fsnotify_parent_ref_bench.c #define _GNU_SOURCE #include <errno.h> #include <fcntl.h> #include <inttypes.h> #include <limits.h> #include <poll.h> #include <pthread.h> #include <stdatomic.h> #include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/inotify.h> #include <sys/stat.h> #include <sys/statfs.h> #include <time.h> #include <unistd.h> #include <linux/magic.h> #define FILES 1024 #define FILE_SIZE 4096 #define DEFAULT_WRITES 1000000UL struct gate { pthread_mutex_t mutex; pthread_cond_t cond; unsigned int ready; bool start; bool abort; }; struct worker { char path[PATH_MAX]; unsigned long writes; unsigned int id; struct gate *gate; uint64_t begin_ns; uint64_t end_ns; int error; }; struct listener { int fd; atomic_bool stop; uint64_t events; uint64_t overflows; int error; }; static uint64_t now_ns(void) { struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts)) { perror("clock_gettime"); exit(EXIT_FAILURE); } return (uint64_t)ts.tv_sec * 1000000000ULL + ts.tv_nsec; } static unsigned long parse_count(const char *s, const char *name) { char *end; unsigned long value; errno = 0; value = strtoul(s, &end, 10); if (errno || !*s || *end || !value) { fprintf(stderr, "Invalid %s: %s\n", name, s); exit(EXIT_FAILURE); } return value; } static void file_path(char *out, size_t size, const char *dir, unsigned int i) { int n = snprintf(out, size, "%s/file-%04u", dir, i); if (n < 0 || (size_t)n >= size) { fputs("File path is too long\n", stderr); exit(EXIT_FAILURE); } } static void *run_worker(void *arg) { struct worker *w = arg; int fd; char byte = (char)w->id; unsigned long i; bool abort; fd = open(w->path, O_WRONLY | O_CLOEXEC); if (fd < 0) w->error = errno; pthread_mutex_lock(&w->gate->mutex); w->gate->ready++; pthread_cond_broadcast(&w->gate->cond); while (!w->gate->start) pthread_cond_wait(&w->gate->cond, &w->gate->mutex); abort = w->gate->abort; pthread_mutex_unlock(&w->gate->mutex); if (abort || w->error) goto out; w->begin_ns = now_ns(); for (i = 0; i < w->writes; i++) { if (pwrite(fd, &byte, 1, 0) != 1) { w->error = errno ? errno : EIO; break; } } w->end_ns = now_ns(); out: if (fd >= 0 && close(fd) && !w->error) w->error = errno; return NULL; } static void *drain_events(void *arg) { struct listener *l = arg; char buf[65536] __attribute__((aligned(8))); struct pollfd pfd = { .fd = l->fd, .events = POLLIN }; for (;;) { int ready = poll(&pfd, 1, atomic_load(&l->stop) ? 0 : 50); if (ready < 0) { if (errno == EINTR) continue; l->error = errno; break; } if (!ready) { if (atomic_load(&l->stop)) break; continue; } for (;;) { ssize_t len = read(l->fd, buf, sizeof(buf)); size_t pos = 0; if (len < 0) { if (errno == EINTR) continue; if (errno == EAGAIN) break; l->error = errno; return NULL; } while (pos + sizeof(struct inotify_event) <= (size_t)len) { struct inotify_event *ev = (struct inotify_event *)(buf + pos); size_t step = sizeof(*ev) + ev->len; if (step > (size_t)len - pos) { l->error = EPROTO; return NULL; } l->events++; if (ev->mask & IN_Q_OVERFLOW) l->overflows++; pos += step; } if (pos != (size_t)len) { l->error = EPROTO; return NULL; } } } return NULL; } static void cleanup(const char *dir) { char path[PATH_MAX]; unsigned int i; for (i = 0; i < FILES; i++) { file_path(path, sizeof(path), dir, i); if (unlink(path) && errno != ENOENT) perror(path); } if (rmdir(dir)) perror(dir); } int main(int argc, char **argv) { char dir[PATH_MAX], path[PATH_MAX]; unsigned long writes = DEFAULT_WRITES; unsigned long count; unsigned int clients, i, created = 0; struct statfs fs; struct gate gate = { .mutex = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, }; struct listener listener = { .fd = -1 }; struct worker *workers; pthread_t *threads, listener_thread; bool watched = true, listener_started = false; uint64_t first = UINT64_MAX, last = 0, sum = 0; uint64_t shortest = UINT64_MAX, longest = 0; char zeroes[FILE_SIZE] = { 0 }; int rc = EXIT_FAILURE, err; if (argc < 3 || argc > 5) { fprintf(stderr, "Usage: %s TMPFS_ROOT CLIENTS [WRITES_PER_CLIENT] [watch|none]\n", argv[0]); return rc; } clients = parse_count(argv[2], "clients"); if (clients > FILES) { fprintf(stderr, "Clients must be at most %d\n", FILES); return rc; } if (argc > 3) writes = parse_count(argv[3], "writes per client"); if (writes > ULONG_MAX / clients) { fputs("Total write count is too large\n", stderr); return rc; } if (argc > 4) { if (!strcmp(argv[4], "none")) watched = false; else if (strcmp(argv[4], "watch")) { fputs("Mode must be watch or none\n", stderr); return rc; } } if (statfs(argv[1], &fs)) { perror(argv[1]); return rc; } if (fs.f_type != TMPFS_MAGIC) { fprintf(stderr, "%s must be a tmpfs directory\n", argv[1]); return rc; } if (snprintf(dir, sizeof(dir), "%s/fsnotify-parent-ref-XXXXXX", argv[1]) >= (int)sizeof(dir) || !mkdtemp(dir)) { perror("mkdtemp"); return rc; } workers = calloc(clients, sizeof(*workers)); threads = calloc(clients, sizeof(*threads)); if (!workers || !threads) { fputs("Cannot allocate worker state\n", stderr); goto out; } for (i = 0; i < FILES; i++) { int fd; file_path(path, sizeof(path), dir, i); fd = open(path, O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0600); if (fd < 0) { perror(path); goto out; } /* Allocate the tmpfs page before the measured write loop. */ if (write(fd, zeroes, sizeof(zeroes)) != sizeof(zeroes)) { perror(path); close(fd); goto out; } if (close(fd)) { perror(path); goto out; } } if (watched) { listener.fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); if (listener.fd < 0 || inotify_add_watch(listener.fd, dir, IN_MODIFY) < 0) { perror("inotify watch"); goto out; } err = pthread_create(&listener_thread, NULL, drain_events, &listener); if (err) { errno = err; perror("listener thread"); goto out; } listener_started = true; } for (i = 0; i < clients; i++) { workers[i].id = i; workers[i].writes = writes; workers[i].gate = &gate; file_path(workers[i].path, sizeof(workers[i].path), dir, i); err = pthread_create(&threads[i], NULL, run_worker, &workers[i]); if (err) { errno = err; perror("worker thread"); gate.abort = true; goto release; } created++; } pthread_mutex_lock(&gate.mutex); while (gate.ready < clients) pthread_cond_wait(&gate.cond, &gate.mutex); pthread_mutex_unlock(&gate.mutex); for (i = 0; i < clients; i++) { if (workers[i].error) { fprintf(stderr, "Worker %u open: %s\n", i, strerror(workers[i].error)); gate.abort = true; break; } } printf("test_dir=%s files=%d file_size=%d clients=%u writes_per_client=%lu mode=%s\n", dir, FILES, FILE_SIZE, clients, writes, watched ? "watch" : "none"); fflush(stdout); release: pthread_mutex_lock(&gate.mutex); gate.start = true; pthread_cond_broadcast(&gate.cond); pthread_mutex_unlock(&gate.mutex); for (i = 0; i < created; i++) pthread_join(threads[i], NULL); if (listener_started) { atomic_store(&listener.stop, true); pthread_join(listener_thread, NULL); printf("inotify_events=%" PRIu64 " queue_overflows=%" PRIu64 "\n", listener.events, listener.overflows); if (listener.error) fprintf(stderr, "Inotify listener: %s\n", strerror(listener.error)); if (listener.overflows) fputs("Inotify queue overflowed; event delivery load changed during the run\n", stderr); } if (gate.abort || listener.error) goto out; for (i = 0; i < clients; i++) { uint64_t duration; if (workers[i].error) { fprintf(stderr, "Worker %u write: %s\n", i, strerror(workers[i].error)); goto out; } duration = workers[i].end_ns - workers[i].begin_ns; if (workers[i].begin_ns < first) first = workers[i].begin_ns; if (workers[i].end_ns > last) last = workers[i].end_ns; if (duration < shortest) shortest = duration; if (duration > longest) longest = duration; sum += duration; } count = (unsigned long)clients * writes; printf("total_writes=%lu wall_seconds=%.6f throughput_writes_per_sec=%.0f mean_worker_ns_per_write=%.1f shortest_worker_seconds=%.6f longest_worker_seconds=%.6f\n", count, (last - first) / 1e9, count * 1e9 / (last - first), (double)sum / count, shortest / 1e9, longest / 1e9); printf("result=%s\n", listener.overflows ? "QUEUE_OVERFLOW" : "PASS"); rc = listener.overflows ? EXIT_FAILURE : EXIT_SUCCESS; out: if (listener.fd >= 0) close(listener.fd); cleanup(dir); free(workers); free(threads); return rc; } Please compile : fsnotify_parent_ref_bench.c -o fsnotify_parent_ref_bench. After that run the script ./run_parent_ref_scale.sh Thanks Partha ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-10-13 6:39 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-10-07 15:20 [PATCH 0/1] fsnotify: Check parent watches without child dentry flags Partha Sarathi Satapathy 2026-10-07 15:20 ` [PATCH 1/1] " Partha Sarathi Satapathy 2026-10-08 9:42 ` [PATCH 0/1] " Jan Kara 2026-10-09 11:48 ` [External] : " Partha Satapathy 2026-10-12 8:40 ` Jan Kara 2026-10-13 6:38 ` Partha Satapathy
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.