Skip to contentSkip to conversation

fs/super: skip non-memcg-aware nr_cached_objects in memcg slab shrink

PATCH Applied Usama Arif 5 participants 11 messages Jun 9 – Jul 14

Subsystems (3)
lore
Share & tools
lore

Usama Arif

author
#0 lore raw

PATCH 1/1 fs/super: skip non-memcg-aware nr_cached_objects in memcg slab shrink

The super_block shrinker is registered with SHRINKER_MEMCG_AWARE because its dentry and inode LRUs are memcg-aware (via list_lru). But the optional ->nr_cached_objects() hooks that the shrinker also drives are not memcg-aware:
btrfs extent maps and xfs inode reclaim operate on filesystem-global state, and shmem's unused-huge shrinker walks a per-superblock shrinklist. None of them filter by sc->memcg.

The mismatch shows up under memcg-heavy slab reclaim. shrink_slab_memcg() calls do_shrink_slab() once per (memcg, NUMA node) pair for every memcg whose bit is set in the per-superblock shrinker bitmap, which on a busy host means hundreds of calls per reclaim pass. Each scan queues the same global shrinker work item that's already kicked from the root path.

Because btrfs/xfs global count is typically non-zero on any in-use filesystem, the returned total stays positive even if a memcg's own dentry/inode LRUs are empty. shrink_slab_memcg() therefore never clears the SB shrinker bit in the memcg bitmap, so subsequent reclaim passes from the same memcg re-enter super_cache_count() and pay for the global counter walk again.

Restrict ->nr_cached_objects() to the global shrink path (sc->memcg NULL or root). The memcg-aware dentry/inode LRUs keep being counted and scanned per memcg as before; only the global fs-specific hooks are skipped. The root/global shrink path still drives those hooks; only their invocation from non-root memcg slab reclaim is removed.

---
 fs/super.c | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)
fs/super.c +17 -2
index 378e81efe643..5216c5dbd4c4 100644
@@ -24,6 +24,7 @@
 #include <linux/export.h>
 #include <linux/slab.h>
 #include <linux/blkdev.h>
+#include <linux/memcontrol.h>
 #include <linux/mount.h>
 #include <linux/security.h>
 #include <linux/writeback.h>		/* for the emergency remount stuff */
@@ -169,6 +170,19 @@ static void super_wake(struct super_block *sb, unsigned int flag)
 	wake_up_var(&sb->s_flags);
 }
 
