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

Miklos Szeredi <[email protected]>
Newsgroups gmane.linux.nfs,gmane.linux.kernel.autofs,gmane.linux.kernel,gmane.comp.file-systems.ceph.devel,gmane.comp.file-systems.coda.general,gmane.linux.file-systems
Message-ID <CAJfpegt1wHngLs9uR943gT6zxV67D53k3n4GiFKowsAb3C6LJw@mail.gmail.com>
On Mon, 3 Aug 2026 at 03:37, NeilBrown <[email protected]> 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
rmwhilereaddir.c (text/x-csrc, 905 B)
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <err.h>
#include <sys/stat.h>

#define CHECK_ERR(_expr) \
	({ typeof(_expr) _ret = (_expr); if (_ret == -1) { err(1, #_expr); } _ret; })
#define CHECK_NULL(_expr) \
	({ typeof(_expr) _ret = (_expr); if (_ret == NULL) { errx(1, #_expr " returned NULL"); } _ret; })

int main(void)
{
	int dfd;
	DIR *dp;
	struct dirent *de;

	CHECK_ERR(mkdir("dir", 0777));
	CHECK_ERR(mknod("dir/foo", 0666, 0));
	CHECK_ERR(mknod("dir/bar", 0666, 0));
	dfd = CHECK_ERR(open("dir", O_RDONLY));
	dp = CHECK_NULL(fdopendir(dfd));
	do {
		de = CHECK_NULL(readdir(dp));
	} while (de->d_name[0] == '.');
	CHECK_ERR(lseek(dfd, de->d_off, SEEK_SET)); /* links cursor between foo and bar */
	CHECK_ERR(unlink("dir/foo"));
	CHECK_ERR(unlink("dir/bar"));
	CHECK_ERR(lseek(dfd, 0, SEEK_SET)); /* removes cursor */
	CHECK_ERR(closedir(dp));
	CHECK_ERR(rmdir("dir"));
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.