The nr_dentry_negative counter only tracks the number of negative dentries in lru lists, not when they are in shrink lists. In both __d_clear_type_and_inode() and __d_instantiate(), only the DCACHE_LRU_LIST flag is checked. Though it is highly unlikely that the DCACHE_SHRINK_LIST flag may be set, it is still possible. Fix that by checking the DCACHE_SHRINK_LIST flag as well to make sure that the accounting is correct. The negative dentry test is also moved from __d_instantiate() to __d_set_inode_and_type() to cover more cases. Fixes: af0c9af1b3f6 ("fs/dcache: Track & report number of negative dentries") Signed-off-by: Waiman Long <longman@xxxxxxxxxx> --- fs/dcache.c | 13 +++++++------ include/linux/dcache.h | 9 +++++++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index b280e07e162b..c17b538bf41c 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -315,6 +315,12 @@ static inline void __d_set_inode_and_type(struct dentry *dentry, { unsigned flags; + /* + * Decrement negative dentry count if it was in the LRU list. + */ + if (unlikely(d_in_lru(dentry) && d_is_negative(dentry))) + this_cpu_dec(nr_dentry_negative); + dentry->d_inode = inode; flags = READ_ONCE(dentry->d_flags); flags &= ~(DCACHE_ENTRY_TYPE | DCACHE_FALLTHRU); @@ -329,7 +335,7 @@ static inline void __d_clear_type_and_inode(struct dentry *dentry) flags &= ~(DCACHE_ENTRY_TYPE | DCACHE_FALLTHRU); WRITE_ONCE(dentry->d_flags, flags); dentry->d_inode = NULL; - if (dentry->d_flags & DCACHE_LRU_LIST) + if (d_in_lru(dentry)) this_cpu_inc(nr_dentry_negative); } @@ -1919,11 +1925,6 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode) WARN_ON(d_in_lookup(dentry)); spin_lock(&dentry->d_lock); - /* - * Decrement negative dentry count if it was in the LRU list. - */ - if (dentry->d_flags & DCACHE_LRU_LIST) - this_cpu_dec(nr_dentry_negative); hlist_add_head(&dentry->d_u.d_alias, &inode->i_dentry); raw_write_seqcount_begin(&dentry->d_seq); __d_set_inode_and_type(dentry, inode, add_flags); diff --git a/include/linux/dcache.h b/include/linux/dcache.h index c1488cc84fd9..2762ca2508f9 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -369,6 +369,15 @@ static inline void d_lookup_done(struct dentry *dentry) } } +/* + * Dentry is in a LRU list, not a shrink list. + */ +static inline bool d_in_lru(struct dentry *dentry) +{ + return (dentry->d_flags & (DCACHE_SHRINK_LIST | DCACHE_LRU_LIST)) + == DCACHE_LRU_LIST; +} + extern void dput(struct dentry *); static inline bool d_managed(const struct dentry *dentry) -- 2.18.1