Re: [PATCH v1 01/12] VFS: don't count references through ->d_parent

From: NeilBrown

Date: Mon Aug 10 2026 - 22:12:34 EST


On Mon, 10 Aug 2026, Miklos Szeredi wrote:
> On Mon, 3 Aug 2026 at 03:37, NeilBrown <neilb@xxxxxxxxxxx> wrote:
>
> > When a DCACHE_CURSOR dentry is added to d_children, it is only ever
> > added "before" or "behind" an existing child, so it will never be the
> > first and so never needs to adjust the refcount on the parent. If it
> > remains on the list until dput() it could be the last child to be
> > removed, in which case normal handling applies in dentry_unlist() (where
> > it has been moved from dentry_kill().
>
> Not handling the parent refcount together with cursor removal can
> result in inconsistency, e.g:
>
> - add cursor (seek between two positive dentries)
> - remove all real children (parent refcount not touched, since cursor
> is still on d_children)
> - remove cursor (seek to zero offset)
> - close directory (dentry_unlist() will skip parent refcount update
> since dentry->d_sib is no longer linked)
>
> Repro attached, produces "BUG: Dentry ffff888107ef75d0{i=143e,n=dir}
> still in use (1) [unm
> ount of hugetlbfs hugetlbfs]".
>
> Thanks,
> Milklos
>

Thanks for the review. I realised that when I was doing patch 11 which
changes d_for_each_positive_child() to use a cursor, but I didn't think
the apply that understand back at this patch.

There is only on user of cursors so it can be easily fixed as follows.

Thanks,
NeilBrown

index 5a0d276379d1..91f535192ee2 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -169,6 +169,8 @@ loff_t dcache_dir_lseek(struct file *file, loff_t offset, int whence)
hlist_del_init(&cursor->d_sib);
if (to)
hlist_add_behind(&cursor->d_sib, &to->d_sib);
+ else if (hlist_empty(&dentry->d_children)
+ dput_dlock(dentry);
spin_unlock(&dentry->d_lock);
dput(to);

@@ -213,6 +215,8 @@ int dcache_readdir(struct file *file, struct dir_context *ctx)
hlist_del_init(&cursor->d_sib);
if (next)
hlist_add_before(&cursor->d_sib, &next->d_sib);
+ else if (hlist_empty(&dentry->d_children))
+ dput_dlock(dentry);
spin_unlock(&dentry->d_lock);
dput(next);