Skip to content

Commit 3b153da

Browse files
Brian Fosterroxanan1996
authored andcommitted
vfs: don't mod negative dentry count when on shrinker list
BugLink: https://bugs.launchpad.net/bugs/2075170 [ Upstream commit aabfe57 ] The nr_dentry_negative counter is intended to only account negative dentries that are present on the superblock LRU. Therefore, the LRU add, remove and isolate helpers modify the counter based on whether the dentry is negative, but the shrinker list related helpers do not modify the counter, and the paths that change a dentry between positive and negative only do so if DCACHE_LRU_LIST is set. The problem with this is that a dentry on a shrinker list still has DCACHE_LRU_LIST set to indicate ->d_lru is in use. The additional DCACHE_SHRINK_LIST flag denotes whether the dentry is on LRU or a shrink related list. Therefore if a relevant operation (i.e. unlink) occurs while a dentry is present on a shrinker list, and the associated codepath only checks for DCACHE_LRU_LIST, then it is technically possible to modify the negative dentry count for a dentry that is off the LRU. Since the shrinker list related helpers do not modify the negative dentry count (because non-LRU dentries should not be included in the count) when the dentry is ultimately removed from the shrinker list, this can cause the negative dentry count to become permanently inaccurate. This problem can be reproduced via a heavy file create/unlink vs. drop_caches workload. On an 80xcpu system, I start 80 tasks each running a 1k file create/delete loop, and one task spinning on drop_caches. After 10 minutes or so of runtime, the idle/clean cache negative dentry count increases from somewhere in the range of 5-10 entries to several hundred (and increasingly grows beyond nr_dentry_unused). Tweak the logic in the paths that turn a dentry negative or positive to filter out the case where the dentry is present on a shrink related list. This allows the above workload to maintain an accurate negative dentry count. Fixes: af0c9af ("fs/dcache: Track & report number of negative dentries") Signed-off-by: Brian Foster <bfoster@redhat.com> Link: https://lore.kernel.org/r/20240703121301.247680-1-bfoster@redhat.com Acked-by: Ian Kent <ikent@redhat.com> Reviewed-by: Josef Bacik <josef@toxicpanda.com> Reviewed-by: Waiman Long <longman@redhat.com> Signed-off-by: Christian Brauner <brauner@kernel.org> Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Manuel Diewald <manuel.diewald@canonical.com> Signed-off-by: Stefan Bader <stefan.bader@canonical.com>
1 parent 3659f39 commit 3b153da

File tree

1 file changed

+9
-3
lines changed

1 file changed

+9
-3
lines changed

fs/dcache.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,11 @@ static inline void __d_clear_type_and_inode(struct dentry *dentry)
331331
flags &= ~(DCACHE_ENTRY_TYPE | DCACHE_FALLTHRU);
332332
WRITE_ONCE(dentry->d_flags, flags);
333333
dentry->d_inode = NULL;
334-
if (flags & DCACHE_LRU_LIST)
334+
/*
335+
* The negative counter only tracks dentries on the LRU. Don't inc if
336+
* d_lru is on another list.
337+
*/
338+
if ((flags & (DCACHE_LRU_LIST|DCACHE_SHRINK_LIST)) == DCACHE_LRU_LIST)
335339
this_cpu_inc(nr_dentry_negative);
336340
}
337341

@@ -1976,9 +1980,11 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode)
19761980

19771981
spin_lock(&dentry->d_lock);
19781982
/*
1979-
* Decrement negative dentry count if it was in the LRU list.
1983+
* The negative counter only tracks dentries on the LRU. Don't dec if
1984+
* d_lru is on another list.
19801985
*/
1981-
if (dentry->d_flags & DCACHE_LRU_LIST)
1986+
if ((dentry->d_flags &
1987+
(DCACHE_LRU_LIST|DCACHE_SHRINK_LIST)) == DCACHE_LRU_LIST)
19821988
this_cpu_dec(nr_dentry_negative);
19831989
hlist_add_head(&dentry->d_u.d_alias, &inode->i_dentry);
19841990
raw_write_seqcount_begin(&dentry->d_seq);

0 commit comments

Comments
 (0)