[PATCH v1 05/12] libfs: simplify scan_positives()

From: NeilBrown

Date: Sun Aug 02 2026 - 21:38:43 EST


From: NeilBrown <neil@xxxxxxxxxx>

This patch removes explicit use of hlist_node from scan_positives() and
callers, and consistently uses struct dentry instead.

Previously scan_positives() had two args which were closely related: p
and last.
If last was not NULL, p was precisely &last->d_sib.next.
If last WAS NULL, then p was
&cursor->d_parent->d_children.first or
&cursor->d_sib.next

If, for the final case, we pass 'cursor' as 'last', then we only need
'last' not 'p'. For this to work we must dget(cursor) before passing
it, as scan_positives() calls dput() on last.

So now to scan from the start, pass NULL, to scan from a particular
dentry, pass that dentry.

Signed-off-by: NeilBrown <neil@xxxxxxxxxx>
---
fs/libfs.c | 29 +++++++++++++----------------
1 file changed, 13 insertions(+), 16 deletions(-)

diff --git a/fs/libfs.c b/fs/libfs.c
index fc283e0a7c3a..080ca615593c 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -105,16 +105,18 @@ EXPORT_SYMBOL(dcache_dir_close);
* If no such element exists, NULL is returned.
*/
static struct dentry *scan_positives(struct dentry *cursor,
- struct hlist_node **p,
- loff_t count,
- struct dentry *last)
+ struct dentry *last,
+ loff_t count)
{
struct dentry *dentry = cursor->d_parent, *found = NULL;
+ struct dentry *next;

spin_lock(&dentry->d_lock);
- while (*p) {
- struct dentry *d = hlist_entry(*p, struct dentry, d_sib);
- p = &d->d_sib.next;
+ next = last ? d_next_sibling(last) : d_first_child(dentry);
+ while (next) {
+ struct dentry *d = next;
+
+ next = d_next_sibling(next);
// we must at least skip cursors, to avoid livelocks
if (d->d_flags & DCACHE_DENTRY_CURSOR)
continue;
@@ -131,10 +133,10 @@ static struct dentry *scan_positives(struct dentry *cursor,
if (!hlist_unhashed(&cursor->d_sib))
__hlist_del(&cursor->d_sib);
hlist_add_behind(&cursor->d_sib, &d->d_sib);
- p = &cursor->d_sib.next;
spin_unlock(&dentry->d_lock);
cond_resched();
spin_lock(&dentry->d_lock);
+ next = d_next_sibling(cursor);
}
}
spin_unlock(&dentry->d_lock);
@@ -163,8 +165,7 @@ loff_t dcache_dir_lseek(struct file *file, loff_t offset, int whence)
inode_lock_shared(dentry->d_inode);

if (offset > 2)
- to = scan_positives(cursor, &dentry->d_children.first,
- offset - 2, NULL);
+ to = scan_positives(cursor, NULL, offset - 2);
spin_lock(&dentry->d_lock);
hlist_del_init(&cursor->d_sib);
if (to)
@@ -191,23 +192,19 @@ int dcache_readdir(struct file *file, struct dir_context *ctx)
struct dentry *dentry = file->f_path.dentry;
struct dentry *cursor = file->private_data;
struct dentry *next = NULL;
- struct hlist_node **p;

if (!dir_emit_dots(file, ctx))
return 0;

- if (ctx->pos == 2)
- p = &dentry->d_children.first;
- else
- p = &cursor->d_sib.next;
+ if (ctx->pos > 2)
+ next = dget(cursor);

- while ((next = scan_positives(cursor, p, 1, next)) != NULL) {
+ while ((next = scan_positives(cursor, next, 1)) != NULL) {
if (!dir_emit(ctx, next->d_name.name, next->d_name.len,
d_inode(next)->i_ino,
fs_umode_to_dtype(d_inode(next)->i_mode)))
break;
ctx->pos++;
- p = &next->d_sib.next;
}
spin_lock(&dentry->d_lock);
hlist_del_init(&cursor->d_sib);
--
2.50.0.107.gf914562f5916.dirty