[PATCH 5/5] mm/mglru: reschedule between memcg reclaim attempts

From: Max Kellermann

Date: Mon Oct 12 2026 - 16:55:37 EST


shrink_one() can return immediately for empty or protected memcgs, and
try_to_shrink_lruvec() can break out before reaching its
cond_resched(). A scan through many memcgs can repeatedly rotate
entries without giving other runnable tasks a scheduling opportunity.

Call cond_resched_tasks_rcu_qs() (as the existing shrink_lruvec() loop
does) after shrink_one(), while the memcg reference still pins the
cursor and before reacquiring the RCU read lock. Also call it between
bins and nulls-marker retries, covering scans that skip every entry.

Signed-off-by: Max Kellermann <max.kellermann@xxxxxxxxx>
---
mm/vmscan.c | 11 +++++++++++
1 file changed, 11 insertions(+)

diff --git a/mm/vmscan.c b/mm/vmscan.c
index 922c59eb9629..c9d53546dc10 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -5194,6 +5194,9 @@ static void shrink_many(struct pglist_data *pgdat, struct scan_control *sc)
gen = get_memcg_gen(READ_ONCE(pgdat->memcg_lru.seq));
bin = first_bin = get_random_u32_below(MEMCG_NR_BINS);
restart:
+ /* Yield between bins/retries that contain only skipped entries. */
+ cond_resched_tasks_rcu_qs();
+
op = 0;
memcg = NULL;

@@ -5224,6 +5227,14 @@ static void shrink_many(struct pglist_data *pgdat, struct scan_control *sc)

op = shrink_one(lruvec, sc);

+ /*
+ * Empty or protected memcgs can bypass the
+ * rescheduling points in page reclaim. The memcg
+ * reference pins our cursor while we yield outside
+ * the RCU read-side critical section.
+ */
+ cond_resched_tasks_rcu_qs();
+
rcu_read_lock();

if (should_abort_scan(lruvec, sc))
--
2.47.3