Re: [RFC][PATCH] make sure that lock_for_kill() callers drop the locks in safe order

Calvin Owens
2026-04-10T22:15:20Z

On Friday 04/10 at 22:24 +0100, Al Viro wrote:
> On Fri, Apr 10, 2026 at 02:13:10PM -0700, Calvin Owens wrote:
> > On Friday 04/10 at 15:32 -0400, Jeff Layton wrote:
> > > Yep, not even with that. One thing that claude pointed out: doing an
> > > mdelay() might prevent it from making any progress freeing dentries.
> > > I'm experimenting now with one that only randomly does a delay, but so
> > > far that hasn't turned up anything. I'll let you know if that changes.
> >
> > Hi Jeff,
> >
> > I've been poking at this one too. With the attached reproducer, the
> > waiter will get stuck in an infinite loop apparently in d_walk() with a
> > stock 7.0-rc7 (in my case with PREEMPT_LAZY):
>
> Interesting... I'll try it in a bit, but just in case - could you check if
> git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs.git #work.dcache-busy-wait
> steps into the same thing on your setup?

Hi Al,

It still reproduces on that branch in the exact same way.

I'm absolutely 100% guessing... but I recall tomfoolery with the "magic"
symlinks in /proc/$/fd/* in the past... the below patch makes the infinite
spins impossible to trigger with my reproducer, does my explanation make
any sense to you? Apologies if I'm off in the weeds...

Thanks,
Calvin

---
From: Calvin Owens <calvin@wbinvd.org>
Subject: [PATCH] fs/proc: Implement d_weak_revalidate()

I think this is necessary because ND_JUMPED can happen in /proc when
the magic symlinks in /proc/$/fd/* point to files in /proc. If true,
this is a problem, because the default behavior for d_weak_revalidate()
is to assume the jumped-to dentries are always valid.

Fix by checking the in_use count in d_weak_revalidate() for procfs, the
same way it is checked in procfs d_revalidate() today.

Signed-off-by: Calvin Owens <calvin@wbinvd.org>
---
fs/proc/generic.c | 22 +++++++++++++++++-----
1 file changed, 17 insertions(+), 5 deletions(-)

diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 501889856461..359176e5eeb3 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -216,15 +216,26 @@ void proc_free_inum(unsigned int inum)
ida_free(&proc_inum_ida, inum - PROC_DYNAMIC_FIRST);
}

+static int __proc_misc_revalidate(struct dentry *dentry)
+{
+ if (atomic_read(&PDE(d_inode(dentry))->in_use) < 0)
+ return 0; /* revalidate */
+ return 1;
+}
+
static int proc_misc_d_revalidate(struct inode *dir, const struct qstr *name,
struct dentry *dentry, unsigned int flags)
{
if (flags & LOOKUP_RCU)
return -ECHILD;

- if (atomic_read(&PDE(d_inode(dentry))->in_use) < 0)
- return 0; /* revalidate */
- return 1;
+ return __proc_misc_revalidate(dentry);
+}
+
+static int proc_misc_d_weak_revalidate(struct dentry * dentry,
+ unsigned int flags)
+{
+ return __proc_misc_revalidate(dentry);
}

static int proc_misc_d_delete(const struct dentry *dentry)
@@ -233,8 +244,9 @@ static int proc_misc_d_delete(const struct dentry *dentry)
}

static const struct dentry_operations proc_misc_dentry_ops = {
- .d_revalidate = proc_misc_d_revalidate,
- .d_delete = proc_misc_d_delete,
+ .d_revalidate = proc_misc_d_revalidate,
+ .d_weak_revalidate = proc_misc_d_weak_revalidate,
+ .d_delete = proc_misc_d_delete,
};

/*
--
2.47.3