+/*
+ * The s_op->nr_cached_objects hooks (used for example by btrfs and xfs)
+ * operate on filesystem-global state and ignore sc->memcg. Driving them
+ * from per-memcg shrink_slab_memcg() invocations only burns CPU walking
+ * per-cpu counters and queueing duplicate work: the actual reclaim happens on
+ * the global path (kswapd or root direct reclaim) regardless. Restrict them
+ * to that path.
+ */
+static inline bool super_fs_objects_eligible(struct shrink_control *sc)
+{
+	return !sc->memcg || mem_cgroup_is_root(sc->memcg);
+}
+
 /*
  * One thing we have to be careful of with a per-sb shrinker is that we don't
  * drop the last active reference to the superblock from within the shrinker.
@@ -198,7 +212,7 @@ static unsigned long super_cache_scan(struct shrinker *shrink,
 	if (!super_trylock_shared(sb))
 		return SHRINK_STOP;
 
-	if (sb->s_op->nr_cached_objects)
+	if (sb->s_op->nr_cached_objects && super_fs_objects_eligible(sc))
 		fs_objects = sb->s_op->nr_cached_objects(sb, sc);
 
 	inodes = list_lru_shrink_count(&sb->s_inode_lru, sc);
@@ -259,7 +273,8 @@ static unsigned long super_cache_count(struct shrinker *shrink,
 		return 0;
 	smp_rmb();
 
-	if (sb->s_op && sb->s_op->nr_cached_objects)
+	if (sb->s_op && sb->s_op->nr_cached_objects &&
+	    super_fs_objects_eligible(sc))
 		total_objects = sb->s_op->nr_cached_objects(sb, sc);
 
 	total_objects += list_lru_shrink_count(&sb->s_dentry_lru, sc);
Signature · 1 line
2.52.0
reviewer
#1 lore raw
Quoted 19 lines · Usama Arif

The super_block shrinker is registered with SHRINKER_MEMCG_AWARE because its dentry and inode LRUs are memcg-aware (via list_lru). But the optional ->nr_cached_objects() hooks that the shrinker also drives are not memcg-aware:
btrfs extent maps and xfs inode reclaim operate on filesystem-global state, and shmem's unused-huge shrinker walks a per-superblock shrinklist. None of them filter by sc->memcg.

The mismatch shows up under memcg-heavy slab reclaim. shrink_slab_memcg() calls do_shrink_slab() once per (memcg, NUMA node) pair for every memcg whose bit is set in the per-superblock shrinker bitmap, which on a busy host means hundreds of calls per reclaim pass. Each scan queues the same global shrinker work item that's already kicked from the root path.

Because btrfs/xfs global count is typically non-zero on any in-use filesystem, the returned total stays positive even if a memcg's own dentry/inode LRUs are empty. shrink_slab_memcg() therefore never clears the SB shrinker bit in the memcg bitmap, so subsequent reclaim passes from the same memcg re-enter super_cache_count() and pay for the global counter walk again.

Quoted 7 lines

Restrict ->nr_cached_objects() to the global shrink path (sc->memcg NULL or root). The memcg-aware dentry/inode LRUs keep being counted and scanned per memcg as before; only the global fs-specific hooks are skipped. The root/global shrink path still drives those hooks; only their invocation from non-root memcg slab reclaim is removed.

To me this makes sense. However I'm bit surprised that XFS inode shrinker (which is what gets counted in nr_cached_objects for XFS) isn't memcg aware. I guess since these inodes are on their way to a relatively quick destruction, nobody really bothered. So I'm fine with the change, just I'd like to make sure XFS folks are aware and don't plan anything in this area.

Honza

Quoted 58 lines
---
 fs/super.c | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)
fs/super.c +17 -2
index 378e81efe643..5216c5dbd4c4 100644
@@ -24,6 +24,7 @@
 #include <linux/export.h>
 #include <linux/slab.h>
 #include <linux/blkdev.h>
+#include <linux/memcontrol.h>
 #include <linux/mount.h>
 #include <linux/security.h>
 #include <linux/writeback.h>		/* for the emergency remount stuff */
@@ -169,6 +170,19 @@ static void super_wake(struct super_block *sb, unsigned int flag)
 	wake_up_var(&sb->s_flags);
 }
 
