@@ -489,12 +489,14 @@ void inode_sb_list_add(struct inode *inode)
spin_lock(&inode->i_sb->s_inode_list_lock);
list_add(&inode->i_sb_list, &inode->i_sb->s_inodes);
spin_unlock(&inode->i_sb->s_inode_list_lock);
+ this_cpu_inc(*inode->i_sb->s_inodes_nr);
}
EXPORT_SYMBOL_GPL(inode_sb_list_add);
static inline void inode_sb_list_del(struct inode *inode)
{
if (!list_empty(&inode->i_sb_list)) {
+ this_cpu_dec(*inode->i_sb->s_inodes_nr);
spin_lock(&inode->i_sb->s_inode_list_lock);
list_del_init(&inode->i_sb_list);
spin_unlock(&inode->i_sb->s_inode_list_lock);
@@ -278,6 +278,7 @@ static void destroy_super_work(struct work_struct *work)
security_sb_free(s);
put_user_ns(s->s_user_ns);
kfree(s->s_subtype);
+ free_percpu(s->s_inodes_nr);
for (int i = 0; i < SB_FREEZE_LEVELS; i++)
percpu_free_rwsem(&s->s_writers.rw_sem[i]);
kfree(s);
@@ -298,6 +299,7 @@ static void destroy_unused_super(struct super_block *s)
super_unlock_excl(s);
list_lru_destroy(&s->s_dentry_lru);
list_lru_destroy(&s->s_inode_lru);
+ free_percpu(s->s_inodes_nr);
shrinker_free(s->s_shrink);
/* no delays needed */
destroy_super_work(&s->destroy_work);
@@ -375,6 +377,10 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags,
s->s_time_min = TIME64_MIN;
s->s_time_max = TIME64_MAX;
+ s->s_inodes_nr = alloc_percpu(size_t);
+ if (!s->s_inodes_nr)
+ goto fail;
+
s->s_shrink = shrinker_alloc(SHRINKER_NUMA_AWARE | SHRINKER_MEMCG_AWARE,
"sb-%s", type->name);
if (!s->s_shrink)
@@ -408,6 +414,7 @@ static void __put_super(struct super_block *s)
WARN_ON(s->s_dentry_lru.node);
WARN_ON(s->s_inode_lru.node);
WARN_ON(!list_empty(&s->s_mounts));
+ WARN_ON(per_cpu_sum(s->s_inodes_nr));
call_rcu(&s->rcu, destroy_super_rcu);
}
}
@@ -1346,6 +1346,7 @@ struct super_block {
/* s_inode_list_lock protects s_inodes */
spinlock_t s_inode_list_lock ____cacheline_aligned_in_smp;
struct list_head s_inodes; /* all inodes */
+ size_t __percpu *s_inodes_nr;
spinlock_t s_inode_wblist_lock;
struct list_head s_inodes_wb; /* writeback inodes */
Upcoming shrinker debugging patchset is going to give us a callback for reporting on all memory owned by a shrinker. This adds a counter for total number of inodes allocated for a given superblock, so we can compare with the number of reclaimable inodes we already have. Cc: Alexander Viro <viro@zeniv.linux.org.uk> Cc: Christian Brauner <brauner@kernel.org> Cc: Dave Chinner <david@fromorbit.com> Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev> --- fs/inode.c | 2 ++ fs/super.c | 7 +++++++ include/linux/fs.h | 1 + 3 files changed, 10 insertions(+)