[PATCH 5/5] seqlock: change __dentry_path() to use __SEQLOCK_READ_SECTION()

Oleg Nesterov posted 5 patches 2 months, 1 week ago
[PATCH 5/5] seqlock: change __dentry_path() to use __SEQLOCK_READ_SECTION()
Posted by Oleg Nesterov 2 months, 1 week ago
To simplify the code and make it more readable.

Signed-off-by: Oleg Nesterov <oleg@redhat.com>
---
 fs/d_path.c | 31 +++++++++++++------------------
 1 file changed, 13 insertions(+), 18 deletions(-)

diff --git a/fs/d_path.c b/fs/d_path.c
index bb365511066b..0dab1ab1e78d 100644
--- a/fs/d_path.c
+++ b/fs/d_path.c
@@ -332,28 +332,23 @@ static char *__dentry_path(const struct dentry *d, struct prepend_buffer *p)
 {
 	const struct dentry *dentry;
 	struct prepend_buffer b;
-	int seq = 0;
 
 	rcu_read_lock();
-restart:
-	dentry = d;
-	b = *p;
-	read_seqbegin_or_lock(&rename_lock, &seq);
-	while (!IS_ROOT(dentry)) {
-		const struct dentry *parent = dentry->d_parent;
+	__SEQLOCK_READ_SECTION(&rename_lock, lockless, seq, NULL) {
+		dentry = d;
+		b = *p;
+		while (!IS_ROOT(dentry)) {
+			const struct dentry *parent = dentry->d_parent;
 
-		prefetch(parent);
-		if (!prepend_name(&b, &dentry->d_name))
-			break;
-		dentry = parent;
-	}
-	if (!(seq & 1))
-		rcu_read_unlock();
-	if (need_seqretry(&rename_lock, seq)) {
-		seq = 1;
-		goto restart;
+			prefetch(parent);
+			if (!prepend_name(&b, &dentry->d_name))
+				break;
+			dentry = parent;
+		}
+		if (lockless)
+			rcu_read_unlock();
 	}
-	done_seqretry(&rename_lock, seq);
+
 	if (b.len == p->len)
 		prepend_char(&b, '/');
 	return extract_string(&b);
-- 
2.25.1.362.g51ebf55
Re: [PATCH 5/5] seqlock: change __dentry_path() to use __SEQLOCK_READ_SECTION()
Posted by Linus Torvalds 2 months, 1 week ago
On Sun, 5 Oct 2025 at 07:52, Oleg Nesterov <oleg@redhat.com> wrote:
>
> To simplify the code and make it more readable.

Ok, so the other ones looked fine. This one I'm not convinced about.

The end result makes no sense taken alone. It does that odd
"rcu_read_unlock()" after the first loop, and it basically ends up
being very incestuous with that implementation, which means that now
you have to understand both pieces intimately to figure it out, and
they are not near each other.

It *might* be solved by just renaming 'lockless' to
'first_lockless_iteration' - not because it changes the code, but
because it makes the logic much more explicit, and then that

        if (first_lockless_iteration)
                rcu_read_unlock();

test inside that loop would at least make a lot more conceptual sense
without knowing the internal implementation of that macro.

But honestly, while I think that would turn it from "too ugly to live"
to "I don't love it but I can deal with it", I would wish for
something better.

Side note: that whole internal loop:

        while (!IS_ROOT(dentry)) {
                const struct dentry *parent = dentry->d_parent;

                prefetch(parent);
                if (!prepend_name(&b, &dentry->d_name))
                        break;
                dentry = parent;
        }

should be a helper function of its own, I think. And if you do that,
maybe you can switch the whole thing over to just making the first
non-locked iteration be an explicitly separate phase?

I dunno. I don't love that code in the existing format - but I think
you ended up hiding that subtlety even more.

             Linus