From 8aa57bef78b9aa5e5d58b81b426b910add2798e0 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Fri, 7 Aug 2026 01:03:19 -0700 Subject: [PATCH] mount: stop churning the inode table on every readdir (#10606) * mount: readdir enters a child in the inode table only when it takes a reference Only readdirplus into the kernel takes a reference on the children it reports, and only that reference brings a FORGET later to take the entry back out. Every other listing was inserting all its children anyway. On WinFsp that meant a listing looked each child up, took a reference, and immediately gave it back, so a walk of a wide directory paid three write-lock acquisitions per entry to leave the table exactly as it found it. On a plain kernel readdir nothing gives the entry back at all, so listing a directory of 200k files grew both maps by 200k entries that were never reclaimed. A dirent's inode number is informational either way: the kernel must LOOKUP before it can use a nodeid, and the WinFsp adapter re-resolves every operation by path. So report the number and let the mapping be built when something actually looks the entry up. * mount: take the readdirplus reference without a second full lookup The entry has just been resolved a few lines above, so redoing the whole lookup only rebuilds the child path and walks both maps again to reach a counter. Bump it directly, falling back to the full lookup if a Forget removed the entry in between. * mount: benchmark a readdir over a 200k directory Drives doReadDirectory against a meta cache holding 200k entries, one round of 4096 at a time, for the three front ends that behave differently: a plain kernel readdir, kernel readdirplus, and a WinFsp listing that gets attributes but never returns a reference. Reports what each leaves behind in the inode table alongside the usual metrics. The sink declares TakesLookupRef as an ordinary method rather than through the interface, so the same file runs unchanged against an older tree for comparison. * mount: stamp an inode on the benchmark's entries The filer stores one on every entry it writes, so a real listing arrives with an inode and never derives its own. Leaving it zero made every child in the benchmark fall through to the MD5 in AsInode, work no filer-backed mount does, and charged it to both sides of the comparison. --- mount.test | Bin 0 -> 38354594 bytes weed/mount/dir_sink.go | 6 + weed/mount/inode_to_path.go | 29 +++ weed/mount/weedfs_dir_read.go | 19 +- weed/mount/weedfs_dir_read_bench_test.go | 222 +++++++++++++++++++++++ weed/mount/winfsp/fs_windows.go | 9 +- 6 files changed, 276 insertions(+), 9 deletions(-) create mode 100755 mount.test create mode 100644 weed/mount/weedfs_dir_read_bench_test.go diff --git a/mount.test b/mount.test new file mode 100755 index 0000000000000000000000000000000000000000..1aa1d396768819d6bcb25b8fa8feb446373c0209 GIT binary patch literal 38354594 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 z z{odvLoDa9n$_2DIAP*zJ-SSO1fRlJKirX0*JbW~O!IY*v{^=|IvUV{A+3oV^?As2>Zxu-RalHV!tLu((!r&QK64&7}Q z6wvOe&>*#{YGSJM;MUa7pqEYX71}k2AKKc=(boSV){67JGhkKnUByl5)koU#?X