+/*
+ * The s_op->nr_cached_objects hooks (used for example by btrfs and xfs)
+ * operate on filesystem-global state and ignore sc->memcg. Driving them
+ * from per-memcg shrink_slab_memcg() invocations only burns CPU walking
+ * per-cpu counters and queueing duplicate work: the actual reclaim happens on
+ * the global path (kswapd or root direct reclaim) regardless. Restrict them
+ * to that path.
+ */
+static inline bool super_fs_objects_eligible(struct shrink_control *sc)
+{
+	return !sc->memcg || mem_cgroup_is_root(sc->memcg);
+}
+
 /*
  * One thing we have to be careful of with a per-sb shrinker is that we don't
  * drop the last active reference to the superblock from within the shrinker.
@@ -198,7 +212,7 @@ static unsigned long super_cache_scan(struct shrinker *shrink,
 	if (!super_trylock_shared(sb))
 		return SHRINK_STOP;
 
-	if (sb->s_op->nr_cached_objects)
+	if (sb->s_op->nr_cached_objects && super_fs_objects_eligible(sc))
 		fs_objects = sb->s_op->nr_cached_objects(sb, sc);
 
 	inodes = list_lru_shrink_count(&sb->s_inode_lru, sc);
@@ -259,7 +273,8 @@ static unsigned long super_cache_count(struct shrinker *shrink,
 		return 0;
 	smp_rmb();
 
-	if (sb->s_op && sb->s_op->nr_cached_objects)
+	if (sb->s_op && sb->s_op->nr_cached_objects &&
+	    super_fs_objects_eligible(sc))
 		total_objects = sb->s_op->nr_cached_objects(sb, sc);
 
 	total_objects += list_lru_shrink_count(&sb->s_dentry_lru, sc);
Signature · 1 line
2.52.0
Signature · 2 lines
Jan Kara <[email protected]>
SUSE Labs, CR
author
#2 lore raw
Quoted 26 lines · Jan Kara
Quoted 25 lines · Usama Arif

The super_block shrinker is registered with SHRINKER_MEMCG_AWARE because its dentry and inode LRUs are memcg-aware (via list_lru). But the optional ->nr_cached_objects() hooks that the shrinker also drives are not memcg-aware:
btrfs extent maps and xfs inode reclaim operate on filesystem-global state, and shmem's unused-huge shrinker walks a per-superblock shrinklist. None of them filter by sc->memcg.

The mismatch shows up under memcg-heavy slab reclaim. shrink_slab_memcg() calls do_shrink_slab() once per (memcg, NUMA node) pair for every memcg whose bit is set in the per-superblock shrinker bitmap, which on a busy host means hundreds of calls per reclaim pass. Each scan queues the same global shrinker work item that's already kicked from the root path.

Because btrfs/xfs global count is typically non-zero on any in-use filesystem, the returned total stays positive even if a memcg's own dentry/inode LRUs are empty. shrink_slab_memcg() therefore never clears the SB shrinker bit in the memcg bitmap, so subsequent reclaim passes from the same memcg re-enter super_cache_count() and pay for the global counter walk again.

Restrict ->nr_cached_objects() to the global shrink path (sc->memcg NULL or root). The memcg-aware dentry/inode LRUs keep being counted and scanned per memcg as before; only the global fs-specific hooks are skipped. The root/global shrink path still drives those hooks; only their invocation from non-root memcg slab reclaim is removed.

Quoted 9 lines
Quoted 1 line

To me this makes sense. However I'm bit surprised that XFS inode shrinker (which is what gets counted in nr_cached_objects for XFS) isn't memcg aware. I guess since these inodes are on their way to a relatively quick destruction, nobody really bothered. So I'm fine with the change, just I'd like to make sure XFS folks are aware and don't plan anything in this area.

Thanks for the review!

Yes I am hoping that if there are any objections from xfs or btrfs, it gets raised. Have cc'ed the btrfs and xfs maintainers and reviewers.

Thanks!
Usama

Quoted 60 lines

Honza

Quoted 58 lines
---
 fs/super.c | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)
fs/super.c +17 -2
index 378e81efe643..5216c5dbd4c4 100644
@@ -24,6 +24,7 @@
 #include <linux/export.h>
 #include <linux/slab.h>
 #include <linux/blkdev.h>
+#include <linux/memcontrol.h>
 #include <linux/mount.h>
 #include <linux/security.h>
 #include <linux/writeback.h>		/* for the emergency remount stuff */
@@ -169,6 +170,19 @@ static void super_wake(struct super_block *sb, unsigned int flag)
 	wake_up_var(&sb->s_flags);
 }
 
