[PATCH] mm: list_lru: clear shrinker id when disabling memcg awareness
From: Wentao Guan
Date: Fri Sep 04 2026 - 15:14:39 EST
When cgroup.memory=nokmem is in effect, __list_lru_init() disables
memcg awareness for the list_lru, but it leaves lru->shrinker_id copied
from the supplied shrinker.
This used to be mostly harmless for memcg-aware slab list_lrus because,
with kmem accounting disabled, slab objects are not charged to memcgs and
list_lru_add_obj() passes NULL memcgs. The THP deferred split conversion
changed that: it initializes a memcg-aware list_lru with the THP shrinker
and later adds anon THPs with the folio memcg, even when kmem accounting
is disabled. The list_lru itself has been collapsed to plain per-node
lists, but the stale shrinker id still makes __list_lru_add() call
set_shrinker_bit().
This was reproduced on a kernel with CONFIG_MEMCG=y and
CONFIG_TRANSPARENT_HUGEPAGE=y, booted with kernel memory accounting
disabled. The same condition can be reproduced by booting with:
cgroup.memory=nokmem
and then faulting in anonymous THPs so that they are queued on the THP
deferred split list_lru. The warning showed that the memcg had no kmem id
and no memcg shrinker bitmap capacity, while the list_lru still carried a
valid shrinker id:
set_shrinker_bit: memcg=ffff8d23cbe60000 css_id=31 kmem_id=-1 \
dying=0 nid=0 shrinker_id=0 shrinker_nr_max=0 map_nr_max=0 \
info=ffff8d23d68c60c0
WARNING: mm/shrinker.c:218 at set_shrinker_bit+0xdb/0x100
The stack was:
set_shrinker_bit
__list_lru_add
deferred_split_folio
map_anon_folio_pmd_nopf
map_anon_folio_pmd_pf
__do_huge_pmd_anonymous_page
do_huge_pmd_anonymous_page
__handle_mm_fault
handle_mm_fault
do_user_addr_fault
exc_page_fault
asm_exc_page_fault
The list_lru code already uses shrinker_id == -1 as the sentinel that
prevents set_shrinker_bit() from doing any memcg bitmap operation. Keep
that invariant when memcg awareness is forced off due to disabled kmem
accounting.
This is related to the cgroup_disable=memory case fixed by Qinyun Tan,
where __list_lru_init() also needs to collapse list_lrus to plain
per-node lists when the memory controller is disabled entirely. That
patch makes the memcg awareness decision complete; this patch keeps the
shrinker id in sync with that decision.
Link: https://lore.kernel.org/linux-mm/20260902093202.609559-1-qinyuntan@xxxxxxxxxxxxxxxxx/
Fixes: fafaeceb89a5 ("mm: switch deferred split shrinker to list_lru")
Assisted-by: atomcode:gpt-5.5-medium
Signed-off-by: Wentao Guan <guanwentao@xxxxxxxxxxxxx>
---
mm/list_lru.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/mm/list_lru.c b/mm/list_lru.c
index 36662d02ff963..c08a5e2367106 100644
--- a/mm/list_lru.c
+++ b/mm/list_lru.c
@@ -671,8 +671,10 @@ int __list_lru_init(struct list_lru *lru, bool memcg_aware, struct shrinker *shr
else
lru->shrinker_id = -1;
- if (mem_cgroup_kmem_disabled())
+ if (mem_cgroup_kmem_disabled()) {
memcg_aware = false;
+ lru->shrinker_id = -1;
+ }
#endif
lru->node = kzalloc_objs(*lru->node, nr_node_ids);
--
2.30.2