[RFC PATCH v5 14/14] mm/mglru: Avoid scan anon while swapcache is low for GFP_NOIO allocation

From: Barry Song (Xiaomi)

Date: Thu Oct 22 2026 - 18:05:06 EST


From: Bo Zhang <zhangbo56@xxxxxxxxxx>

With GFP_NOIO, we cannot reclaim anon folios unless they are in
the swap cache. CLRU skips anon reclaim when the swap cache is
very low by commit 74d9aa0d8e98 ("mm: vmscan: avoid anon scanning
for GFP_NOIO with low swapcache"). Apply the same logic to MGLRU.

The new scan-budget-based mechanism makes this straightforward to
implement. Thanks also to Xiang Liu, who previously attempted to fix
this before we had the scan-budget-based model [1].

[1] https://lore.kernel.org/linux-mm/179248830836.1744016.8424217811286616919.noio-mglru-rfc-v2@xxxxxxxxxxxxx/
Cc: Xiang Liu <liuxiang.277@xxxxxxxxxxxxx>
Signed-off-by: Bo Zhang <zhangbo56@xxxxxxxxxx>
Co-developed-by: Barry Song (Xiaomi) <baohua@xxxxxxxxxx>
Signed-off-by: Barry Song (Xiaomi) <baohua@xxxxxxxxxx>
---
mm/vmscan.c | 20 ++++++--------------
1 file changed, 6 insertions(+), 14 deletions(-)

diff --git a/mm/vmscan.c b/mm/vmscan.c
index 6d5a823667e0..d72a5aef3a0f 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -379,13 +379,6 @@ static inline bool reclaimable_anon_is_low(struct mem_cgroup *memcg,
if (!sc || (sc->gfp_mask & __GFP_IO))
return false;

- /*
- * FIXME: MGLRU doesn't fully respect can_reclaim_anon_pages() for the
- * scanning type, so only apply this to the traditional LRU for now.
- */
- if (lru_gen_enabled())
- return false;
-
if (memcg) {
struct lruvec *lruvec = mem_cgroup_lruvec(memcg, pgdat);

@@ -5061,26 +5054,23 @@ static void lru_gen_balance_scan(struct lruvec *lruvec, struct scan_control *sc,
{
struct lru_gen_folio *lrugen = &lruvec->lrugen;
u64 refault_rate[ANON_AND_FILE], anon_factor, file_factor;
- u64 anon_scan, file_scan, total;
+ u64 anon_scan, file_scan;
u64 anon_weight, file_weight;
int size_bias;

- total = nr[LRU_GEN_ANON] + nr[LRU_GEN_FILE];
- if (!total)
- return;
-
if (swappiness <= MIN_SWAPPINESS) {
nr[LRU_GEN_ANON] = 0;
nr[LRU_GEN_FILE] = min_t(u64, total_scan, nr[LRU_GEN_FILE]);
- return;
}

if (swappiness == SWAPPINESS_ANON_ONLY) {
nr[LRU_GEN_FILE] = 0;
nr[LRU_GEN_ANON] = min_t(u64, total_scan, nr[LRU_GEN_ANON]);
- return;
}

+ if (!nr[LRU_GEN_ANON] || !nr[LRU_GEN_FILE])
+ return;
+
for (int type = LRU_GEN_ANON; type <= LRU_GEN_FILE; type++) {
int hist = lru_hist_from_seq(READ_ONCE(lrugen->min_seq[type]));
/* start at MIN_LRU_BATCH to smooth the rate on gen start */
@@ -5306,6 +5296,8 @@ static void lru_gen_prepare_scan(struct lruvec *lruvec, struct scan_control *sc,
unsigned long total;

lruvec_evictable_size(lruvec, swappiness, nr_to_scan);
+ if (!can_reclaim_anon_pages(memcg, lruvec_pgdat(lruvec)->node_id, sc))
+ nr_to_scan[LRU_GEN_ANON] = 0;
total = nr_to_scan[LRU_GEN_ANON] + nr_to_scan[LRU_GEN_FILE];

/* Just scrape all pages calculated above if this memcg was deleted */
--
2.34.1