Commit 0baad6f9b997 ("fs/super: skip non-memcg-aware nr_cached_objects in memcg slab shrink") added a check in fs/super.c that skipped every ->nr_cached_objects() hook whenever the shrinker was invoked for a non-root memcg, on the assumption that none of them honour sc->memcg. That assumption is wrong for XFS, whose inode-reclaim hook is intentionally driven from per-memcg contexts to free memcg-charged slab. Encoding a blanket "never memcg-aware" policy in fs/super.c short-circuits that path. Push the check down into the callbacks whose counters really are irrelevant to per-memcg reclaim - btrfs_nr_cached_objects() and shmem_unused_huge_count() - and drop the fs/super.c gate. Each filesystem can now lift the restriction independently if its counter later grows memcg awareness, without touching fs/super.c. Introduce mem_cgroup_shrink_is_root() in <linux/memcontrol.h> so the callbacks don't open-code "sc->memcg is NULL or root".
Unified · Split
| @@ -22,6 +22,7 @@ · 21 unmodified lines | |||
| 22 | #include <linux/namei.h> | 22 | #include <linux/namei.h> |
| 23 | #include <linux/miscdevice.h> | 23 | #include <linux/miscdevice.h> |
| 24 | #include <linux/magic.h> | 24 | #include <linux/magic.h> |
| 25 | #include <linux/memcontrol.h> | ||
| 25 | #include <linux/slab.h> | 26 | #include <linux/slab.h> |
| 26 | #include <linux/ratelimit.h> | 27 | #include <linux/ratelimit.h> |
| 27 | #include <linux/crc32c.h> | 28 | #include <linux/crc32c.h> |
| @@ -2434,6 +2435,15 @@ static long btrfs_nr_cached_objects(struct super_block *sb, struct shrink_contro · 2406 unmodified lines | |||
| 2434 | struct btrfs_fs_info *fs_info = btrfs_sb(sb); | 2435 | struct btrfs_fs_info *fs_info = btrfs_sb(sb); |
| 2435 | const s64 nr = percpu_counter_read_positive(&fs_info->evictable_extent_maps); | 2436 | const s64 nr = percpu_counter_read_positive(&fs_info->evictable_extent_maps); |
| 2436 | 2437 | ||
| 2438 | /* | ||
| 2439 | * The evictable extent map counter is filesystem-global and does not | ||
| 2440 | * honour sc->memcg, so it is only meaningful on the global (kswapd or | ||
| 2441 | * root direct reclaim) shrink path. Skip the per-memcg iterations of | ||
| 2442 | * shrink_slab_memcg() to avoid queueing duplicate global work. | ||
| 2443 | */ | ||
| 2444 | if (!mem_cgroup_shrink_is_root(sc)) | ||
| 2445 | return 0; | ||
| 2446 | |||
| 2437 | trace_btrfs_extent_map_shrinker_count(fs_info, nr); | 2447 | trace_btrfs_extent_map_shrinker_count(fs_info, nr); |
| 2438 | 2448 | ||
| 2439 | return nr; | 2449 | return nr; |
| @@ -520,6 +520,22 @@ static inline bool mem_cgroup_is_root(struct mem_cgroup *memcg) · 519 unmodified lines | |||
| 520 | return (memcg == root_mem_cgroup); | 520 | return (memcg == root_mem_cgroup); |
| 521 | } | 521 | } |
| 522 | 522 | ||
| 523 | /** | ||
| 524 | * mem_cgroup_shrink_is_root - is this a global or root-memcg shrink invocation? | ||
| 525 | * @sc: shrink_control describing the current shrinker call | ||
| 526 | * | ||
| 527 | * Returns true when @sc represents a global reclaim shrink (sc->memcg == NULL) | ||
| 528 | * or a root-memcg shrink, i.e. not a per-memcg iteration of | ||
| 529 | * shrink_slab_memcg(). Filesystems whose ->nr_cached_objects()/ | ||
| 530 | * ->free_cached_objects() implementations operate on filesystem-global state | ||
| 531 | * and do not honour sc->memcg can use this to early-return 0 in per-memcg | ||
| 532 | * contexts. | ||
| 533 | */ | ||
| 534 | static inline bool mem_cgroup_shrink_is_root(struct shrink_control *sc) | ||
| 535 | { | ||
| 536 | return !sc->memcg || mem_cgroup_is_root(sc->memcg); | ||
| 537 | } | ||
| 538 | |||
| 523 | static inline bool obj_cgroup_is_root(const struct obj_cgroup *objcg) | 539 | static inline bool obj_cgroup_is_root(const struct obj_cgroup *objcg) |
| 524 | { | 540 | { |
| 525 | return objcg->is_root; | 541 | return objcg->is_root; |
| @@ -1071,6 +1087,11 @@ static inline bool mem_cgroup_is_root(struct mem_cgroup *memcg) · 545 unmodified lines | |||
| 1071 | return true; | 1087 | return true; |
| 1072 | } | 1088 | } |
| 1073 | 1089 | ||
| 1090 | static inline bool mem_cgroup_shrink_is_root(struct shrink_control *sc) | ||
| 1091 | { | ||
| 1092 | return true; | ||
| 1093 | } | ||
| 1094 | |||
| 1074 | static inline bool obj_cgroup_is_root(const struct obj_cgroup *objcg) | 1095 | static inline bool obj_cgroup_is_root(const struct obj_cgroup *objcg) |
| 1075 | { | 1096 | { |
| 1076 | return true; | 1097 | return true; |
| @@ -846,6 +846,16 @@ static long shmem_unused_huge_count(struct super_block *sb, · 845 unmodified lines | |||
| 846 | struct shrink_control *sc) | 846 | struct shrink_control *sc) |
| 847 | { | 847 | { |
| 848 | struct shmem_sb_info *sbinfo = SHMEM_SB(sb); | 848 | struct shmem_sb_info *sbinfo = SHMEM_SB(sb); |
| 849 | |||
| 850 | /* | ||
| 851 | * The per-superblock shrinklist is filesystem-global and does not | ||
| 852 | * honour sc->memcg, so it is only meaningful on the global (kswapd or | ||
| 853 | * root direct reclaim) shrink path. Skip the per-memcg iterations of | ||
| 854 | * shrink_slab_memcg() to avoid queueing duplicate global work. | ||
| 855 | */ | ||
| 856 | if (!mem_cgroup_shrink_is_root(sc)) | ||
| 857 | return 0; | ||
| 858 | |||
| 849 | return READ_ONCE(sbinfo->shrinklist_len); | 859 | return READ_ONCE(sbinfo->shrinklist_len); |
| 850 | } | 860 | } |
| 851 | #else /* !CONFIG_TRANSPARENT_HUGEPAGE */ | 861 | #else /* !CONFIG_TRANSPARENT_HUGEPAGE */ |