Linux-mm Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Wupeng Ma <mawupeng1@huawei.com>
To: <akpm@linux-foundation.org>, <liuye@kylinos.cn>,
	<hannes@cmpxchg.org>, <mhocko@kernel.org>, <david@kernel.org>,
	<ljs@kernel.org>, <hughd@google.com>,
	<mgorman@techsingularity.net>, <yang@os.amperecomputing.com>
Cc: <zhangqiuhao@huawei.com>, <wangkefeng.wang@huawei.com>,
	<linux-mm@kvack.org>, <linux-kernel@vger.kernel.org>,
	Wupeng Ma <mawupeng1@huawei.com>
Subject: [PATCH 1/2] mm: vmscan: charge isolate overshoot against scan quota
Date: Tue, 11 Aug 2026 15:08:48 +0800	[thread overview]
Message-ID: <20260811070849.1332165-2-mawupeng1@huawei.com> (raw)
In-Reply-To: <20260811070849.1332165-1-mawupeng1@huawei.com>

shrink_lruvec() charges the per-LRU budget nr[lru] in SWAP_CLUSTER_MAX
(32) chunks, but isolate_lru_folios() may scan far more per call: a
large folio can jump scan by many pages at once (a PMD-sized folio
counts 512), and a zone-ineligible LRU walks the whole list without
feeding scan back.  The overshoot is never refunded, so shrink_lruvec()
keeps charging only 32 per round and rescans the same folios.

Have isolate_lru_folios() record its scanned count in sc->nr_isolate_scanned
and let shrink_lruvec() subtract the overshoot from the remaining quota so
the next round skips already-scanned folios.  The field is reset to 0
before each shrink_list() call, as shrink_list() only reaches
isolate_lru_folios() on some paths (active + skipped_deactivate,
too_many_isolated stall bail out early); a stale value would otherwise
be charged.  The budget floor stays nr_to_scan via max() so an empty
LRU (sc->nr_isolate_scanned = 0) still advances and cannot deadlock.

Co-developed-by: Qiuhao Zhang <zhangqiuhao@huawei.com>
Signed-off-by: Qiuhao Zhang <zhangqiuhao@huawei.com>
Signed-off-by: Wupeng Ma <mawupeng1@huawei.com>
---
 mm/vmscan.c | 32 +++++++++++++++++++++-----------
 1 file changed, 21 insertions(+), 11 deletions(-)

