* [PATCH v2 0/3] mm/mglru: add tracepoints for scan and aging paths
@ 2026-09-11 10:29 Ridong Chen
2026-09-11 10:29 ` [PATCH v2 1/3] mm/mglru: factor out lru_gen_seq_nr_pages() Ridong Chen
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: Ridong Chen @ 2026-09-11 10:29 UTC (permalink / raw)
To: Steven Rostedt, Masami Hiramatsu, Andrew Morton, Johannes Weiner
Cc: Mathieu Desnoyers, Kairui Song, Qi Zheng, Shakeel Butt,
Barry Song, Axel Rasmussen, Yuanchu Xie, Wei Xu, Baoquan He,
Baolin Wang, David Hildenbrand, Michal Hocko, Lorenzo Stoakes,
linux-kernel, linux-trace-kernel,
open list:MEMORY MANAGEMENT - MGLRU (MULTI-GEN LRU),
Ridong Chen, Ridong Chen
From: Ridong Chen <chenridong@xiaomi.com>
Background
==========
MGLRU currently has no tracepoints of its own. The scan and evict paths
reuse the classic-LRU tracepoints (trace_mm_vmscan_lru_isolate() and
trace_mm_vmscan_lru_shrink_inactive()), which predate MGLRU and carry no
generation, sequence, memcg or swappiness context. A trace of a running
system therefore cannot tell which memcg a given scan belongs to, how far
reclaim has progressed through the generations, or when a new generation
is created - so how MGLRU actually operates is effectively invisible.
Implementation
==============
Patch 1 is a cleanup that factors the per-generation page-count
summation into a helper, lru_gen_seq_nr_pages(), reused by patch 3.
Patch 2 adds mm_mglru_scan_folios on the scan path, and patch 3 adds
mm_mglru_inc_max_seq on the aging path. Both carry the memcg id and the
generation window (min_seq/max_seq), live at MGLRU-specific layers with
no classic-LRU counterpart, and are guarded so the hot paths stay
zero-cost when disabled.
Effect
======
Paired, the two tracepoints make the full aging-to-eviction window
observable per memcg: aging advances max_seq (the leading edge), scanning
consumes the oldest generations, and the min_seq/max_seq pair on each
event shows how the generation window moves over time.
A sample trace, with the classic-LRU tracepoints left enabled to show
how they interleave:
mm_vmscan_lru_isolate: classzone=4 order=0 nr_requested=36 nr_scanned=31 nr_skipped=0 nr_taken=29 lru=inactive_file
mm_mglru_scan_folios: memcg_id=73 classzone=4 order=0 nr_requested=36 nr_scanned=31 nr_sorted=2 nr_skipped=0 nr_taken=29 lru=inactive_file max_seq=3 tier=3 min_seq=0
mm_vmscan_lru_shrink_inactive: nid=0 nr_scanned=31 nr_reclaimed=29 nr_dirty=0 nr_writeback=0 nr_congested=0 nr_immediate=0 nr_activate_anon=0 nr_activate_file=0 nr_ref_keep=0 nr_unmap_fail=0 priority=5 flags=RECLAIM_WB_FILE|RECLAIM_WB_ASYNC
mm_mglru_inc_max_seq: memcg_id=73 max_seq=4 anon_min_seq=1 file_min_seq=1 nr_anon={0x0,0x0,0x1,0x0} nr_file={0x0,0x427,0x6,0x4a}
mm_vmscan_lru_isolate: classzone=4 order=0 nr_requested=5 nr_scanned=5 nr_skipped=0 nr_taken=1 lru=inactive_file
mm_mglru_scan_folios: memcg_id=73 classzone=4 order=0 nr_requested=5 nr_scanned=5 nr_sorted=4 nr_skipped=0 nr_taken=1 lru=inactive_file max_seq=4 tier=3 min_seq=1
mm_vmscan_lru_shrink_inactive: nid=0 nr_scanned=5 nr_reclaimed=1 nr_dirty=0 nr_writeback=0 nr_congested=0 nr_immediate=0 nr_activate_anon=0 nr_activate_file=0 nr_ref_keep=0 nr_unmap_fail=0 priority=5 flags=RECLAIM_WB_FILE|RECLAIM_WB_ASYNC
The mm_mglru_scan_folios lines carry the memcg id and the generation
window (max_seq/min_seq) that the bare mm_vmscan_lru_isolate lines above
them cannot - those cannot even say which memcg they came from. The
mm_mglru_inc_max_seq line then shows a new generation being created
(max_seq 3 -> 4) with the per-generation page counts for both types.
---
v2: address Sashiko AI review [1] - pass struct lruvec * into the tracepoints
and do the memcg lookup inside TP_fast_assign.
[1] https://sashiko.dev/#/patchset/20260911072848.2346073-1-ridong.chen@linux.dev?part=2
Ridong Chen (3):
mm/mglru: factor out lru_gen_seq_nr_pages()
mm/mglru: add tracepoint for scan_folios()
mm/mglru: add tracepoint for inc_max_seq()
include/trace/events/vmscan.h | 101 ++++++++++++++++++++++++++++++++++
mm/vmscan.c | 53 +++++++++++++++---
2 files changed, 146 insertions(+), 8 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v2 1/3] mm/mglru: factor out lru_gen_seq_nr_pages()
2026-09-11 10:29 [PATCH v2 0/3] mm/mglru: add tracepoints for scan and aging paths Ridong Chen
@ 2026-09-11 10:29 ` Ridong Chen
2026-09-14 7:09 ` Baolin Wang
2026-09-11 10:29 ` [PATCH v2 2/3] mm/mglru: add tracepoint for scan_folios() Ridong Chen
2026-09-11 10:29 ` [PATCH v2 3/3] mm/mglru: add tracepoint for inc_max_seq() Ridong Chen
2 siblings, 1 reply; 8+ messages in thread
From: Ridong Chen @ 2026-09-11 10:29 UTC (permalink / raw)
To: Steven Rostedt, Masami Hiramatsu, Andrew Morton, Johannes Weiner
Cc: Mathieu Desnoyers, Kairui Song, Qi Zheng, Shakeel Butt,
Barry Song, Axel Rasmussen, Yuanchu Xie, Wei Xu, Baoquan He,
Baolin Wang, David Hildenbrand, Michal Hocko, Lorenzo Stoakes,
linux-kernel, linux-trace-kernel,
open list:MEMORY MANAGEMENT - MGLRU (MULTI-GEN LRU),
Ridong Chen, Ridong Chen
From: Ridong Chen <chenridong@xiaomi.com>
Both lruvec_evictable_size() and the debugfs lru_gen_seq_show() compute
the number of pages in a generation the same way: sum lrugen->nr_pages
over all zones for a given (gen, type) and clamp each term to >= 0.
Factor that out into lru_gen_seq_nr_pages() so the open-coded zone loop
lives in one place. No functional change.
A follow-up patch adds a tracepoint that needs the same per-generation
page count, and will reuse this helper instead of open-coding it again.
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
mm/vmscan.c | 24 ++++++++++++++++--------
1 file changed, 16 insertions(+), 8 deletions(-)
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 40d3f1b48a74..2554a6513aa8 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -2813,6 +2813,18 @@ static int get_nr_gens(struct lruvec *lruvec, int type)
return lruvec->lrugen.max_seq - lruvec->lrugen.min_seq[type] + 1;
}
+/* the number of pages in a generation, summed over zones and clamped to >= 0 */
+static unsigned long lru_gen_seq_nr_pages(struct lru_gen_folio *lrugen, int gen, int type)
+{
+ int zone;
+ unsigned long size = 0;
+
+ for (zone = 0; zone < MAX_NR_ZONES; zone++)
+ size += max(READ_ONCE(lrugen->nr_pages[gen][type][zone]), 0L);
+
+ return size;
+}
+
static bool __maybe_unused seq_is_valid(struct lruvec *lruvec)
{
int type;
@@ -4239,7 +4251,7 @@ static void set_initial_priority(struct pglist_data *pgdat, struct scan_control
static unsigned long lruvec_evictable_size(struct lruvec *lruvec, int swappiness)
{
- int gen, type, zone;
+ int gen, type;
unsigned long seq, total = 0;
struct lru_gen_folio *lrugen = &lruvec->lrugen;
DEFINE_MAX_SEQ(lruvec);
@@ -4248,8 +4260,7 @@ static unsigned long lruvec_evictable_size(struct lruvec *lruvec, int swappiness
for_each_evictable_type(type, swappiness) {
for (seq = min_seq[type]; seq <= max_seq; seq++) {
gen = lru_gen_from_seq(seq);
- for (zone = 0; zone < MAX_NR_ZONES; zone++)
- total += max(READ_ONCE(lrugen->nr_pages[gen][type][zone]), 0L);
+ total += lru_gen_seq_nr_pages(lrugen, gen, type);
}
}
@@ -5738,19 +5749,16 @@ static int lru_gen_seq_show(struct seq_file *m, void *v)
seq = 0;
for (; seq <= max_seq; seq++) {
- int type, zone;
+ int type;
int gen = lru_gen_from_seq(seq);
unsigned long birth = READ_ONCE(lruvec->lrugen.timestamps[gen]);
seq_printf(m, " %10lu %10u", seq, jiffies_to_msecs(jiffies - birth));
for (type = 0; type < ANON_AND_FILE; type++) {
- unsigned long size = 0;
+ unsigned long size = lru_gen_seq_nr_pages(lrugen, gen, type);
char mark = full && seq < min_seq[type] ? 'x' : ' ';
- for (zone = 0; zone < MAX_NR_ZONES; zone++)
- size += max(READ_ONCE(lrugen->nr_pages[gen][type][zone]), 0L);
-
seq_printf(m, " %10lu%c", size, mark);
}
--
2.34.1
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v2 2/3] mm/mglru: add tracepoint for scan_folios()
2026-09-11 10:29 [PATCH v2 0/3] mm/mglru: add tracepoints for scan and aging paths Ridong Chen
2026-09-11 10:29 ` [PATCH v2 1/3] mm/mglru: factor out lru_gen_seq_nr_pages() Ridong Chen
@ 2026-09-11 10:29 ` Ridong Chen
2026-09-14 7:47 ` Baolin Wang
2026-09-11 10:29 ` [PATCH v2 3/3] mm/mglru: add tracepoint for inc_max_seq() Ridong Chen
2 siblings, 1 reply; 8+ messages in thread
From: Ridong Chen @ 2026-09-11 10:29 UTC (permalink / raw)
To: Steven Rostedt, Masami Hiramatsu, Andrew Morton, Johannes Weiner
Cc: Mathieu Desnoyers, Kairui Song, Qi Zheng, Shakeel Butt,
Barry Song, Axel Rasmussen, Yuanchu Xie, Wei Xu, Baoquan He,
Baolin Wang, David Hildenbrand, Michal Hocko, Lorenzo Stoakes,
linux-kernel, linux-trace-kernel,
open list:MEMORY MANAGEMENT - MGLRU (MULTI-GEN LRU),
Ridong Chen, Ridong Chen
From: Ridong Chen <chenridong@xiaomi.com>
MGLRU's scan_folios() emits the classic-LRU tracepoint
trace_mm_vmscan_lru_isolate(), which predates MGLRU. It reports the
scan/isolate counts and the LRU type, but carries no generation or
memcg context, so a trace of an MGLRU run cannot tell which memcg a
given scan belongs to, nor how far reclaim has progressed through the
generations.
Add mm_mglru_scan_folios next to it, reporting the same counters plus
nr_sorted (folios moved to a younger generation by sort_folio()) and
the MGLRU context the classic tracepoint lacks: the memcg id, and the
max_seq, tier and min_seq of the type being scanned.
scan_folios() is the MGLRU-specific layer where folios are actually
scanned, so it has no classic-LRU counterpart. That the classic
trace_mm_vmscan_lru_isolate() has lived here stably shows this is a
long-lived place to hook, and the new tracepoint can be enabled on its
own to observe the MGLRU-specific information. The existing tracepoint
is left unchanged.
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
include/trace/events/vmscan.h | 63 +++++++++++++++++++++++++++++++++++
mm/vmscan.c | 6 ++++
2 files changed, 69 insertions(+)
diff --git a/include/trace/events/vmscan.h b/include/trace/events/vmscan.h
index 8a872990b4be..5defa8f6719c 100644
--- a/include/trace/events/vmscan.h
+++ b/include/trace/events/vmscan.h
@@ -392,6 +392,69 @@ TRACE_EVENT(mm_vmscan_lru_isolate,
__print_symbolic(__entry->lru, LRU_NAMES))
);
+TRACE_EVENT(mm_mglru_scan_folios,
+
+ TP_PROTO(struct lruvec *lruvec,
+ int highest_zoneidx,
+ int order,
+ unsigned long nr_requested,
+ unsigned long nr_scanned,
+ unsigned long nr_sorted,
+ unsigned long nr_skipped,
+ unsigned long nr_taken,
+ int lru,
+ unsigned long max_seq,
+ int tier,
+ unsigned long min_seq),
+
+ TP_ARGS(lruvec, highest_zoneidx, order, nr_requested, nr_scanned,
+ nr_sorted, nr_skipped, nr_taken, lru, max_seq, tier, min_seq),
+
+ TP_STRUCT__entry(
+ __field(u64, memcg_id)
+ __field(int, highest_zoneidx)
+ __field(int, order)
+ __field(unsigned long, nr_requested)
+ __field(unsigned long, nr_scanned)
+ __field(unsigned long, nr_sorted)
+ __field(unsigned long, nr_skipped)
+ __field(unsigned long, nr_taken)
+ __field(int, lru)
+ __field(unsigned long, max_seq)
+ __field(int, tier)
+ __field(unsigned long, min_seq)
+ ),
+
+ TP_fast_assign(
+ __entry->memcg_id = mem_cgroup_id(lruvec_memcg(lruvec));
+ __entry->highest_zoneidx = highest_zoneidx;
+ __entry->order = order;
+ __entry->nr_requested = nr_requested;
+ __entry->nr_scanned = nr_scanned;
+ __entry->nr_sorted = nr_sorted;
+ __entry->nr_skipped = nr_skipped;
+ __entry->nr_taken = nr_taken;
+ __entry->lru = lru;
+ __entry->max_seq = max_seq;
+ __entry->tier = tier;
+ __entry->min_seq = min_seq;
+ ),
+
+ TP_printk("memcg_id=%llu classzone=%d order=%d nr_requested=%lu nr_scanned=%lu nr_sorted=%lu nr_skipped=%lu nr_taken=%lu lru=%s max_seq=%lu tier=%d min_seq=%lu",
+ __entry->memcg_id,
+ __entry->highest_zoneidx,
+ __entry->order,
+ __entry->nr_requested,
+ __entry->nr_scanned,
+ __entry->nr_sorted,
+ __entry->nr_skipped,
+ __entry->nr_taken,
+ __print_symbolic(__entry->lru, LRU_NAMES),
+ __entry->max_seq,
+ __entry->tier,
+ __entry->min_seq)
+);
+
TRACE_EVENT(mm_vmscan_write_folio,
TP_PROTO(struct folio *folio),
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 2554a6513aa8..67f59aa73fb9 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -4876,6 +4876,12 @@ static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
trace_mm_vmscan_lru_isolate(sc->reclaim_idx, sc->order, nr_to_scan,
scanned, skipped, isolated,
type ? LRU_INACTIVE_FILE : LRU_INACTIVE_ANON);
+ trace_mm_mglru_scan_folios(lruvec,
+ sc->reclaim_idx, sc->order, nr_to_scan,
+ scanned, sorted, skipped, isolated,
+ type ? LRU_INACTIVE_FILE : LRU_INACTIVE_ANON,
+ lrugen->max_seq, tier,
+ lrugen->min_seq[type]);
*isolatedp = isolated;
return scanned;
--
2.34.1
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v2 3/3] mm/mglru: add tracepoint for inc_max_seq()
2026-09-11 10:29 [PATCH v2 0/3] mm/mglru: add tracepoints for scan and aging paths Ridong Chen
2026-09-11 10:29 ` [PATCH v2 1/3] mm/mglru: factor out lru_gen_seq_nr_pages() Ridong Chen
2026-09-11 10:29 ` [PATCH v2 2/3] mm/mglru: add tracepoint for scan_folios() Ridong Chen
@ 2026-09-11 10:29 ` Ridong Chen
2026-09-14 7:38 ` Baolin Wang
2 siblings, 1 reply; 8+ messages in thread
From: Ridong Chen @ 2026-09-11 10:29 UTC (permalink / raw)
To: Steven Rostedt, Masami Hiramatsu, Andrew Morton, Johannes Weiner
Cc: Mathieu Desnoyers, Kairui Song, Qi Zheng, Shakeel Butt,
Barry Song, Axel Rasmussen, Yuanchu Xie, Wei Xu, Baoquan He,
Baolin Wang, David Hildenbrand, Michal Hocko, Lorenzo Stoakes,
linux-kernel, linux-trace-kernel,
open list:MEMORY MANAGEMENT - MGLRU (MULTI-GEN LRU),
Ridong Chen, Ridong Chen
From: Ridong Chen <chenridong@xiaomi.com>
Aging in MGLRU advances max_seq via inc_max_seq(), creating a new
youngest generation. There is currently no tracepoint on this path, so
the moment a new generation is created, and how the min_seq of each type
trails behind it, cannot be observed as it happens.
Add mm_mglru_inc_max_seq, emitted right after max_seq is bumped, with
the memcg id, the new max_seq, the anon and file min_seq, and the number
of pages in each generation for both types, summed over zones the same
way the debugfs lru_gen file reports them. The nr_anon/nr_file arrays are
indexed by generation slot, so the emitted max_seq/min_seq say which slot
holds which seq. They are printed with __print_array(), so the output is
hex and its length follows MAX_NR_GENS automatically. Paired with the
mm_mglru_scan_folios tracepoint it makes the full aging-to-eviction
window observable per memcg.
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
include/trace/events/vmscan.h | 38 +++++++++++++++++++++++++++++++++++
mm/vmscan.c | 23 +++++++++++++++++++++
2 files changed, 61 insertions(+)
diff --git a/include/trace/events/vmscan.h b/include/trace/events/vmscan.h
index 5defa8f6719c..a20c6b2e062f 100644
--- a/include/trace/events/vmscan.h
+++ b/include/trace/events/vmscan.h
@@ -455,6 +455,44 @@ TRACE_EVENT(mm_mglru_scan_folios,
__entry->min_seq)
);
+TRACE_EVENT(mm_mglru_inc_max_seq,
+
+ TP_PROTO(struct lruvec *lruvec,
+ unsigned long max_seq,
+ unsigned long anon_min_seq,
+ unsigned long file_min_seq,
+ unsigned long *nr_anon,
+ unsigned long *nr_file),
+
+ TP_ARGS(lruvec, max_seq, anon_min_seq, file_min_seq, nr_anon, nr_file),
+
+ TP_STRUCT__entry(
+ __field(u64, memcg_id)
+ __field(unsigned long, max_seq)
+ __field(unsigned long, anon_min_seq)
+ __field(unsigned long, file_min_seq)
+ __array(unsigned long, nr_anon, MAX_NR_GENS)
+ __array(unsigned long, nr_file, MAX_NR_GENS)
+ ),
+
+ TP_fast_assign(
+ __entry->memcg_id = mem_cgroup_id(lruvec_memcg(lruvec));
+ __entry->max_seq = max_seq;
+ __entry->anon_min_seq = anon_min_seq;
+ __entry->file_min_seq = file_min_seq;
+ memcpy(__entry->nr_anon, nr_anon, sizeof(__entry->nr_anon));
+ memcpy(__entry->nr_file, nr_file, sizeof(__entry->nr_file));
+ ),
+
+ TP_printk("memcg_id=%llu max_seq=%lu anon_min_seq=%lu file_min_seq=%lu nr_anon=%s nr_file=%s",
+ __entry->memcg_id,
+ __entry->max_seq,
+ __entry->anon_min_seq,
+ __entry->file_min_seq,
+ __print_array(__entry->nr_anon, MAX_NR_GENS, sizeof(unsigned long)),
+ __print_array(__entry->nr_file, MAX_NR_GENS, sizeof(unsigned long)))
+);
+
TRACE_EVENT(mm_vmscan_write_folio,
TP_PROTO(struct folio *folio),
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 67f59aa73fb9..d1495a7d469d 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -4100,6 +4100,27 @@ static void try_to_inc_min_seq(struct lruvec *lruvec, int swappiness)
}
}
+static void trace_inc_max_seq(struct lruvec *lruvec)
+{
+ int type, gen;
+ unsigned long nr[ANON_AND_FILE][MAX_NR_GENS];
+ struct lru_gen_folio *lrugen;
+
+ if (!trace_mm_mglru_inc_max_seq_enabled())
+ return;
+
+ lrugen = &lruvec->lrugen;
+ for (type = 0; type < ANON_AND_FILE; type++)
+ for (gen = 0; gen < MAX_NR_GENS; gen++)
+ nr[type][gen] = lru_gen_seq_nr_pages(lrugen, gen, type);
+
+ trace_mm_mglru_inc_max_seq(lruvec,
+ lrugen->max_seq,
+ lrugen->min_seq[LRU_GEN_ANON],
+ lrugen->min_seq[LRU_GEN_FILE],
+ nr[LRU_GEN_ANON], nr[LRU_GEN_FILE]);
+}
+
static bool inc_max_seq(struct lruvec *lruvec, unsigned long seq, int swappiness)
{
bool success;
@@ -4159,6 +4180,8 @@ static bool inc_max_seq(struct lruvec *lruvec, unsigned long seq, int swappiness
WRITE_ONCE(lrugen->timestamps[next], jiffies);
/* make sure preceding modifications appear */
smp_store_release(&lrugen->max_seq, lrugen->max_seq + 1);
+
+ trace_inc_max_seq(lruvec);
unlock:
lruvec_unlock_irq(lruvec);
--
2.34.1
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2 1/3] mm/mglru: factor out lru_gen_seq_nr_pages()
2026-09-11 10:29 ` [PATCH v2 1/3] mm/mglru: factor out lru_gen_seq_nr_pages() Ridong Chen
@ 2026-09-14 7:09 ` Baolin Wang
0 siblings, 0 replies; 8+ messages in thread
From: Baolin Wang @ 2026-09-14 7:09 UTC (permalink / raw)
To: Ridong Chen, Steven Rostedt, Masami Hiramatsu, Andrew Morton,
Johannes Weiner
Cc: Mathieu Desnoyers, Kairui Song, Qi Zheng, Shakeel Butt,
Barry Song, Axel Rasmussen, Yuanchu Xie, Wei Xu, Baoquan He,
David Hildenbrand, Michal Hocko, Lorenzo Stoakes, linux-kernel,
linux-trace-kernel,
open list:MEMORY MANAGEMENT - MGLRU (MULTI-GEN LRU),
Ridong Chen
On 9/11/26 6:29 PM, Ridong Chen wrote:
> From: Ridong Chen <chenridong@xiaomi.com>
>
> Both lruvec_evictable_size() and the debugfs lru_gen_seq_show() compute
> the number of pages in a generation the same way: sum lrugen->nr_pages
> over all zones for a given (gen, type) and clamp each term to >= 0.
>
> Factor that out into lru_gen_seq_nr_pages() so the open-coded zone loop
> lives in one place. No functional change.
>
> A follow-up patch adds a tracepoint that needs the same per-generation
> page count, and will reuse this helper instead of open-coding it again.
>
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
> ---
LGTM.
Reviewed-by: Baolin Wang <baolin.wang@linux.alibaba.com>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2 3/3] mm/mglru: add tracepoint for inc_max_seq()
2026-09-11 10:29 ` [PATCH v2 3/3] mm/mglru: add tracepoint for inc_max_seq() Ridong Chen
@ 2026-09-14 7:38 ` Baolin Wang
0 siblings, 0 replies; 8+ messages in thread
From: Baolin Wang @ 2026-09-14 7:38 UTC (permalink / raw)
To: Ridong Chen, Steven Rostedt, Masami Hiramatsu, Andrew Morton,
Johannes Weiner
Cc: Mathieu Desnoyers, Kairui Song, Qi Zheng, Shakeel Butt,
Barry Song, Axel Rasmussen, Yuanchu Xie, Wei Xu, Baoquan He,
David Hildenbrand, Michal Hocko, Lorenzo Stoakes, linux-kernel,
linux-trace-kernel,
open list:MEMORY MANAGEMENT - MGLRU (MULTI-GEN LRU),
Ridong Chen
On 9/11/26 6:29 PM, Ridong Chen wrote:
> From: Ridong Chen <chenridong@xiaomi.com>
>
> Aging in MGLRU advances max_seq via inc_max_seq(), creating a new
> youngest generation. There is currently no tracepoint on this path, so
> the moment a new generation is created, and how the min_seq of each type
> trails behind it, cannot be observed as it happens.
>
> Add mm_mglru_inc_max_seq, emitted right after max_seq is bumped, with
> the memcg id, the new max_seq, the anon and file min_seq, and the number
> of pages in each generation for both types, summed over zones the same
> way the debugfs lru_gen file reports them. The nr_anon/nr_file arrays are
> indexed by generation slot, so the emitted max_seq/min_seq say which slot
> holds which seq. They are printed with __print_array(), so the output is
> hex and its length follows MAX_NR_GENS automatically. Paired with the
> mm_mglru_scan_folios tracepoint it makes the full aging-to-eviction
> window observable per memcg.
>
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
> ---
Make sense to me.
Reviewed-by: Baolin Wang <baolin.wang@linux.alibaba.com>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2 2/3] mm/mglru: add tracepoint for scan_folios()
2026-09-11 10:29 ` [PATCH v2 2/3] mm/mglru: add tracepoint for scan_folios() Ridong Chen
@ 2026-09-14 7:47 ` Baolin Wang
2026-09-14 9:04 ` Ridong Chen
0 siblings, 1 reply; 8+ messages in thread
From: Baolin Wang @ 2026-09-14 7:47 UTC (permalink / raw)
To: Ridong Chen, Steven Rostedt, Masami Hiramatsu, Andrew Morton,
Johannes Weiner
Cc: Mathieu Desnoyers, Kairui Song, Qi Zheng, Shakeel Butt,
Barry Song, Axel Rasmussen, Yuanchu Xie, Wei Xu, Baoquan He,
David Hildenbrand, Michal Hocko, Lorenzo Stoakes, linux-kernel,
linux-trace-kernel,
open list:MEMORY MANAGEMENT - MGLRU (MULTI-GEN LRU),
Ridong Chen
On 9/11/26 6:29 PM, Ridong Chen wrote:
> From: Ridong Chen <chenridong@xiaomi.com>
>
> MGLRU's scan_folios() emits the classic-LRU tracepoint
> trace_mm_vmscan_lru_isolate(), which predates MGLRU. It reports the
> scan/isolate counts and the LRU type, but carries no generation or
> memcg context, so a trace of an MGLRU run cannot tell which memcg a
> given scan belongs to, nor how far reclaim has progressed through the
> generations.
>
> Add mm_mglru_scan_folios next to it, reporting the same counters plus
> nr_sorted (folios moved to a younger generation by sort_folio()) and
> the MGLRU context the classic tracepoint lacks: the memcg id, and the
> max_seq, tier and min_seq of the type being scanned.
>
> scan_folios() is the MGLRU-specific layer where folios are actually
> scanned, so it has no classic-LRU counterpart. That the classic
> trace_mm_vmscan_lru_isolate() has lived here stably shows this is a
> long-lived place to hook, and the new tracepoint can be enabled on its
> own to observe the MGLRU-specific information. The existing tracepoint
> is left unchanged.
>
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
> ---
> include/trace/events/vmscan.h | 63 +++++++++++++++++++++++++++++++++++
> mm/vmscan.c | 6 ++++
> 2 files changed, 69 insertions(+)
>
> diff --git a/include/trace/events/vmscan.h b/include/trace/events/vmscan.h
> index 8a872990b4be..5defa8f6719c 100644
> --- a/include/trace/events/vmscan.h
> +++ b/include/trace/events/vmscan.h
> @@ -392,6 +392,69 @@ TRACE_EVENT(mm_vmscan_lru_isolate,
> __print_symbolic(__entry->lru, LRU_NAMES))
> );
>
[snip]
> diff --git a/mm/vmscan.c b/mm/vmscan.c
> index 2554a6513aa8..67f59aa73fb9 100644
> --- a/mm/vmscan.c
> +++ b/mm/vmscan.c
> @@ -4876,6 +4876,12 @@ static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
> trace_mm_vmscan_lru_isolate(sc->reclaim_idx, sc->order, nr_to_scan,
> scanned, skipped, isolated,
> type ? LRU_INACTIVE_FILE : LRU_INACTIVE_ANON);
> + trace_mm_mglru_scan_folios(lruvec,
> + sc->reclaim_idx, sc->order, nr_to_scan,
> + scanned, sorted, skipped, isolated,
> + type ? LRU_INACTIVE_FILE : LRU_INACTIVE_ANON,
> + lrugen->max_seq, tier,
> + lrugen->min_seq[type]);
Both tracepoints will print some duplicated content, and I'm not sure
it's worth a new tracepoint just to trace max_seq and min_seq.
Anyway, I'm not against this patch, but I'd like to hear others' opinions.
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2 2/3] mm/mglru: add tracepoint for scan_folios()
2026-09-14 7:47 ` Baolin Wang
@ 2026-09-14 9:04 ` Ridong Chen
0 siblings, 0 replies; 8+ messages in thread
From: Ridong Chen @ 2026-09-14 9:04 UTC (permalink / raw)
To: Baolin Wang, Steven Rostedt, Masami Hiramatsu, Andrew Morton,
Johannes Weiner
Cc: Mathieu Desnoyers, Kairui Song, Qi Zheng, Shakeel Butt,
Barry Song, Axel Rasmussen, Yuanchu Xie, Wei Xu, Baoquan He,
David Hildenbrand, Michal Hocko, Lorenzo Stoakes, linux-kernel,
linux-trace-kernel,
open list:MEMORY MANAGEMENT - MGLRU (MULTI-GEN LRU),
Ridong Chen
On 9/14/2026 3:47 PM, Baolin Wang wrote:
>
>
> On 9/11/26 6:29 PM, Ridong Chen wrote:
>> From: Ridong Chen <chenridong@xiaomi.com>
>>
>> MGLRU's scan_folios() emits the classic-LRU tracepoint
>> trace_mm_vmscan_lru_isolate(), which predates MGLRU. It reports the
>> scan/isolate counts and the LRU type, but carries no generation or
>> memcg context, so a trace of an MGLRU run cannot tell which memcg a
>> given scan belongs to, nor how far reclaim has progressed through the
>> generations.
>>
>> Add mm_mglru_scan_folios next to it, reporting the same counters plus
>> nr_sorted (folios moved to a younger generation by sort_folio()) and
>> the MGLRU context the classic tracepoint lacks: the memcg id, and the
>> max_seq, tier and min_seq of the type being scanned.
>>
>> scan_folios() is the MGLRU-specific layer where folios are actually
>> scanned, so it has no classic-LRU counterpart. That the classic
>> trace_mm_vmscan_lru_isolate() has lived here stably shows this is a
>> long-lived place to hook, and the new tracepoint can be enabled on its
>> own to observe the MGLRU-specific information. The existing tracepoint
>> is left unchanged.
>>
>> Assisted-by: Claude:claude-opus-4-8
>> Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
>> ---
>> include/trace/events/vmscan.h | 63 +++++++++++++++++++++++++++++++++++
>> mm/vmscan.c | 6 ++++
>> 2 files changed, 69 insertions(+)
>>
>> diff --git a/include/trace/events/vmscan.h b/include/trace/events/vmscan.h
>> index 8a872990b4be..5defa8f6719c 100644
>> --- a/include/trace/events/vmscan.h
>> +++ b/include/trace/events/vmscan.h
>> @@ -392,6 +392,69 @@ TRACE_EVENT(mm_vmscan_lru_isolate,
>> __print_symbolic(__entry->lru, LRU_NAMES))
>> );
>
> [snip]
>
>> diff --git a/mm/vmscan.c b/mm/vmscan.c
>> index 2554a6513aa8..67f59aa73fb9 100644
>> --- a/mm/vmscan.c
>> +++ b/mm/vmscan.c
>> @@ -4876,6 +4876,12 @@ static int scan_folios(unsigned long nr_to_scan, struct
>> lruvec *lruvec,
>> trace_mm_vmscan_lru_isolate(sc->reclaim_idx, sc->order, nr_to_scan,
>> scanned, skipped, isolated,
>> type ? LRU_INACTIVE_FILE : LRU_INACTIVE_ANON);
>> + trace_mm_mglru_scan_folios(lruvec,
>> + sc->reclaim_idx, sc->order, nr_to_scan,
>> + scanned, sorted, skipped, isolated,
>> + type ? LRU_INACTIVE_FILE : LRU_INACTIVE_ANON,
>> + lrugen->max_seq, tier,
>> + lrugen->min_seq[type]);
>
> Both tracepoints will print some duplicated content, and I'm not sure it's worth
> a new tracepoint just to trace max_seq and min_seq.
>
> Anyway, I'm not against this patch, but I'd like to hear others' opinions.
Thank you for your review.
Since the lruvec is per memcg and per node, we would like to know how the memcg
scans and reclaims its lruvec.
I am also concerned that the content may be duplicated where we place it here.
However, with Barry's suggestion, it is stable here. What is more, the trace is
under the control of the trace event gate. It is convenient to control and would
not lead to any regression when they are disabled.
As my cover letter mentions, with the current trace events, we don't know when
the per-memcg lruvec ages, when it is scanned and reclaimed, or with what
seq(gen). With this series, we can tell the memcg aging and reclaim actions.
--
Best regards
Ridong
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-09-14 9:04 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-11 10:29 [PATCH v2 0/3] mm/mglru: add tracepoints for scan and aging paths Ridong Chen
2026-09-11 10:29 ` [PATCH v2 1/3] mm/mglru: factor out lru_gen_seq_nr_pages() Ridong Chen
2026-09-14 7:09 ` Baolin Wang
2026-09-11 10:29 ` [PATCH v2 2/3] mm/mglru: add tracepoint for scan_folios() Ridong Chen
2026-09-14 7:47 ` Baolin Wang
2026-09-14 9:04 ` Ridong Chen
2026-09-11 10:29 ` [PATCH v2 3/3] mm/mglru: add tracepoint for inc_max_seq() Ridong Chen
2026-09-14 7:38 ` Baolin Wang
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®