* [PATCH 0/3] mm/mglru: add tracepoints for scan and aging paths
@ 2026-09-11 7:28 Ridong Chen
2026-09-11 7:28 ` [PATCH 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 7:28 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.
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 | 52 ++++++++++++++---
2 files changed, 145 insertions(+), 8 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 1/3] mm/mglru: factor out lru_gen_seq_nr_pages()
2026-09-11 7:28 [PATCH 0/3] mm/mglru: add tracepoints for scan and aging paths Ridong Chen
@ 2026-09-11 7:28 ` Ridong Chen
2026-09-11 7:28 ` [PATCH 2/3] mm/mglru: add tracepoint for scan_folios() Ridong Chen
2026-09-11 7:28 ` [PATCH 3/3] mm/mglru: add tracepoint for inc_max_seq() Ridong Chen
2 siblings, 0 replies; 8+ messages in thread
From: Ridong Chen @ 2026-09-11 7:28 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 2/3] mm/mglru: add tracepoint for scan_folios()
2026-09-11 7:28 [PATCH 0/3] mm/mglru: add tracepoints for scan and aging paths Ridong Chen
2026-09-11 7:28 ` [PATCH 1/3] mm/mglru: factor out lru_gen_seq_nr_pages() Ridong Chen
@ 2026-09-11 7:28 ` Ridong Chen
2026-09-11 14:11 ` Steven Rostedt
2026-09-11 7:28 ` [PATCH 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 7:28 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..c39dfacef033 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(u64 memcg_id,
+ 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(memcg_id, 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 = memcg_id;
+ __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..bd1b9ecf2e84 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(mem_cgroup_id(lruvec_memcg(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 3/3] mm/mglru: add tracepoint for inc_max_seq()
2026-09-11 7:28 [PATCH 0/3] mm/mglru: add tracepoints for scan and aging paths Ridong Chen
2026-09-11 7:28 ` [PATCH 1/3] mm/mglru: factor out lru_gen_seq_nr_pages() Ridong Chen
2026-09-11 7:28 ` [PATCH 2/3] mm/mglru: add tracepoint for scan_folios() Ridong Chen
@ 2026-09-11 7:28 ` Ridong Chen
2026-09-11 14:15 ` Steven Rostedt
2 siblings, 1 reply; 8+ messages in thread
From: Ridong Chen @ 2026-09-11 7:28 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 | 22 ++++++++++++++++++++
2 files changed, 60 insertions(+)
diff --git a/include/trace/events/vmscan.h b/include/trace/events/vmscan.h
index c39dfacef033..9cf3f4564afb 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(u64 memcg_id,
+ unsigned long max_seq,
+ unsigned long anon_min_seq,
+ unsigned long file_min_seq,
+ unsigned long *nr_anon,
+ unsigned long *nr_file),
+
+ TP_ARGS(memcg_id, 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 = memcg_id;
+ __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 bd1b9ecf2e84..d0e59baf5d5c 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -4100,6 +4100,26 @@ 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 = &lruvec->lrugen;
+
+ if (!trace_mm_mglru_inc_max_seq_enabled())
+ return;
+
+ 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(mem_cgroup_id(lruvec_memcg(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 +4179,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 2/3] mm/mglru: add tracepoint for scan_folios()
2026-09-11 7:28 ` [PATCH 2/3] mm/mglru: add tracepoint for scan_folios() Ridong Chen
@ 2026-09-11 14:11 ` Steven Rostedt
2026-09-13 10:25 ` Ridong Chen
0 siblings, 1 reply; 8+ messages in thread
From: Steven Rostedt @ 2026-09-11 14:11 UTC (permalink / raw)
To: Ridong Chen
Cc: Masami Hiramatsu, Andrew Morton, Johannes Weiner,
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
On Fri, 11 Sep 2026 15:28:47 +0800
Ridong Chen <ridong.chen@linux.dev> wrote:
> diff --git a/include/trace/events/vmscan.h b/include/trace/events/vmscan.h
> index 8a872990b4be..c39dfacef033 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(u64 memcg_id,
> + 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(memcg_id, 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)
Please keep "int"s together. This creates a structure that is used to write
into the ring buffer. On 64bit machines, the above would add 4 bytes of
padding after each int, whereas:
__field(unsigned long, max_seq)
__field(unsigned long, min_seq)
__field(int, lru)
__field(int, tier)
would not.
> + ),
> +
> + TP_fast_assign(
> + __entry->memcg_id = memcg_id;
> + __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..bd1b9ecf2e84 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(mem_cgroup_id(lruvec_memcg(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]);
Can't this information be processed in the tracepoint? That is:
TP_PROTO(struct lruvec *lruvec,
struct scan_control *sc,
struct lru_gen_folio *lrugen,
unsigned long nr_requested,
unsigned long nr_scanned,
unsigned long nr_sorted,
unsigned long nr_skipped,
unsigned long nr_taken,
int type),
TP_ARGS(lruvec, sc, lrugen, nr_requested, nr_scanned,
nr_sorted, nr_skipped, nr_taken, type),
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(unsigned long, max_seq)
__field(unsigned long, min_seq)
__field(int, lru)
__field(int, tier)
),
TP_fast_assign(
__entry->memcg_id = mem_cgroup_id(lruvec_memcg(lruvec));
__entry->highest_zoneidx = sc->reclaim_idx;
__entry->order = sc->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 = type ? LRU_INACTIVE_FILE : LRU_INACTIVE_ANON;
__entry->max_seq = lrugen->max_seq;
__entry->tier = tier;
__entry->min_seq = lrugen->min_seq;
),
This moves the code to generate the parameters into the TP_fast_assign()
which is in a separate text section. It remove code from the work flow
improving instruction cache.
Same can be done for that trace_mm_vmscan_lru_isolate() trace event.
-- Steve
>
> *isolatedp = isolated;
> return scanned;
> --
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 3/3] mm/mglru: add tracepoint for inc_max_seq()
2026-09-11 7:28 ` [PATCH 3/3] mm/mglru: add tracepoint for inc_max_seq() Ridong Chen
@ 2026-09-11 14:15 ` Steven Rostedt
2026-09-13 10:28 ` Ridong Chen
0 siblings, 1 reply; 8+ messages in thread
From: Steven Rostedt @ 2026-09-11 14:15 UTC (permalink / raw)
To: Ridong Chen
Cc: Masami Hiramatsu, Andrew Morton, Johannes Weiner,
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
On Fri, 11 Sep 2026 15:28:48 +0800
Ridong Chen <ridong.chen@linux.dev> wrote:
> +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 = &lruvec->lrugen;
> +
> + if (!trace_mm_mglru_inc_max_seq_enabled())
> + return;
> +
> + 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(mem_cgroup_id(lruvec_memcg(lruvec)),
> + lrugen->max_seq,
> + lrugen->min_seq[LRU_GEN_ANON],
> + lrugen->min_seq[LRU_GEN_FILE],
> + nr[LRU_GEN_ANON], nr[LRU_GEN_FILE]);
> +}
I would always look at trying to move as much logic into the
TP_fast_assign() and not have it be where the tracepoint is called.
-- Steve
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/3] mm/mglru: add tracepoint for scan_folios()
2026-09-11 14:11 ` Steven Rostedt
@ 2026-09-13 10:25 ` Ridong Chen
0 siblings, 0 replies; 8+ messages in thread
From: Ridong Chen @ 2026-09-13 10:25 UTC (permalink / raw)
To: Steven Rostedt
Cc: Masami Hiramatsu, Andrew Morton, Johannes Weiner,
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
On 9/11/2026 10:11 PM, Steven Rostedt wrote:
> On Fri, 11 Sep 2026 15:28:47 +0800
> Ridong Chen <ridong.chen@linux.dev> wrote:
>
>> diff --git a/include/trace/events/vmscan.h b/include/trace/events/vmscan.h
>> index 8a872990b4be..c39dfacef033 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(u64 memcg_id,
>> + 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(memcg_id, 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)
>
Thank you very much for your review.
> Please keep "int"s together. This creates a structure that is used to write
> into the ring buffer. On 64bit machines, the above would add 4 bytes of
> padding after each int, whereas:
>
> __field(unsigned long, max_seq)
> __field(unsigned long, min_seq)
> __field(int, lru)
> __field(int, tier)
>
>
> would not.
>
Will upate.
>> + ),
>> +
>> + TP_fast_assign(
>> + __entry->memcg_id = memcg_id;
>> + __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..bd1b9ecf2e84 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(mem_cgroup_id(lruvec_memcg(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]);
>
> Can't this information be processed in the tracepoint? That is:
>
> TP_PROTO(struct lruvec *lruvec,
> struct scan_control *sc,
> struct lru_gen_folio *lrugen,
> unsigned long nr_requested,
> unsigned long nr_scanned,
> unsigned long nr_sorted,
> unsigned long nr_skipped,
> unsigned long nr_taken,
> int type),
>
> TP_ARGS(lruvec, sc, lrugen, nr_requested, nr_scanned,
> nr_sorted, nr_skipped, nr_taken, type),
>
> 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(unsigned long, max_seq)
> __field(unsigned long, min_seq)
> __field(int, lru)
> __field(int, tier)
> ),
>
> TP_fast_assign(
> __entry->memcg_id = mem_cgroup_id(lruvec_memcg(lruvec));
> __entry->highest_zoneidx = sc->reclaim_idx;
> __entry->order = sc->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 = type ? LRU_INACTIVE_FILE : LRU_INACTIVE_ANON;
> __entry->max_seq = lrugen->max_seq;
> __entry->tier = tier;
> __entry->min_seq = lrugen->min_seq;
> ),
>
>
> This moves the code to generate the parameters into the TP_fast_assign()
> which is in a separate text section. It remove code from the work flow
> improving instruction cache.
>
> Same can be done for that trace_mm_vmscan_lru_isolate() trace event.
>
Sashiko has reported the same issue, so I updated my series [1] when I received
Sashiko's report. Thank you again for pointing this out.
[1] https://lore.kernel.org/linux-mm/20260911102939.2485750-3-ridong.chen@linux.dev/
>
>>
>> *isolatedp = isolated;
>> return scanned;
>> --
--
Best regards
Ridong
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 3/3] mm/mglru: add tracepoint for inc_max_seq()
2026-09-11 14:15 ` Steven Rostedt
@ 2026-09-13 10:28 ` Ridong Chen
0 siblings, 0 replies; 8+ messages in thread
From: Ridong Chen @ 2026-09-13 10:28 UTC (permalink / raw)
To: Steven Rostedt
Cc: Masami Hiramatsu, Andrew Morton, Johannes Weiner,
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
On 9/11/2026 10:15 PM, Steven Rostedt wrote:
> On Fri, 11 Sep 2026 15:28:48 +0800
> Ridong Chen <ridong.chen@linux.dev> wrote:
>
>> +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 = &lruvec->lrugen;
>> +
>> + if (!trace_mm_mglru_inc_max_seq_enabled())
>> + return;
>> +
>> + 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(mem_cgroup_id(lruvec_memcg(lruvec)),
>> + lrugen->max_seq,
>> + lrugen->min_seq[LRU_GEN_ANON],
>> + lrugen->min_seq[LRU_GEN_FILE],
>> + nr[LRU_GEN_ANON], nr[LRU_GEN_FILE]);
>> +}
>
> I would always look at trying to move as much logic into the
> TP_fast_assign() and not have it be where the tracepoint is called.
>
> -- Steve
Thanks. Will update.
--
Best regards
Ridong
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-09-13 10:28 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-11 7:28 [PATCH 0/3] mm/mglru: add tracepoints for scan and aging paths Ridong Chen
2026-09-11 7:28 ` [PATCH 1/3] mm/mglru: factor out lru_gen_seq_nr_pages() Ridong Chen
2026-09-11 7:28 ` [PATCH 2/3] mm/mglru: add tracepoint for scan_folios() Ridong Chen
2026-09-11 14:11 ` Steven Rostedt
2026-09-13 10:25 ` Ridong Chen
2026-09-11 7:28 ` [PATCH 3/3] mm/mglru: add tracepoint for inc_max_seq() Ridong Chen
2026-09-11 14:15 ` Steven Rostedt
2026-09-13 10:28 ` Ridong Chen
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®