diff --git a/mm/vmscan.c b/mm/vmscan.c
index 56708d1d2dfd5..147e74f9732d5 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -164,6 +164,9 @@ struct scan_control {
 	/* Incremented by the number of inactive pages that were scanned */
 	unsigned long nr_scanned;
 
+	/* Number of pages that were scanned from isolate_lru_folios() */
+	unsigned long nr_isolate_scanned;
+
 	/* Number of pages freed so far during a call to shrink_zones() */
 	unsigned long nr_reclaimed;
 
@@ -1673,7 +1676,6 @@ static __always_inline void update_lru_sizes(struct lruvec *lruvec,
  * @nr_to_scan:	The number of eligible pages to look through on the list.
  * @lruvec:	The LRU vector to pull pages from.
  * @dst:	The temp list to put pages on to.
- * @nr_scanned:	The number of pages that were scanned.
  * @sc:		The scan_control struct for this reclaim session
  * @lru:	LRU list id for isolating
  *
@@ -1681,8 +1683,7 @@ static __always_inline void update_lru_sizes(struct lruvec *lruvec,
  */
 static unsigned long isolate_lru_folios(unsigned long nr_to_scan,
 		struct lruvec *lruvec, struct list_head *dst,
-		unsigned long *nr_scanned, struct scan_control *sc,
-		enum lru_list lru)
+		struct scan_control *sc, enum lru_list lru)
 {
 	struct list_head *src = &lruvec->lists[lru];
 	unsigned long nr_taken = 0;
@@ -1766,7 +1767,7 @@ static unsigned long isolate_lru_folios(unsigned long nr_to_scan,
 			skipped += nr_skipped[zid];
 		}
 	}
-	*nr_scanned = total_scan;
+	sc->nr_isolate_scanned = total_scan;
 	trace_mm_vmscan_lru_isolate(sc->reclaim_idx, sc->order, nr_to_scan,
 				    total_scan, skipped, nr_taken, lru);
 	update_lru_sizes(lruvec, lru, nr_zone_taken);
@@ -2014,8 +2015,8 @@ static unsigned long shrink_inactive_list(unsigned long nr_to_scan,
 
 	lruvec_lock_irq(lruvec);
 
-	nr_taken = isolate_lru_folios(nr_to_scan, lruvec, &folio_list,
-				     &nr_scanned, sc, lru);
+	nr_taken = isolate_lru_folios(nr_to_scan, lruvec, &folio_list, sc, lru);
+	nr_scanned = sc->nr_isolate_scanned;
 
 	__mod_node_page_state(pgdat, NR_ISOLATED_ANON + file, nr_taken);
 	item = PGSCAN_KSWAPD + reclaimer_offset(sc);
@@ -2071,7 +2072,6 @@ static void shrink_active_list(unsigned long nr_to_scan,
 			       enum lru_list lru)
 {
 	unsigned long nr_taken;
-	unsigned long nr_scanned;
 	vm_flags_t vm_flags;
 	LIST_HEAD(l_hold);	/* The folios which were snipped off */
 	LIST_HEAD(l_active);
@@ -2085,12 +2085,11 @@ static void shrink_active_list(unsigned long nr_to_scan,
 
 	lruvec_lock_irq(lruvec);
 
-	nr_taken = isolate_lru_folios(nr_to_scan, lruvec, &l_hold,
-				     &nr_scanned, sc, lru);
+	nr_taken = isolate_lru_folios(nr_to_scan, lruvec, &l_hold, sc, lru);
 
 	__mod_node_page_state(pgdat, NR_ISOLATED_ANON + file, nr_taken);
 
-	mod_lruvec_state(lruvec, PGREFILL, nr_scanned);
+	mod_lruvec_state(lruvec, PGREFILL, sc->nr_isolate_scanned);
 
 	lruvec_unlock_irq(lruvec);
 
@@ -5920,10 +5919,21 @@ static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
 		for_each_evictable_lru(lru) {
 			if (nr[lru]) {
 				nr_to_scan = min(nr[lru], SWAP_CLUSTER_MAX);
-				nr[lru] -= nr_to_scan;
 
+				sc->nr_isolate_scanned = 0;
 				nr_reclaimed += shrink_list(lru, nr_to_scan,
 							    lruvec, sc);
+				/*
+				 * isolate_lru_folios() may scan far more
+				 * than nr_to_scan when the LRU holds
+				 * ineligible folios (zone-skip) or large
+				 * folios. Charge that overshoot against the
+				 * remaining quota (clamped by min() so it
+				 * cannot go negative) so the next iteration
+				 * does not rescan the same skipped folios.
+				 */
+				nr[lru] -= min(nr[lru],
+					       max(nr_to_scan, sc->nr_isolate_scanned));
 			}
 		}
 
-- 
2.43.0



  reply	other threads:[~2026-08-11  7:35 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-11  7:08 [PATCH 0/2] mm: vmscan: fix scan overshoot and ineligible folio scanning Wupeng Ma
2026-08-11  7:08 ` Wupeng Ma [this message]
2026-08-11  7:08 ` [PATCH 2/2] mm: vmscan: stop scanning ineligible folios after max_nr_skipped Wupeng Ma
2026-08-12  1:42 ` [PATCH 0/2] mm: vmscan: fix scan overshoot and ineligible folio scanning Andrew Morton
2026-08-12  2:29   ` mawupeng
2026-08-25  0:59     ` mawupeng
2026-08-30  3:18       ` Andrew Morton
2026-09-01  3:12         ` mawupeng

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260811070849.1332165-2-mawupeng1@huawei.com \
    --to=mawupeng1@huawei.com \
    --cc=akpm@linux-foundation.org \
    --cc=david@kernel.org \
    --cc=hannes@cmpxchg.org \
    --cc=hughd@google.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=liuye@kylinos.cn \
    --cc=ljs@kernel.org \
    --cc=mgorman@techsingularity.net \
    --cc=mhocko@kernel.org \
    --cc=wangkefeng.wang@huawei.com \
    --cc=yang@os.amperecomputing.com \
    --cc=zhangqiuhao@huawei.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox