[PATCH v2 0/7] mm: switch THP shrinker to list_lru

Johannes Weiner
2026-03-12T20:53:34Z

This is version 2 of switching the THP shrinker to list_lru. I fixed
the lockdep splat Usama had called out - with explicit rcu_read_lock()
annotation based on list feedback (thanks!). I also split out the
list_lru prep bits, per Dave's request, to make review a bit easier.

I was initially looking at mm/shrinker.c and the scalability issues
resulting from shrinker_alloc -> for_each_nid -> for_each_memcg. In
the process of working on that, though, the open-coded THP shrinker
queue started getting in the way. Switching it to list_lru seemed like
a good bugfix and cleanup (9 files changed, 140 insertions(+), 300
deletions(-)) in its own right, so here goes.

Patches 1-4 are cleanups and small refactors in list_lru code. They're
basically independent, but make the THP shrinker conversion easier.

Patch 5 extends the list_lru API to allow the caller to control the
locking scope. The THP shrinker has private state it needs to keep
synchronized with the LRU state.

Patch 6 extends the list_lru API with a convenience helper to do
list_lru head allocation (memcg_list_lru_alloc) when coming from a
folio. Anon THPs are instantiated in several places, and with the
folio reparenting patches pending, folio_memcg() access is now a more
delicate dance. This avoids having to replicate that dance everywhere.

Patch 7 finally switches the deferred_split_queue to list_lru.

Based on mm-unstable.

include/linux/huge_mm.h | 6 +-
include/linux/list_lru.h | 46 ++++++
include/linux/memcontrol.h | 4 -
include/linux/mmzone.h | 12 --
mm/huge_memory.c | 330 +++++++++++++------------------------------
mm/internal.h | 2 +-
mm/khugepaged.c | 7 +
mm/list_lru.c | 193 ++++++++++++++++---------
mm/memcontrol.c | 12 +-
mm/memory.c | 52 ++++---
mm/mm_init.c | 15 --
11 files changed, 309 insertions(+), 370 deletions(-)