+/*
+ * The s_op->nr_cached_objects hooks (used for example by btrfs and xfs)
+ * operate on filesystem-global state and ignore sc->memcg. Driving them
+ * from per-memcg shrink_slab_memcg() invocations only burns CPU walking
+ * per-cpu counters and queueing duplicate work: the actual reclaim happens on
+ * the global path (kswapd or root direct reclaim) regardless. Restrict them
+ * to that path.
+ */
+static inline bool super_fs_objects_eligible(struct shrink_control *sc)
+{
+	return !sc->memcg || mem_cgroup_is_root(sc->memcg);
+}
+
 /*
  * One thing we have to be careful of with a per-sb shrinker is that we don't
  * drop the last active reference to the superblock from within the shrinker.
@@ -198,7 +212,7 @@ static unsigned long super_cache_scan(struct shrinker *shrink,
 	if (!super_trylock_shared(sb))
 		return SHRINK_STOP;
 
-	if (sb->s_op->nr_cached_objects)
+	if (sb->s_op->nr_cached_objects && super_fs_objects_eligible(sc))
 		fs_objects = sb->s_op->nr_cached_objects(sb, sc);
 
 	inodes = list_lru_shrink_count(&sb->s_inode_lru, sc);
@@ -259,7 +273,8 @@ static unsigned long super_cache_count(struct shrinker *shrink,
 		return 0;
 	smp_rmb();
 
-	if (sb->s_op && sb->s_op->nr_cached_objects)
+	if (sb->s_op && sb->s_op->nr_cached_objects &&
+	    super_fs_objects_eligible(sc))
 		total_objects = sb->s_op->nr_cached_objects(sb, sc);
 
 	total_objects += list_lru_shrink_count(&sb->s_dentry_lru, sc);
Signature · 1 line
2.52.0
maintainer Applied
#3 lore raw
In reply to Usama Arif
Quoted 1 line · Usama Arif

fs/super: skip non-memcg-aware nr_cached_objects in memcg slab shrink

Applied to the vfs-7.3.misc branch of the vfs/vfs.git tree. Patches in the vfs-7.3.misc branch should appear in linux-next soon.

Please report any outstanding bugs that were missed during review in a new review to the original patch series allowing us to drop it.

It's encouraged to provide Acked-bys and Reviewed-bys even though the patch has now been applied. If possible patch trailers will be updated.

Note that commit hashes shown below are subject to change due to rebase, trailer updates or similar. If in doubt, please check the listed branch.

tree:   https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git
branch: vfs-7.3.misc

[1/1] fs/super: skip non-memcg-aware nr_cached_objects in memcg slab shrink

      https://git.kernel.org/vfs/vfs/c/5eacee52be6b
#4 lore raw
In reply to Usama Arif
Quoted 6 lines · Usama Arif

The super_block shrinker is registered with SHRINKER_MEMCG_AWARE because its dentry and inode LRUs are memcg-aware (via list_lru). But the optional ->nr_cached_objects() hooks that the shrinker also drives are not memcg-aware:
btrfs extent maps and xfs inode reclaim operate on filesystem-global state, and shmem's unused-huge shrinker walks a per-superblock shrinklist. None of them filter by sc->memcg.

I see the underlying objects whose count is returned by ->nr_cached_objects() hook is memcg charged for shmem and xfs but not for btrfs. Do you envision there might be a rare scenario where we have a lot of memory charged to a memcg consumed by objects which ->nr_cached_objects() tracks and that memory becomes unreclaimable due to this patch?

Quoted 7 lines

The mismatch shows up under memcg-heavy slab reclaim. shrink_slab_memcg() calls do_shrink_slab() once per (memcg, NUMA node) pair for every memcg whose bit is set in the per-superblock shrinker bitmap, which on a busy host means hundreds of calls per reclaim pass. Each scan queues the same global shrinker work item that's already kicked from the root path.

Quoted 5 lines

Because btrfs/xfs global count is typically non-zero on any in-use filesystem, the returned total stays positive even if a memcg's own dentry/inode LRUs are empty. shrink_slab_memcg() therefore never clears the SB shrinker bit in the memcg bitmap, so subsequent reclaim passes from the same memcg re-enter super_cache_count() and pay for the global counter walk again.

What is the main concern? Is it the amount of CPU wasted or are we over reclaiming or reclaiming from unrelated memcgs?

Quoted 8 lines

Restrict ->nr_cached_objects() to the global shrink path (sc->memcg NULL or root). The memcg-aware dentry/inode LRUs keep being counted and scanned per memcg as before; only the global fs-specific hooks are skipped. The root/global shrink path still drives those hooks; only their invocation from non-root memcg slab reclaim is removed.

I am fine with the stopgap but it would be nice to have proper memcg awareness in xfs and shmem callbacks. For btrfs, I am not sure if it makes sense to memcg charge btrfs_extent_map objects but at least to decision to skip memcg reclaim will be inside the fs callbacks i.e. nr_cached_objects.

author
#5 lore raw
Quoted 8 lines · Shakeel Butt
Quoted 6 lines · Usama Arif

The super_block shrinker is registered with SHRINKER_MEMCG_AWARE because its dentry and inode LRUs are memcg-aware (via list_lru). But the optional ->nr_cached_objects() hooks that the shrinker also drives are not memcg-aware:
btrfs extent maps and xfs inode reclaim operate on filesystem-global state, and shmem's unused-huge shrinker walks a per-superblock shrinklist. None of them filter by sc->memcg.

Quoted 5 lines

I see the underlying objects whose count is returned by ->nr_cached_objects() hook is memcg charged for shmem and xfs but not for btrfs. Do you envision there might be a rare scenario where we have a lot of memory charged to a memcg consumed by objects which ->nr_cached_objects() tracks and that memory becomes unreclaimable due to this patch?

Hello!

Thanks for the review.

For XFS, xfs_inode is SLAB_ACCOUNT, so a memcg can have memory charged in XFS inodes sitting in XFS' internal reclaim state. But the current callback does not target that memcg: xfs_fs_free_cached_objects() calls xfs_reclaim_inodes_nr(), which walks the mount and reclaims XFS_ICI_RECLAIM_TAG inodes without checking sc->memcg. So non-root memcg reclaim was only getting an opportunistic mount-wide reclaim pass here; it could reclaim the target memcg's inodes by chance, but it could just as well reclaim inodes charged to other memcgs. This patch removes that cross-memcg side effect, not a correct memcg-targeted reclaim path.

Those XFS inodes are still reclaimed by XFS' own reclaim worker, which is queued when reclaimable inodes are tagged and requeues while reclaimable inodes remain. With the default xfssyncd_centisecs value, that is about every 5 seconds. The root/global superblock shrinker path also continues to call the XFS callbacks. This will keep the memory reclaimable.

For shmem, shrinklist_len counts inodes whose tail large folio could be split — splitting itself doesn't free anything; it just lets normal page LRU reclaim the truncated tail. The folios are on the memcg-aware LRU and will be aged/reclaimed there independently, so skipping the split from memcg context just delays the split?

or btrfs extent maps, as you note, these objects are not memcg-charged.

Quoted 8 lines
Quoted 8 lines

The mismatch shows up under memcg-heavy slab reclaim. shrink_slab_memcg() calls do_shrink_slab() once per (memcg, NUMA node) pair for every memcg whose bit is set in the per-superblock shrinker bitmap, which on a busy host means hundreds of calls per reclaim pass. Each scan queues the same global shrinker work item that's already kicked from the root path.

Quoted 9 lines
Quoted 5 lines

Because btrfs/xfs global count is typically non-zero on any in-use filesystem, the returned total stays positive even if a memcg's own dentry/inode LRUs are empty. shrink_slab_memcg() therefore never clears the SB shrinker bit in the memcg bitmap, so subsequent reclaim passes from the same memcg re-enter super_cache_count() and pay for the global counter walk again.

What is the main concern? Is it the amount of CPU wasted or are we over reclaiming or reclaiming from unrelated memcgs?

The primary concern is wasted CPU. On the busy hosts where I saw this, hundreds of memcgs all repeating that walk per reclaim pass is what made it visible in profiles.

Another concern is misattribution. Reclaim from memcg X ends up reclaiming another memcg Y, which could have an affect on Y and won't provide proper isolation.

Quoted 7 lines
Quoted 7 lines

Restrict ->nr_cached_objects() to the global shrink path (sc->memcg NULL or root). The memcg-aware dentry/inode LRUs keep being counted and scanned per memcg as before; only the global fs-specific hooks are skipped. The root/global shrink path still drives those hooks; only their invocation from non-root memcg slab reclaim is removed.

Quoted 7 lines
Quoted 1 line

I am fine with the stopgap but it would be nice to have proper memcg awareness in xfs and shmem callbacks. For btrfs, I am not sure if it makes sense to memcg charge btrfs_extent_map objects but at least to decision to skip memcg reclaim will be inside the fs callbacks i.e. nr_cached_objects.

Agreed that this is the right long term approach. If the preference is to move this down to the fs own callbacks instead of fs/super.c, I can do that in the revision as well.

#6 lore raw
In reply to Usama Arif

Hi Usama,

Quoted 6 lines · Usama Arif

The super_block shrinker is registered with SHRINKER_MEMCG_AWARE because its dentry and inode LRUs are memcg-aware (via list_lru). But the optional ->nr_cached_objects() hooks that the shrinker also drives are not memcg-aware:
btrfs extent maps and xfs inode reclaim operate on filesystem-global state, and shmem's unused-huge shrinker walks a per-superblock shrinklist. None of them filter by sc->memcg.

This makes sense for now, but how should we handle this if these shrinkers are made memcg-aware in the future?

I've recently been working on making the shmem huge shrinker memcg-aware, but noticed my changes broke after rebasing onto the latest tree, which led me to this patch.

As for the shmem huge shrinker, I can work around this limitation by making it a separate shrinker. But this feels more like an overall design decision, effectively meaning that all nr_cached_objects are precluded from being memcg-aware.

Thanks,
Qi

author
#7 lore raw
Quoted 17 lines · Qi Zheng

Hi Usama,

Quoted 6 lines · Usama Arif

The super_block shrinker is registered with SHRINKER_MEMCG_AWARE because its dentry and inode LRUs are memcg-aware (via list_lru). But the optional ->nr_cached_objects() hooks that the shrinker also drives are not memcg-aware:
btrfs extent maps and xfs inode reclaim operate on filesystem-global state, and shmem's unused-huge shrinker walks a per-superblock shrinklist. None of them filter by sc->memcg.

This makes sense for now, but how should we handle this if these shrinkers are made memcg-aware in the future?

I've recently been working on making the shmem huge shrinker memcg-aware, but noticed my changes broke after rebasing onto the latest tree, which led me to this patch.

Quoted 8 lines

As for the shmem huge shrinker, I can work around this limitation by making it a separate shrinker. But this feels more like an overall design decision, effectively meaning that all nr_cached_objects are precluded from being memcg-aware.

Thanks,
Qi

Hi Qi,

Thanks for raising this. I think the alternative is what I mentioned in
https://lore.kernel.org/all/[email protected]/
move this down to the fs own callbacks instead of here. Would that be better?

Thanks,
Usama

#8 lore raw

Hi Usama,

Quoted 30 lines · Usama Arif
Quoted 17 lines · Qi Zheng

Hi Usama,

Quoted 6 lines · Usama Arif

The super_block shrinker is registered with SHRINKER_MEMCG_AWARE because its dentry and inode LRUs are memcg-aware (via list_lru). But the optional ->nr_cached_objects() hooks that the shrinker also drives are not memcg-aware:
btrfs extent maps and xfs inode reclaim operate on filesystem-global state, and shmem's unused-huge shrinker walks a per-superblock shrinklist. None of them filter by sc->memcg.

This makes sense for now, but how should we handle this if these shrinkers are made memcg-aware in the future?

I've recently been working on making the shmem huge shrinker memcg-aware, but noticed my changes broke after rebasing onto the latest tree, which led me to this patch.

Quoted 8 lines

As for the shmem huge shrinker, I can work around this limitation by making it a separate shrinker. But this feels more like an overall design decision, effectively meaning that all nr_cached_objects are precluded from being memcg-aware.

Thanks,
Qi

Quoted 6 lines

Hi Qi,

Thanks for raising this. I think the alternative is what I mentioned in
https://lore.kernel.org/all/[email protected]/
move this down to the fs own callbacks instead of here. Would that be better?

Yes, I think this is a better approach.

Later on, shrinkers that are converted to be memcg-aware can lift this restriction themselves.

Thanks,
Qi

Quoted 3 lines

Thanks,
Usama

author
#9 lore raw
Quoted 40 lines · On Tue, 14 Jul 2026 10:09:26 +0800 Qi Zheng <[email protected]> wrote:

Hi Usama,

Quoted 30 lines · Usama Arif
Quoted 17 lines · Qi Zheng

Hi Usama,

Quoted 6 lines · Usama Arif

The super_block shrinker is registered with SHRINKER_MEMCG_AWARE because its dentry and inode LRUs are memcg-aware (via list_lru). But the optional ->nr_cached_objects() hooks that the shrinker also drives are not memcg-aware:
btrfs extent maps and xfs inode reclaim operate on filesystem-global state, and shmem's unused-huge shrinker walks a per-superblock shrinklist. None of them filter by sc->memcg.

This makes sense for now, but how should we handle this if these shrinkers are made memcg-aware in the future?

I've recently been working on making the shmem huge shrinker memcg-aware, but noticed my changes broke after rebasing onto the latest tree, which led me to this patch.

Quoted 8 lines

As for the shmem huge shrinker, I can work around this limitation by making it a separate shrinker. But this feels more like an overall design decision, effectively meaning that all nr_cached_objects are precluded from being memcg-aware.

Thanks,
Qi

Quoted 6 lines

Hi Qi,

Thanks for raising this. I think the alternative is what I mentioned in
https://lore.kernel.org/all/[email protected]/
move this down to the fs own callbacks instead of here. Would that be better?

Quoted 7 lines

Yes, I think this is a better approach.

Later on, shrinkers that are converted to be memcg-aware can lift this restriction themselves.

Thanks,
Qi

Thanks Qi, I have sent a patch to push it down to filesystems in [1]. No functional change inteneded in there. You would have to rebase your patch on top of that one and remove my code in shmem specifically.

[1] https://lore.kernel.org/all/[email protected]/

Quoted 6 lines
Quoted 4 lines

Thanks,
Usama

#10 lore raw
Quoted 47 lines · Usama Arif
Quoted 45 lines · On Tue, 14 Jul 2026 10:09:26 +0800 Qi Zheng <[email protected]> wrote:

Hi Usama,

Quoted 30 lines · Usama Arif
Quoted 17 lines · Qi Zheng

Hi Usama,

Quoted 6 lines · Usama Arif

The super_block shrinker is registered with SHRINKER_MEMCG_AWARE because its dentry and inode LRUs are memcg-aware (via list_lru). But the optional ->nr_cached_objects() hooks that the shrinker also drives are not memcg-aware:
btrfs extent maps and xfs inode reclaim operate on filesystem-global state, and shmem's unused-huge shrinker walks a per-superblock shrinklist. None of them filter by sc->memcg.

This makes sense for now, but how should we handle this if these shrinkers are made memcg-aware in the future?

I've recently been working on making the shmem huge shrinker memcg-aware, but noticed my changes broke after rebasing onto the latest tree, which led me to this patch.

Quoted 8 lines

As for the shmem huge shrinker, I can work around this limitation by making it a separate shrinker. But this feels more like an overall design decision, effectively meaning that all nr_cached_objects are precluded from being memcg-aware.

Thanks,
Qi

Quoted 6 lines

Hi Qi,

Thanks for raising this. I think the alternative is what I mentioned in
https://lore.kernel.org/all/[email protected]/
move this down to the fs own callbacks instead of here. Would that be better?

Yes, I think this is a better approach.

Later on, shrinkers that are converted to be memcg-aware can lift this restriction themselves.

Quoted 6 lines
Quoted 2 lines

Thanks,
Qi

Thanks Qi, I have sent a patch to push it down to filesystems in [1]. No functional change inteneded in there. You would have to rebase your patch on top of that one and remove my code in shmem specifically.

Thanks! I will rebase my patch once your patch is merged into linux-next.

Quoted 9 lines

[1] https://lore.kernel.org/all/[email protected]/

Quoted 6 lines
Quoted 4 lines

Thanks,
Usama