mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH resend 0/2] mm: vmscan: fix scan overshoot and ineligible folio scanning
@ 2026-09-01  8:47 john
  2026-09-01  8:47 ` [PATCH resend 1/2] mm: vmscan: charge isolate overshoot against scan quota john
                   ` (3 more replies)
  0 siblings, 4 replies; 7+ messages in thread
From: john @ 2026-09-01  8:47 UTC (permalink / raw)
  To: akpm, liuye, hannes, mhocko, david, ljs, hughd, mgorman, yang
  Cc: zhangqiuhao, wangkefeng.wang, mawupeng1, love_goo, linux-mm,
	linux-kernel, Wupeng Ma

From: Wupeng Ma <mawupeng1@huawei.com>

Rebase to the latest v7.3-rc-1.

These problems only surface when reclaim targets a lower zone
while the LRU holds folios from a higher zone.  Normal userspace
allocations go to the highest zone.  The zone-skip branch stays
dead under typical loads.  Lower-zone-pressured configs (DMA32
module allocations, memory-constrained devices) hit the issues.
They inflate scan cost and delay the OOM.

shrink_lruvec() drives reclaim in SWAP_CLUSTER_MAX (32) chunks, but
isolate_lru_folios() may scan far more than that per call on a single
LRU.  The excess is never charged back, so shrink_lruvec() keeps
rescanning the same folios round after round.  When reclaim targets a
lower zone, the same scanner also keeps walking zone-ineligible folios
that can never satisfy the allocation, inflating nr_reclaimed into a
false progress that delays the OOM.

This series fixes both:

  [1/2] Charge the isolate overshoot against the scan quota so the
        next round skips already-scanned folios.
  [2/2] Stop scanning once too many zone-ineligible folios have been
        skipped, instead of force-isolating them.

Background
==========

We observed slow, unexpected OOM behavior during extreme stress testing,
and while digging into the reclaim code during that analysis we spotted
these latent risks in isolate_lru_folios() -- the overshoot never
being charged back, and the force-isolate path on lower-zone reclaim.
The concerns below are the ones surfaced from reading the code, then
confirmed by constructing the situation deliberately.

The problem only appears when reclaim targets a lower zone while the
LRU holds folios from a higher zone:

  - reclaim_idx points at DMA32/DMA (the triggering allocation asked
    for a lower zone, e.g. __GFP_DMA32), and
  - the LRU holds folios from a higher zone (Normal/Movable).

Normal userspace allocations go to the highest zone, so reclaim_idx
never points below it and the zone-skip branch is never taken.  It
needs a real lower-zone allocator to drain that zone below watermark;
that is also why it went unnoticed upstream -- the 1c7b17cf hard-lockup
fix that introduced the force-isolate path was found only on a ~1 TB
box running DMA32 module allocations.

Impact
======

Reproduced on x86 QEMU, 7.2-rc6, with a kernel module doing
__GFP_DMA32 allocations to drain DMA32 below watermark (LRU folios
sit in Movable):

  - A single isolate_lru_folios() call scanned 32794 pages and took
    25 (32769 skipped): 99.9% wasted on ineligible folios.
  - Across one run, isolate was called 339 times, 140-165 of which
    isolated nothing (taken=0, pure empty scans).
  - shrink_lruvec() charges only the 32-page quota per round and
    never refunds the overshoot, so the same skipped folios are
    rescanned round after round.  vmstat on a memcg OOM path:
    pgscan_direct / pgsteal_direct = 2836624 / 112719 = 25.2x
    (isolate -> shrink_folio_list returns the folio -> isolate again).

Once max_nr_skipped hits SWAP_CLUSTER_MAX_SKIPPED, the current code
force-isolates the remaining ineligible folios.  On the inactive LRU
they reach shrink_folio_list() and get reclaimed though they can
never satisfy the allocation, inflating nr_reclaimed and resetting
no_progress_loops in should_reclaim_retry(), delaying the OOM.

A/B results (same .config, md5-identical):

                          baseline    patched
  empty scans (taken=0)   140-165         0
  isolate calls               339         7
  total pages scanned      343711      3140
  single-call max scan      32794      3104

Empty-scan 0 is the stable evidence (holds every run).  The
pgscan/pgsteal ratio is volatile (baseline 56-26675x, patched
65-1324x, ranges overlap) and is not relied on alone.

Caveats and reproduction
========================

  - Triggering needs a lower-zone-pressured box.  To confirm the code
    analysis, the situation was constructed on a small x86 QEMU VM
    (1500M, CONFIG_LRU_GEN=n) with kernel cmdline
    `movable_zone=DMA32 kernelcore=256M` (DMA32 small, Normal empty,
    Movable large), then a kernel module doing
    `alloc_page(GFP_DMA32)` drains DMA32 below watermark.  LRU folios
    sit in Movable and are zone-skipped while reclaiming for DMA32.
    Observed via the `mm_vmscan_lru_isolate` tracepoint and
    /proc/vmstat (pgscan_direct, pgsteal_direct).


Wupeng Ma (2):
  mm: vmscan: charge isolate overshoot against scan quota
  mm: vmscan: stop scanning ineligible folios after max_nr_skipped

 mm/vmscan.c | 48 +++++++++++++++++++++++++++++++++---------------
 1 file changed, 33 insertions(+), 15 deletions(-)

-- 
2.53.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-09-09  9:56 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-01  8:47 [PATCH resend 0/2] mm: vmscan: fix scan overshoot and ineligible folio scanning john
2026-09-01  8:47 ` [PATCH resend 1/2] mm: vmscan: charge isolate overshoot against scan quota john
2026-09-09  9:52   ` Kunwu Chan
2026-09-01  8:47 ` [PATCH resend 2/2] mm: vmscan: stop scanning ineligible folios after max_nr_skipped john
2026-09-09  9:56   ` Kunwu Chan
2026-09-08  1:49 ` [PATCH resend 0/2] mm: vmscan: fix scan overshoot and ineligible folio scanning john
2026-09-09  2:11 ` Andrew Morton

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®