* [PATCH v3] mm: vmscan: rework lru_shrink and write_folio tracepoints
@ 2026-09-14 7:24 qiwu.chen(陈启武)
2026-09-14 7:37 ` sashiko-bot
2026-09-14 13:12 ` Matthew Wilcox
0 siblings, 2 replies; 4+ messages in thread
From: qiwu.chen(陈启武) @ 2026-09-14 7:24 UTC (permalink / raw)
To: rostedt@goodmis.org, mhiramat@kernel.org,
akpm@linux-foundation.org, hannes@cmpxchg.org, david@kernel.org,
mhocko@kernel.org, willy@infradead.org
Cc: linux-trace-kernel@vger.kernel.org, linux-mm@kvack.org,
qiwu.chen(陈启武)
From: "qiwu.chen" <qiwu.chen@transsion.com>
Currently, reclaim_flags always contains RECLAIM_WB_ASYNC in lru_shrink
tracepoints since commit 41ac1999c3e35 ("mm: vmscan: do not stall on
writeback during memory compaction"), which is useless for debugging
memory pressure issues. Other RECLAIM_WB_* flags are not used anywhere
else, so they can be directly removed.
This patch reworks the lru_shrink and write_folio tracepoints for better
correlation and analysis:
- traces each folio lru type instead of reclaim_flags.
- traces each lru_shrink with reason.
- remove the printing of the unnecessary PFN for mm_vmscan_write_folio.
- traces the number of activated folios by folio_check_references.
Fixes: 41ac1999c3e35 ("mm: vmscan: do not stall on writeback during memory compaction")
Signed-off-by: qiwu.chen <qiwu.chen@transsion.com>
---
include/linux/vmstat.h | 1 +
include/trace/events/vmscan.h | 77 ++++++++++++++++-------------------
mm/vmscan.c | 11 ++---
3 files changed, 41 insertions(+), 48 deletions(-)
diff --git a/include/linux/vmstat.h b/include/linux/vmstat.h
index 5b31d8e7ae40..72c3b51a633f 100644
--- a/include/linux/vmstat.h
+++ b/include/linux/vmstat.h
@@ -22,6 +22,7 @@ struct reclaim_stat {
unsigned nr_immediate;
unsigned nr_activate[ANON_AND_FILE];
unsigned nr_ref_keep;
+ unsigned nr_ref_activate;
unsigned nr_unmap_fail;
unsigned nr_lazyfree_fail;
unsigned nr_demoted;
diff --git a/include/trace/events/vmscan.h b/include/trace/events/vmscan.h
index b4bf7b8def1f..66fa5bf9a439 100644
--- a/include/trace/events/vmscan.h
+++ b/include/trace/events/vmscan.h
@@ -11,22 +11,6 @@
#include <linux/memcontrol.h>
#include <trace/events/mmflags.h>
-#define RECLAIM_WB_ANON 0x0001u
-#define RECLAIM_WB_FILE 0x0002u
-#define RECLAIM_WB_MIXED 0x0010u
-#define RECLAIM_WB_SYNC 0x0004u /* Unused, all reclaim async */
-#define RECLAIM_WB_ASYNC 0x0008u
-#define RECLAIM_WB_LRU (RECLAIM_WB_ANON|RECLAIM_WB_FILE)
-
-#define show_reclaim_flags(flags) \
- (flags) ? __print_flags(flags, "|", \
- {RECLAIM_WB_ANON, "RECLAIM_WB_ANON"}, \
- {RECLAIM_WB_FILE, "RECLAIM_WB_FILE"}, \
- {RECLAIM_WB_MIXED, "RECLAIM_WB_MIXED"}, \
- {RECLAIM_WB_SYNC, "RECLAIM_WB_SYNC"}, \
- {RECLAIM_WB_ASYNC, "RECLAIM_WB_ASYNC"} \
- ) : "RECLAIM_WB_NONE"
-
#define _VMSCAN_THROTTLE_WRITEBACK (1 << VMSCAN_THROTTLE_WRITEBACK)
#define _VMSCAN_THROTTLE_ISOLATED (1 << VMSCAN_THROTTLE_ISOLATED)
#define _VMSCAN_THROTTLE_NOPROGRESS (1 << VMSCAN_THROTTLE_NOPROGRESS)
@@ -51,10 +35,11 @@ TRACE_DEFINE_ENUM(KSWAPD_CLEAR_HOPELESS_PCP);
{KSWAPD_CLEAR_HOPELESS_PCP, "PCP"}, \
{KSWAPD_CLEAR_HOPELESS_OTHER, "OTHER"}
-#define trace_reclaim_flags(file) ( \
- (file ? RECLAIM_WB_FILE : RECLAIM_WB_ANON) | \
- (RECLAIM_WB_ASYNC) \
- )
+#define trace_reclaim_reason_ops \
+ {PGSTEAL_KSWAPD, "KSWAPD"}, \
+ {PGSTEAL_DIRECT, "DIRECT"}, \
+ {PGSTEAL_KHUGEPAGED, "KHUGEPAGED"}, \
+ {PGSTEAL_PROACTIVE, "PROACTIVE"}
TRACE_EVENT(mm_vmscan_kswapd_sleep,
@@ -413,20 +398,18 @@ TRACE_EVENT(mm_vmscan_write_folio,
TP_ARGS(folio),
TP_STRUCT__entry(
- __field(unsigned long, pfn)
- __field(int, reclaim_flags)
+ __field(struct folio *, folio)
+ __field(int, lru)
),
TP_fast_assign(
- __entry->pfn = folio_pfn(folio);
- __entry->reclaim_flags = trace_reclaim_flags(
- folio_is_file_lru(folio));
+ __entry->folio = folio;
+ __entry->lru = folio_lru_list(folio);
),
- TP_printk("page=%p pfn=0x%lx flags=%s",
- pfn_to_page(__entry->pfn),
- __entry->pfn,
- show_reclaim_flags(__entry->reclaim_flags))
+ TP_printk("folio=%p lru=%s",
+ __entry->folio,
+ __print_symbolic(__entry->lru, LRU_NAMES))
);
TRACE_EVENT(mm_vmscan_reclaim_pages,
@@ -478,9 +461,9 @@ TRACE_EVENT(mm_vmscan_lru_shrink_inactive,
TP_PROTO(int nid,
unsigned long nr_scanned, unsigned long nr_reclaimed,
- struct reclaim_stat *stat, int priority, int file),
+ struct reclaim_stat *stat, int priority, int lru, int reason),
- TP_ARGS(nid, nr_scanned, nr_reclaimed, stat, priority, file),
+ TP_ARGS(nid, nr_scanned, nr_reclaimed, stat, priority, lru, reason),
TP_STRUCT__entry(
__field(int, nid)
@@ -493,9 +476,11 @@ TRACE_EVENT(mm_vmscan_lru_shrink_inactive,
__field(unsigned int, nr_activate0)
__field(unsigned int, nr_activate1)
__field(unsigned long, nr_ref_keep)
+ __field(unsigned long, nr_ref_activate)
__field(unsigned long, nr_unmap_fail)
__field(int, priority)
- __field(int, reclaim_flags)
+ __field(int, lru)
+ __field(int, reason)
),
TP_fast_assign(
@@ -509,29 +494,32 @@ TRACE_EVENT(mm_vmscan_lru_shrink_inactive,
__entry->nr_activate0 = stat->nr_activate[0];
__entry->nr_activate1 = stat->nr_activate[1];
__entry->nr_ref_keep = stat->nr_ref_keep;
+ __entry->nr_ref_activate = stat->nr_ref_activate;
__entry->nr_unmap_fail = stat->nr_unmap_fail;
__entry->priority = priority;
- __entry->reclaim_flags = trace_reclaim_flags(file);
+ __entry->lru = lru;
+ __entry->reason = reason;
),
- TP_printk("nid=%d nr_scanned=%ld nr_reclaimed=%ld nr_dirty=%ld nr_writeback=%ld nr_congested=%ld nr_immediate=%ld nr_activate_anon=%d nr_activate_file=%d nr_ref_keep=%ld nr_unmap_fail=%ld priority=%d flags=%s",
+ TP_printk("nid=%d nr_scanned=%ld nr_reclaimed=%ld nr_dirty=%ld nr_writeback=%ld nr_congested=%ld nr_immediate=%ld nr_activate_anon=%d nr_activate_file=%d nr_ref_keep=%ld nr_ref_activate=%ld nr_unmap_fail=%ld priority=%d lru=%s reason=%s",
__entry->nid,
__entry->nr_scanned, __entry->nr_reclaimed,
__entry->nr_dirty, __entry->nr_writeback,
__entry->nr_congested, __entry->nr_immediate,
__entry->nr_activate0, __entry->nr_activate1,
- __entry->nr_ref_keep, __entry->nr_unmap_fail,
- __entry->priority,
- show_reclaim_flags(__entry->reclaim_flags))
+ __entry->nr_ref_keep, __entry->nr_ref_keep,
+ __entry->nr_unmap_fail, __entry->priority,
+ __print_symbolic(__entry->lru, LRU_NAMES),
+ __print_symbolic(__entry->reason, trace_reclaim_reason_ops))
);
TRACE_EVENT(mm_vmscan_lru_shrink_active,
TP_PROTO(int nid, unsigned long nr_taken,
unsigned long nr_active, unsigned long nr_deactivated,
- unsigned long nr_referenced, int priority, int file),
+ unsigned long nr_referenced, int priority, int lru, int reason),
- TP_ARGS(nid, nr_taken, nr_active, nr_deactivated, nr_referenced, priority, file),
+ TP_ARGS(nid, nr_taken, nr_active, nr_deactivated, nr_referenced, priority, lru, reason),
TP_STRUCT__entry(
__field(int, nid)
@@ -540,7 +528,8 @@ TRACE_EVENT(mm_vmscan_lru_shrink_active,
__field(unsigned long, nr_deactivated)
__field(unsigned long, nr_referenced)
__field(int, priority)
- __field(int, reclaim_flags)
+ __field(int, lru)
+ __field(int, reason)
),
TP_fast_assign(
@@ -550,15 +539,17 @@ TRACE_EVENT(mm_vmscan_lru_shrink_active,
__entry->nr_deactivated = nr_deactivated;
__entry->nr_referenced = nr_referenced;
__entry->priority = priority;
- __entry->reclaim_flags = trace_reclaim_flags(file);
+ __entry->lru = lru;
+ __entry->reason = reason;
),
- TP_printk("nid=%d nr_taken=%ld nr_active=%ld nr_deactivated=%ld nr_referenced=%ld priority=%d flags=%s",
+ TP_printk("nid=%d nr_taken=%ld nr_active=%ld nr_deactivated=%ld nr_referenced=%ld priority=%d lru=%s reason=%s",
__entry->nid,
__entry->nr_taken,
__entry->nr_active, __entry->nr_deactivated, __entry->nr_referenced,
__entry->priority,
- show_reclaim_flags(__entry->reclaim_flags))
+ __print_symbolic(__entry->lru, LRU_NAMES),
+ __print_symbolic(__entry->reason, trace_reclaim_reason_ops))
);
TRACE_EVENT(mm_vmscan_node_reclaim_begin,
diff --git a/mm/vmscan.c b/mm/vmscan.c
index f11491ee9ed5..c90e2dc1d324 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -1233,6 +1233,7 @@ static unsigned int shrink_folio_list(struct list_head *folio_list,
switch (references) {
case FOLIOREF_ACTIVATE:
+ stat->nr_ref_activate += nr_pages;
goto activate_locked;
case FOLIOREF_KEEP:
stat->nr_ref_keep += nr_pages;
@@ -2041,7 +2042,7 @@ static unsigned long shrink_inactive_list(unsigned long nr_to_scan,
handle_reclaim_writeback(nr_taken, pgdat, sc, &stat);
trace_mm_vmscan_lru_shrink_inactive(pgdat->node_id,
- nr_scanned, nr_reclaimed, &stat, sc->priority, file);
+ nr_scanned, nr_reclaimed, &stat, sc->priority, lru, item);
return nr_reclaimed;
}
@@ -2148,7 +2149,8 @@ static void shrink_active_list(unsigned long nr_to_scan,
mod_lruvec_state(lruvec, PGROTATE_ANON + file, nr_rotated);
trace_mm_vmscan_lru_shrink_active(pgdat->node_id, nr_taken, nr_activate,
- nr_deactivate, nr_rotated, sc->priority, file);
+ nr_deactivate, nr_rotated, sc->priority, lru,
+ PGSTEAL_KSWAPD + reclaimer_offset(sc));
}
static unsigned int reclaim_folio_list(struct list_head *folio_list,
@@ -4910,12 +4912,13 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
reclaimed = shrink_folio_list(&list, pgdat, sc, &stat, false, memcg);
sc->nr_reclaimed += reclaimed;
total_reclaimed += reclaimed;
+ item = PGSTEAL_KSWAPD + reclaimer_offset(sc);
/* Retry pass is only meant for clean folios without new isolation */
if (isolated)
handle_reclaim_writeback(isolated, pgdat, sc, &stat);
trace_mm_vmscan_lru_shrink_inactive(pgdat->node_id,
type_scanned, reclaimed, &stat, sc->priority,
- type ? LRU_INACTIVE_FILE : LRU_INACTIVE_ANON);
+ type ? LRU_INACTIVE_FILE : LRU_INACTIVE_ANON, item);
list_for_each_entry_safe_reverse(folio, next, &list, lru) {
DEFINE_MIN_SEQ(lruvec);
@@ -4946,8 +4949,6 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
mod_lruvec_state(lruvec, PGDEMOTE_KSWAPD + reclaimer_offset(sc),
stat.nr_demoted);
-
- item = PGSTEAL_KSWAPD + reclaimer_offset(sc);
mod_lruvec_state(lruvec, item, reclaimed);
mod_lruvec_state(lruvec, PGSTEAL_ANON + type, reclaimed);
--
2.50.1
^ permalink raw reply related [flat|nested] 4+ messages in thread* Re: [PATCH v3] mm: vmscan: rework lru_shrink and write_folio tracepoints
2026-09-14 7:24 [PATCH v3] mm: vmscan: rework lru_shrink and write_folio tracepoints qiwu.chen(陈启武)
@ 2026-09-14 7:37 ` sashiko-bot
2026-09-14 13:12 ` Matthew Wilcox
1 sibling, 0 replies; 4+ messages in thread
From: sashiko-bot @ 2026-09-14 7:37 UTC (permalink / raw)
To: qiwu.chen(������)
Cc: linux-trace-kernel
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Medium] The `mm_vmscan_lru_shrink_inactive` tracepoint prints `nr_ref_keep` twice and omits the newly added `nr_ref_activate` field.
- [Medium] Missing TRACE_DEFINE_ENUM exports for PGSTEAL_* enum values used in trace_reclaim_reason_ops.
- [Medium] The patch breaks the in-tree `trace-vmscan-postprocess.pl` script by altering tracepoint arguments without updating the script.
--
commit 415a9b2c51b867b9381070883253c78fbde68aee
Author: qiwu.chen <qiwu.chen@transsion.com>
mm: vmscan: rework lru_shrink and write_folio tracepoints
This commit reworks the lru_shrink and write_folio tracepoints for better
correlation and analysis. It traces each folio LRU type and reclaim reason,
adds activation statistics, and removes some unused reclaim flags.
> diff --git a/include/trace/events/vmscan.h b/include/trace/events/vmscan.h
> index 8a872990b4bee..db6c6d2d777af 100644
> --- a/include/trace/events/vmscan.h
> +++ b/include/trace/events/vmscan.h
[ ... ]
> -#define trace_reclaim_flags(file) ( \
> - (file ? RECLAIM_WB_FILE : RECLAIM_WB_ANON) | \
> - (RECLAIM_WB_ASYNC) \
> - )
> +#define trace_reclaim_reason_ops \
> + {PGSTEAL_KSWAPD, "KSWAPD"}, \
> + {PGSTEAL_DIRECT, "DIRECT"}, \
> + {PGSTEAL_KHUGEPAGED, "KHUGEPAGED"}, \
> + {PGSTEAL_PROACTIVE, "PROACTIVE"}
[Severity: Medium]
Since this macro maps enums to strings for use with __print_symbolic(), do
these enum values need corresponding TRACE_DEFINE_ENUM() exports?
Without them, userspace tools like perf or trace-cmd will not be able to
evaluate the enum values from the trace format file and will fail to parse
the reason field.
[ ... ]
> @@ -495,29 +480,32 @@ TRACE_EVENT(mm_vmscan_lru_shrink_inactive,
> __entry->nr_activate0 = stat->nr_activate[0];
> __entry->nr_activate1 = stat->nr_activate[1];
> __entry->nr_ref_keep = stat->nr_ref_keep;
> + __entry->nr_ref_activate = stat->nr_ref_activate;
> __entry->nr_unmap_fail = stat->nr_unmap_fail;
> __entry->priority = priority;
> - __entry->reclaim_flags = trace_reclaim_flags(file);
> + __entry->lru = lru;
> + __entry->reason = reason;
> ),
>
> - TP_printk("nid=%d nr_scanned=%ld nr_reclaimed=%ld nr_dirty=%ld nr_writeback=%ld nr_congested=%ld nr_immediate=%ld nr_activate_anon=%d nr_activate_file=%d nr_ref_keep=%ld nr_unmap_fail=%ld priority=%d flags=%s",
> + TP_printk("nid=%d nr_scanned=%ld nr_reclaimed=%ld nr_dirty=%ld nr_writeback=%ld nr_congested=%ld nr_immediate=%ld nr_activate_anon=%d nr_activate_file=%d nr_ref_keep=%ld nr_ref_activate=%ld nr_unmap_fail=%ld priority=%d lru=%s reason=%s",
> __entry->nid,
> __entry->nr_scanned, __entry->nr_reclaimed,
> __entry->nr_dirty, __entry->nr_writeback,
> __entry->nr_congested, __entry->nr_immediate,
> __entry->nr_activate0, __entry->nr_activate1,
> - __entry->nr_ref_keep, __entry->nr_unmap_fail,
> - __entry->priority,
> - show_reclaim_flags(__entry->reclaim_flags))
> + __entry->nr_ref_keep, __entry->nr_ref_keep,
[Severity: Medium]
Is the second __entry->nr_ref_keep here a typo?
It looks like it should be __entry->nr_ref_activate to match the format
string. The duplicated nr_ref_keep argument causes the correct
nr_ref_activate field to be ignored in the trace output.
[Severity: Medium]
Does the Documentation/trace/postprocess/trace-vmscan-postprocess.pl
script also need to be updated to handle this new format?
The script contains a hardcoded regular expression for this tracepoint:
$regex_lru_shrink_inactive = generate_traceevent_regex(
"vmscan/mm_vmscan_lru_shrink_inactive",
$regex_lru_shrink_inactive_default,
"nid", "nr_scanned", "nr_reclaimed", "nr_dirty", "nr_writeback",
"nr_congested", "nr_immediate", "nr_activate_anon",
"nr_activate_file", "nr_ref_keep",
"nr_unmap_fail", "priority", "flags");
Since flags was renamed to lru, and reason and nr_ref_activate were
added, the parsing logic in the script will fail on traces using this new
format.
> + __entry->nr_unmap_fail, __entry->priority,
> + __print_symbolic(__entry->lru, LRU_NAMES),
> + __print_symbolic(__entry->reason, trace_reclaim_reason_ops))
> );
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260914072433.39636-1-qiwu.chen@transsion.com?part=1
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [PATCH v3] mm: vmscan: rework lru_shrink and write_folio tracepoints
2026-09-14 7:24 [PATCH v3] mm: vmscan: rework lru_shrink and write_folio tracepoints qiwu.chen(陈启武)
2026-09-14 7:37 ` sashiko-bot
@ 2026-09-14 13:12 ` Matthew Wilcox
1 sibling, 0 replies; 4+ messages in thread
From: Matthew Wilcox @ 2026-09-14 13:12 UTC (permalink / raw)
To: qiwu.chen(陈启武)
Cc: rostedt@goodmis.org, mhiramat@kernel.org,
akpm@linux-foundation.org, hannes@cmpxchg.org, david@kernel.org,
mhocko@kernel.org, linux-trace-kernel@vger.kernel.org,
linux-mm@kvack.org
On Mon, Sep 14, 2026 at 07:24:37AM +0000, qiwu.chen(陈启武) wrote:
>
> - TP_printk("page=%p pfn=0x%lx flags=%s",
> - pfn_to_page(__entry->pfn),
> - __entry->pfn,
> - show_reclaim_flags(__entry->reclaim_flags))
> + TP_printk("folio=%p lru=%s",
> + __entry->folio,
> + __print_symbolic(__entry->lru, LRU_NAMES))
> );
Why is it useful to print out the folio pointer? Printing out the PFN
made more sense to me.
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v3] mm: vmscan: rework lru_shrink and write_folio tracepoints
@ 2026-10-14 8:16 qiwu.chen(陈启武)
0 siblings, 0 replies; 4+ messages in thread
From: qiwu.chen(陈启武) @ 2026-10-14 8:16 UTC (permalink / raw)
To: Matthew Wilcox
Cc: rostedt@goodmis.org, mhiramat@kernel.org,
akpm@linux-foundation.org, hannes@cmpxchg.org, david@kernel.org,
mhocko@kernel.org, linux-trace-kernel@vger.kernel.org,
linux-mm@kvack.org
>
> - TP_printk("page=%p pfn=0x%lx flags=%s",
> - pfn_to_page(__entry->pfn),
> - __entry->pfn,
> - show_reclaim_flags(__entry->reclaim_flags))
> + TP_printk("folio=%p lru=%s",
> + __entry->folio,
> + __print_symbolic(__entry->lru, LRU_NAMES))
> );
> Why is it useful to print out the folio pointer? Printing out the PFN made more sense to me.
Tracking a folio pointer gives developers an immediate, unambiguous view of which memory object is being scanned or reclaimed
and where it lives in the LRU lists, rather than dealing with page frame number.
As you mentioned in patch v1: https://lore.kernel.org/all/afdBCWopkfF2ow-Z@casper.infradead.org/,
the original code chooses to save the pfn, and then forcibly converts it back to a pointer via pfn_to_page() during printing.
This is an unnecessary indirection that adds code complexity without providing any extra benefit.
Printing out the PFN surely provides a direct view of the physical address information, if pfn is needed, just print it by folio_pfn()
as showed below:
TRACE_EVENT(mm_vmscan_write_folio,
TP_PROTO(struct folio *folio),
TP_ARGS(folio),
TP_STRUCT__entry(
__field(struct folio *, folio)
__field(int, lru)
),
TP_fast_assign(
__entry->folio = folio;
__entry->lru = folio_lru_list(folio);
),
TP_printk("folio=%p pfn==0x%lx lru=%s",
__entry->folio,
folio_pfn(__entry->folio),
__print_symbolic(__entry->lru, LRU_NAMES))
);
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-10-14 8:16 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-14 7:24 [PATCH v3] mm: vmscan: rework lru_shrink and write_folio tracepoints qiwu.chen(陈启武)
2026-09-14 7:37 ` sashiko-bot
2026-09-14 13:12 ` Matthew Wilcox
-- strict thread matches above, loose matches on Subject: below --
2026-10-14 8:16 qiwu.chen(陈启武)
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.