* [PATCH 1/4] kernfs: Use VFS lookup context in d_revalidate()
2026-08-21 5:05 [PATCH 0/4] kernfs: remove kernfs_rwsem from dentry revalidation Shakeel Butt
@ 2026-08-21 5:05 ` Shakeel Butt
2026-08-21 5:05 ` [PATCH 2/4] kernfs: Prepare directory revisions for lockless reads Shakeel Butt
` (3 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Shakeel Butt @ 2026-08-21 5:05 UTC (permalink / raw)
To: Greg Kroah-Hartman, Tejun Heo
Cc: Christian Brauner, Meta kernel team, driver-core, cgroups,
linux-kernel
The VFS supplies a stable parent inode and expected name to the
revalidation callback. Use them instead of recovering the same
information from mutable dentry fields.
Compare the name using its explicit length because it may point into the
pathname and need not be terminated at name->len. This also prepares the
callback for lockless operation.
Signed-off-by: Shakeel Butt <shakeel.butt@linux.dev>
---
fs/kernfs/dir.c | 39 +++++++++++++++++----------------------
1 file changed, 17 insertions(+), 22 deletions(-)
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index 82bbaeb326aa..541bb5525437 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -1171,23 +1171,19 @@ struct kernfs_node *kernfs_create_empty_dir(struct kernfs_node *parent,
static int kernfs_dop_revalidate(struct inode *dir, const struct qstr *name,
struct dentry *dentry, unsigned int flags)
{
- struct kernfs_node *kn, *parent;
+ struct kernfs_node *kn, *kn_parent;
+ struct kernfs_node *parent = dir->i_private;
struct kernfs_root *root;
+ const char *kn_name;
if (flags & LOOKUP_RCU)
return -ECHILD;
/* Negative hashed dentry? */
if (d_really_is_negative(dentry)) {
- /* If the kernfs parent node has changed discard and
- * proceed to ->lookup.
- *
- * There's nothing special needed here when getting the
- * dentry parent, even if a concurrent rename is in
- * progress. That's because the dentry is negative so
- * it can only be the target of the rename and it will
- * be doing a d_move() not a replace. Consequently the
- * dentry d_parent won't change over the d_move().
+ /*
+ * If the kernfs parent node has changed discard and proceed to
+ * ->lookup.
*
* Also kernfs negative dentries transitioning from
* negative to positive during revalidate won't happen
@@ -1195,14 +1191,11 @@ static int kernfs_dop_revalidate(struct inode *dir, const struct qstr *name,
* changes and the lookup re-done so that a new positive
* dentry can be properly created.
*/
- root = kernfs_root_from_sb(dentry->d_sb);
+ root = kernfs_root(parent);
down_read(&root->kernfs_rwsem);
- parent = kernfs_dentry_node(dentry->d_parent);
- if (parent) {
- if (kernfs_dir_changed(parent, dentry)) {
- up_read(&root->kernfs_rwsem);
- return 0;
- }
+ if (kernfs_dir_changed(parent, dentry)) {
+ up_read(&root->kernfs_rwsem);
+ return 0;
}
up_read(&root->kernfs_rwsem);
@@ -1220,18 +1213,20 @@ static int kernfs_dop_revalidate(struct inode *dir, const struct qstr *name,
if (!kernfs_active(kn))
goto out_bad;
- parent = kernfs_parent(kn);
+ kn_parent = kernfs_parent(kn);
/* The kernfs node has been moved? */
- if (kernfs_dentry_node(dentry->d_parent) != parent)
+ if (parent != kn_parent)
goto out_bad;
/* The kernfs node has been renamed */
- if (strcmp(dentry->d_name.name, kernfs_rcu_name(kn)) != 0)
+ kn_name = kernfs_rcu_name(kn);
+ if (name->len != strlen(kn_name) ||
+ memcmp(name->name, kn_name, name->len))
goto out_bad;
/* The kernfs node has been moved to a different namespace */
- if (parent && kernfs_ns_enabled(parent) &&
- kernfs_ns_id(kernfs_info(dentry->d_sb)->ns) != kernfs_ns_id(kn->ns))
+ if (kn_parent && kernfs_ns_enabled(kn_parent) &&
+ kernfs_ns_id(kernfs_info(dir->i_sb)->ns) != kernfs_ns_id(kn->ns))
goto out_bad;
up_read(&root->kernfs_rwsem);
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH 2/4] kernfs: Prepare directory revisions for lockless reads
2026-08-21 5:05 [PATCH 0/4] kernfs: remove kernfs_rwsem from dentry revalidation Shakeel Butt
2026-08-21 5:05 ` [PATCH 1/4] kernfs: Use VFS lookup context in d_revalidate() Shakeel Butt
@ 2026-08-21 5:05 ` Shakeel Butt
2026-08-21 5:05 ` [PATCH 3/4] kernfs: Avoid namespace dereference in d_revalidate() Shakeel Butt
` (2 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Shakeel Butt @ 2026-08-21 5:05 UTC (permalink / raw)
To: Greg Kroah-Hartman, Tejun Heo
Cc: Christian Brauner, Meta kernel team, driver-core, cgroups,
linux-kernel
Negative dentry revalidation only needs to sample the parent directory
generation and compare it with the value recorded at lookup time.
Annotate those accesses with READ_ONCE() and WRITE_ONCE() so the
comparison can safely move outside kernfs_rwsem.
Revision updates remain serialized by kernfs_rwsem. Assert that writer
contract in kernfs_inc_rev(); the read half of the increment stays plain
because the semaphore excludes other writers.
Signed-off-by: Shakeel Butt <shakeel.butt@linux.dev>
---
fs/kernfs/kernfs-internal.h | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h
index aa784b540b36..20a0cf42ba8d 100644
--- a/fs/kernfs/kernfs-internal.h
+++ b/fs/kernfs/kernfs-internal.h
@@ -147,20 +147,19 @@ static inline struct kernfs_node *kernfs_dentry_node(struct dentry *dentry)
static inline void kernfs_set_rev(struct kernfs_node *parent,
struct dentry *dentry)
{
- dentry->d_time = parent->dir.rev;
+ WRITE_ONCE(dentry->d_time, READ_ONCE(parent->dir.rev));
}
static inline void kernfs_inc_rev(struct kernfs_node *parent)
{
- parent->dir.rev++;
+ lockdep_assert_held_write(&parent->dir.root->kernfs_rwsem);
+ WRITE_ONCE(parent->dir.rev, parent->dir.rev + 1);
}
static inline bool kernfs_dir_changed(struct kernfs_node *parent,
struct dentry *dentry)
{
- if (parent->dir.rev != dentry->d_time)
- return true;
- return false;
+ return READ_ONCE(parent->dir.rev) != READ_ONCE(dentry->d_time);
}
extern const struct super_operations kernfs_sops;
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH 3/4] kernfs: Avoid namespace dereference in d_revalidate()
2026-08-21 5:05 [PATCH 0/4] kernfs: remove kernfs_rwsem from dentry revalidation Shakeel Butt
2026-08-21 5:05 ` [PATCH 1/4] kernfs: Use VFS lookup context in d_revalidate() Shakeel Butt
2026-08-21 5:05 ` [PATCH 2/4] kernfs: Prepare directory revisions for lockless reads Shakeel Butt
@ 2026-08-21 5:05 ` Shakeel Butt
2026-08-21 5:05 ` [PATCH 4/4] kernfs: Remove kernfs_rwsem from dentry revalidation Shakeel Butt
2026-08-25 14:58 ` [PATCH 0/4] kernfs: remove " Christian Brauner
4 siblings, 0 replies; 7+ messages in thread
From: Shakeel Butt @ 2026-08-21 5:05 UTC (permalink / raw)
To: Greg Kroah-Hartman, Tejun Heo
Cc: Christian Brauner, Meta kernel team, driver-core, cgroups,
linux-kernel
Commit 1fe989e1c42a ("kernfs: use namespace id instead of pointer for
hashing and comparison") changed dentry revalidation to compare namespace
IDs along with the comparisons that determine visible directory ordering.
Dereferencing a namespace tag that kernfs_rename_ns() can replace is not
suitable once dentry revalidation stops taking kernfs_rwsem. Use pointer
equality for this non-user-visible equality check instead. Namespace IDs
uniquely identify namespace objects, so pointer and ID equality cannot
disagree for valid tags. Hashing and directory ordering continue to use
IDs.
kn->ns becomes a lockless read in the next commit, so mark both sides of
it now. The read is in kernfs_dop_revalidate(); the stores that can run
while the node is visible are the two in kernfs_rename_ns(). The remaining
stores, in kernfs_create_dir_ns(), kernfs_create_empty_dir() and
kernfs_create_link(), all precede kernfs_add_one() and need no marking.
Signed-off-by: Shakeel Butt <shakeel.butt@linux.dev>
---
fs/kernfs/dir.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index 541bb5525437..27949b0e027c 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -1226,7 +1226,7 @@ static int kernfs_dop_revalidate(struct inode *dir, const struct qstr *name,
/* The kernfs node has been moved to a different namespace */
if (kn_parent && kernfs_ns_enabled(kn_parent) &&
- kernfs_ns_id(kernfs_info(dir->i_sb)->ns) != kernfs_ns_id(kn->ns))
+ kernfs_info(dir->i_sb)->ns != READ_ONCE(kn->ns))
goto out_bad;
up_read(&root->kernfs_rwsem);
@@ -1873,7 +1873,7 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
rcu_assign_pointer(kn->__parent, new_parent);
- kn->ns = new_ns;
+ WRITE_ONCE(kn->ns, new_ns);
if (new_name)
rcu_assign_pointer(kn->name, new_name);
@@ -1881,7 +1881,7 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
kernfs_put(old_parent);
} else {
/* name assignment is RCU protected, parent is the same */
- kn->ns = new_ns;
+ WRITE_ONCE(kn->ns, new_ns);
if (new_name)
rcu_assign_pointer(kn->name, new_name);
}
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH 4/4] kernfs: Remove kernfs_rwsem from dentry revalidation
2026-08-21 5:05 [PATCH 0/4] kernfs: remove kernfs_rwsem from dentry revalidation Shakeel Butt
` (2 preceding siblings ...)
2026-08-21 5:05 ` [PATCH 3/4] kernfs: Avoid namespace dereference in d_revalidate() Shakeel Butt
@ 2026-08-21 5:05 ` Shakeel Butt
2026-08-25 14:58 ` [PATCH 0/4] kernfs: remove " Christian Brauner
4 siblings, 0 replies; 7+ messages in thread
From: Shakeel Butt @ 2026-08-21 5:05 UTC (permalink / raw)
To: Greg Kroah-Hartman, Tejun Heo
Cc: Christian Brauner, Meta kernel team, driver-core, cgroups,
linux-kernel
kernfs_dop_revalidate() takes kernfs_rwsem for read once per path component
of every walk into a kernfs mount. Linux rwsems do not permit reader lock
stealing once a writer is queued, so a single writer parks the whole
incoming reader stream in uninterruptible sleep, stalling cgroup-polling
daemons for minutes.
Nothing the callback reads requires the semaphore. kn->active is an
atomic_t that kernfs_find_and_get_node_by_id() already tests through
__kernfs_active(); kn->__parent and kn->name are RCU pointers whose old
values are freed only after a grace period; kn->ns is now compared rather
than dereferenced; parent->dir.rev was annotated earlier in this series.
What the semaphore does provide is a coherent snapshot, and that is not
needed. ->d_revalidate() answers a question about a single instant, and the
answer is already stale when it returns: a rename landing just after
up_read() gives the same outcome as one observed mid-read. A lockless
reader can only return "valid" for the (parent, name, namespace) triple
identifying the dentry it was handed, and that triple was true when the
dentry was instantiated, so it reports a genuine past state exactly as the
locked version did. Removal is backstopped by kernfs_get_active() failing
in the subsequent open().
Take an RCU read lock instead. kernfs_parent() and kernfs_rcu_name() work
unchanged: the condition in their rcu_dereference_check() is an alternative
to holding the RCU read lock, not an extra requirement. The negative dentry
path needs nothing, as @dir pins the parent. The namespace check can use
@parent directly once the preceding check establishes it equals
kernfs_parent(kn), so the kn_parent local and its NULL test go away.
kernfs_ns_enabled() reads @parent->flags, which KERNFS_ACTIVATED and
KERNFS_REMOVING update as a plain read-modify-write under kernfs_rwsem.
Those bits are not read here and KERNFS_NS cannot change once the directory
has children, so mark the read data_race() rather than READ_ONCE(), which
would not silence KCSAN against the unmarked writers anyway.
kernfs_iop_permission() still forces every walk out of RCU-walk before
children are revalidated, so lifting the LOOKUP_RCU bail here would have no
observable effect; it is left to the series fixing that path.
Readers walking cgroupfs and sysfs against concurrent cgroup and netdev
churn: kernfs_rwsem read acquisitions drop from 48,593,360 to 1,280,280,
and kernfs_dop_revalidate() no longer appears among its contention sites.
Signed-off-by: Shakeel Butt <shakeel.butt@linux.dev>
---
fs/kernfs/dir.c | 49 ++++++++++++++++++++-----------------------------
1 file changed, 20 insertions(+), 29 deletions(-)
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index 27949b0e027c..cd7a8ff8b6b2 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -1171,9 +1171,8 @@ struct kernfs_node *kernfs_create_empty_dir(struct kernfs_node *parent,
static int kernfs_dop_revalidate(struct inode *dir, const struct qstr *name,
struct dentry *dentry, unsigned int flags)
{
- struct kernfs_node *kn, *kn_parent;
struct kernfs_node *parent = dir->i_private;
- struct kernfs_root *root;
+ struct kernfs_node *kn;
const char *kn_name;
if (flags & LOOKUP_RCU)
@@ -1191,49 +1190,41 @@ static int kernfs_dop_revalidate(struct inode *dir, const struct qstr *name,
* changes and the lookup re-done so that a new positive
* dentry can be properly created.
*/
- root = kernfs_root(parent);
- down_read(&root->kernfs_rwsem);
- if (kernfs_dir_changed(parent, dentry)) {
- up_read(&root->kernfs_rwsem);
- return 0;
- }
- up_read(&root->kernfs_rwsem);
-
- /* The kernfs parent node hasn't changed, leave the
- * dentry negative and return success.
- */
- return 1;
+ return !kernfs_dir_changed(parent, dentry);
}
kn = kernfs_dentry_node(dentry);
- root = kernfs_root(kn);
- down_read(&root->kernfs_rwsem);
+
+ guard(rcu)();
/* The kernfs node has been deactivated */
- if (!kernfs_active(kn))
- goto out_bad;
+ if (!__kernfs_active(kn))
+ return 0;
- kn_parent = kernfs_parent(kn);
/* The kernfs node has been moved? */
- if (parent != kn_parent)
- goto out_bad;
+ if (kernfs_parent(kn) != parent)
+ return 0;
/* The kernfs node has been renamed */
kn_name = kernfs_rcu_name(kn);
if (name->len != strlen(kn_name) ||
memcmp(name->name, kn_name, name->len))
- goto out_bad;
+ return 0;
- /* The kernfs node has been moved to a different namespace */
- if (kn_parent && kernfs_ns_enabled(kn_parent) &&
+ /*
+ * The kernfs node has been moved to a different namespace.
+ *
+ * KERNFS_NS is set by kernfs_enable_ns() while @parent still has no
+ * children, so it cannot change while a child of @parent is being
+ * revalidated. The other bits in that word, KERNFS_ACTIVATED and
+ * KERNFS_REMOVING, are updated under kernfs_rwsem and are not read
+ * here, so racing with them is intentional and harmless.
+ */
+ if (data_race(kernfs_ns_enabled(parent)) &&
kernfs_info(dir->i_sb)->ns != READ_ONCE(kn->ns))
- goto out_bad;
+ return 0;
- up_read(&root->kernfs_rwsem);
return 1;
-out_bad:
- up_read(&root->kernfs_rwsem);
- return 0;
}
const struct dentry_operations kernfs_dops = {
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [PATCH 0/4] kernfs: remove kernfs_rwsem from dentry revalidation
2026-08-21 5:05 [PATCH 0/4] kernfs: remove kernfs_rwsem from dentry revalidation Shakeel Butt
` (3 preceding siblings ...)
2026-08-21 5:05 ` [PATCH 4/4] kernfs: Remove kernfs_rwsem from dentry revalidation Shakeel Butt
@ 2026-08-25 14:58 ` Christian Brauner
2026-08-26 4:02 ` Ian Kent
4 siblings, 1 reply; 7+ messages in thread
From: Christian Brauner @ 2026-08-25 14:58 UTC (permalink / raw)
To: Greg Kroah-Hartman, Tejun Heo, Shakeel Butt
Cc: Meta kernel team, driver-core, cgroups, linux-kernel,
Christian Brauner
On Thu, 20 Aug 2026 22:05:03 -0700, Shakeel Butt wrote:
> kernfs: remove kernfs_rwsem from dentry revalidation
>
> At Meta, we are seeing important system daemons that poll cgroupfs and
> sysfs geth stuck in kernfs_dop_revalidate() for minutes. The two that
> hurt most are the ones we can least afford to lose: oomd, which decides
> what to kill when a machine runs out of memory, and below[1], which
> records the telemetry used to understand what happened afterwards.
>
> [...]
Applied to the vfs-7.4.kernfs branch of the vfs/vfs.git tree.
Patches in the vfs-7.4.kernfs branch should appear in linux-next soon.
Please report any outstanding bugs that were missed during review in a
new review to the original patch series allowing us to drop it.
It's encouraged to provide Acked-bys and Reviewed-bys even though the
patch has now been applied. If possible patch trailers will be updated.
Note that commit hashes shown below are subject to change due to rebase,
trailer updates or similar. If in doubt, please check the listed branch.
tree: https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git
branch: vfs-7.4.kernfs
[1/4] kernfs: Use VFS lookup context in d_revalidate()
https://git.kernel.org/vfs/vfs/c/b3c97f5cb084
[2/4] kernfs: Prepare directory revisions for lockless reads
https://git.kernel.org/vfs/vfs/c/272e0997df44
[3/4] kernfs: Avoid namespace dereference in d_revalidate()
https://git.kernel.org/vfs/vfs/c/e1c629a8dbc7
[4/4] kernfs: Remove kernfs_rwsem from dentry revalidation
https://git.kernel.org/vfs/vfs/c/8c6ad578e2f0
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [PATCH 0/4] kernfs: remove kernfs_rwsem from dentry revalidation
2026-08-25 14:58 ` [PATCH 0/4] kernfs: remove " Christian Brauner
@ 2026-08-26 4:02 ` Ian Kent
0 siblings, 0 replies; 7+ messages in thread
From: Ian Kent @ 2026-08-26 4:02 UTC (permalink / raw)
To: Christian Brauner, Greg Kroah-Hartman, Tejun Heo, Shakeel Butt
Cc: Meta kernel team, driver-core, cgroups, linux-kernel
On 25/8/26 22:58, Christian Brauner wrote:
> On Thu, 20 Aug 2026 22:05:03 -0700, Shakeel Butt wrote:
>> kernfs: remove kernfs_rwsem from dentry revalidation
>>
>> At Meta, we are seeing important system daemons that poll cgroupfs and
>> sysfs geth stuck in kernfs_dop_revalidate() for minutes. The two that
>> hurt most are the ones we can least afford to lose: oomd, which decides
>> what to kill when a machine runs out of memory, and below[1], which
>> records the telemetry used to understand what happened afterwards.
>>
>> [...]
> Applied to the vfs-7.4.kernfs branch of the vfs/vfs.git tree.
> Patches in the vfs-7.4.kernfs branch should appear in linux-next soon.
>
> Please report any outstanding bugs that were missed during review in a
> new review to the original patch series allowing us to drop it.
>
> It's encouraged to provide Acked-bys and Reviewed-bys even though the
> patch has now been applied. If possible patch trailers will be updated.
I have a report against kernfs where I cannot find a reason for significant
contention, primarily on ->revaliate(), in either of two vmcores, exactly
the case described in this series.
I reviewed the series and it looks good to me.
The descriptions justifying the changes also look sound.
While I think renames and removals should be ok based on the descriptions
justifying the change (my biggest concern with removing the rwsem) I'll
continue to ponder its implications for a while.
Reviewed-by: Ian Kent <raven@themaw.net>
>
> Note that commit hashes shown below are subject to change due to rebase,
> trailer updates or similar. If in doubt, please check the listed branch.
>
> tree: https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git
> branch: vfs-7.4.kernfs
>
> [1/4] kernfs: Use VFS lookup context in d_revalidate()
> https://git.kernel.org/vfs/vfs/c/b3c97f5cb084
> [2/4] kernfs: Prepare directory revisions for lockless reads
> https://git.kernel.org/vfs/vfs/c/272e0997df44
> [3/4] kernfs: Avoid namespace dereference in d_revalidate()
> https://git.kernel.org/vfs/vfs/c/e1c629a8dbc7
> [4/4] kernfs: Remove kernfs_rwsem from dentry revalidation
> https://git.kernel.org/vfs/vfs/c/8c6ad578e2f0
>
>
^ permalink raw reply [flat|nested] 7+ messages in thread