mm/vmscan: move tracepoints to mm/ and add MGLRU tracepoints
Subsystems (8)
v5 cover mm/vmscan: move tracepoints to mm/ and add MGLRU tracepoints
From: Ridong Chen <[email protected]>
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 or memcg 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.
While reviewing an earlier version that added two MGLRU tracepoints, Steven suggested moving include/trace/events/vmscan.h to mm/trace_vmscan.h [1], as several other subsystems already do. Once the header lives under mm/, struct scan_control can be moved there too, letting the trace events access its fields directly instead of having each callsite copy the scalars out by hand. This version merges the two efforts into one series: the restructuring first, then the two new tracepoints built on top of it.
Implementation
==============
Part 1, restructuring (patches 1-4):
1) drop the unused vmscan trace include from memcontrol.c;
2) move include/trace/events/vmscan.h to mm/trace_vmscan.h, setting
TRACE_INCLUDE_PATH to "." and adding -I$(src) for vmscan.o;
3) move struct scan_control into a dedicated mm/vmscan.h with an
ordinary include guard, which trace_vmscan.h pulls in; 4) pass scan_control to the vmscan tracepoints and pick the fields out in TP_fast_assign: the reclaim-begin events, the LRU isolate/shrink events, mm_vmscan_reclaim_pages and mm_vmscan_balance_pgdat_end.
Part 2, MGLRU tracepoints (patches 5-6):
5) add mm_mglru_scan_folios on the scan path, next to the classic mm_vmscan_lru_isolate, reporting the same counters plus nr_sorted and the nid, memcg id, max_seq, tier and min_seq of the lruvec;
6) add mm_mglru_inc_max_seq on the aging path, reporting the nid, memcg id, the new max_seq, the anon/file min_seq and the per-generation page counts of both types.
Both new events only take pointers (lruvec, sc) plus the local counters, and derive everything else in TP_fast_assign(), so the reclaim paths do no extra work when they are disabled.
Effect
======
Part 1 moves the loads of the tracepoint arguments out of the reclaim paths and into the trace event callbacks, which only run when the event is enabled. On x86_64 with gcc 11.4, the callers in mm/vmscan.o shrink accordingly (bytes), e.g.:
isolate_lru_folios 4060 -> 3896 (-164) __node_reclaim 2407 -> 2225 (-182) isolate_folios 8706 -> 8577 (-129) shrink_active_list 3674 -> 3549 (-125) evict_folios 4858 -> 4769 (-89)
There is no functional change in part 1. TP_STRUCT__entry and TP_printk of the existing events are untouched, so their tracefs format is identical. This was confirmed by diffing the tracefs format files before and after, and by capturing the direct/memcg/node reclaim-begin events under load in a QEMU guest (order, gfp_flags and memcg_id all match). BPF programs attached via raw_tp/tp_btf do see the new TP_PROTO and need to be adjusted; tracepoints are not a stable ABI (see Documentation/bpf/bpf_design_QA.rst).
highest_zoneidx and nr_reclaimed stay separate arguments: the former is a balance_pgdat() parameter (sc.reclaim_idx may differ from it by the time the end event fires), and the latter is the return value of shrink_folio_list(), which does not update sc->nr_reclaimed.
Part 2 makes 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=4 nr_scanned=1 nr_skipped=0 nr_taken=1 lru=inactive_file
mm_mglru_scan_folios: nid=0 memcg_id=1833 classzone=4 order=0 nr_requested=4 nr_scanned=1 nr_sorted=0 nr_skipped=0 nr_taken=1 lru=inactive_file max_seq=3 tier=3 min_seq=0 mm_vmscan_lru_shrink_inactive: nid=0 nr_scanned=1 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=2 flags=RECLAIM_WB_FILE|RECLAIM_WB_ASYNC
mm_mglru_inc_max_seq: nid=0 memcg_id=1833 max_seq=4 anon_min_seq=1 file_min_seq=1 nr_anon={0x0,0x0,0x0,0x0} nr_file={0x0,0x12,0x0,0x0}
mm_vmscan_lru_isolate: classzone=4 order=0 nr_requested=3 nr_scanned=3 nr_skipped=0 nr_taken=3 lru=inactive_file
mm_mglru_scan_folios: nid=0 memcg_id=1833 classzone=4 order=0 nr_requested=3 nr_scanned=3 nr_sorted=0 nr_skipped=0 nr_taken=3 lru=inactive_file max_seq=4 tier=3 min_seq=1 mm_vmscan_lru_shrink_inactive: nid=0 nr_scanned=3 nr_reclaimed=3 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=2 flags=RECLAIM_WB_FILE|RECLAIM_WB_ASYNC
The mm_mglru_scan_folios lines carry the node, 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, and the following scan already reports the moved window (max_seq=4 min_seq=1).
[1]: https://lore.kernel.org/linux-mm/20260916095122.50cbb620@robin/
This series was developed with the assistance of an LLM-based coding assistant. All changes have been reviewed by hand, and I take full responsibility for them.
---
Changes in v5:
- Squash v4 patches 4-7 into a single patch 4 (Barry).
- Link to v4: https://lore.kernel.org/linux-mm/[email protected]/
Changes in v4:
- Merge the MGLRU tracepoint series [2] into this one, on top of the restructuring, and drop the RFC tag.
- Patch 2: use TRACE_INCLUDE_PATH "." with CFLAGS_vmscan.o += -I$(src)
instead of a relative path (Steven).
- Patches 8-9: pass lruvec/sc and compute the fields in
TP_fast_assign() instead of at the call site (Steven).
- Patches 8-9: add nid, derived from the lruvec (Baoquan).
- Patch 8: group the int fields at the end of the entry to avoid
padding on 64-bit (Steven).
- Drop "mm/mglru: factor out lru_gen_seq_nr_pages()" from [2]: the per-generation page count is now summed inside TP_fast_assign(), so
the helper has no user. The Reviewed-by tags given to [2] are not
carried over, as patches 8-9 changed substantially.
- Link to RFC v3: https://lore.kernel.org/all/[email protected]/
Changes in RFC v3:
- Use "Assisted-by: LLM" (Muchun).
- Note the BPF raw_tp/tp_btf impact in the changelogs (Sashiko).
- Changelog wording fixes and minor indentation fixes.
- Link to RFC v2: https://lore.kernel.org/all/[email protected]/
Changes in RFC v2:
- Convert the remaining vmscan tracepoints too.
- Move struct scan_control to mm/vmscan.h (Baoquan).
[2]: https://lore.kernel.org/all/[email protected]/
Ridong Chen (6):
mm/memcontrol: drop unused vmscan tracepoint include mm/vmscan: move vmscan tracepoints to a local header mm/vmscan: move struct scan_control to a dedicated header mm/vmscan: pass scan_control to the vmscan tracepoints mm/mglru: add tracepoint for scan_folios() mm/mglru: add tracepoint for inc_max_seq()
MAINTAINERS | 4 + mm/Makefile | 1 + mm/memcontrol.c | 2 - mm/shrinker.c | 2 +- .../events/vmscan.h => mm/trace_vmscan.h | 193 ++++++++++++++---- mm/vmscan.c | 140 ++----------- mm/vmscan.h | 118 +++++++++++ 7 files changed, 296 insertions(+), 164 deletions(-) rename include/trace/events/vmscan.h => mm/trace_vmscan.h (76%) create mode 100644 mm/vmscan.h
Signature · 1 line
2.43.0
PATCH v5 1/6 mm/memcontrol: drop unused vmscan tracepoint include
From: Ridong Chen <[email protected]>
memcontrol.c includes <trace/events/vmscan.h> but does not use any of the tracepoints or macros it defines: all tracepoints invoked here come from memcg.h and writeback.h.
Drop it ahead of moving the vmscan trace header under mm/, after which <trace/events/vmscan.h> will no longer exist.
No functional change.
--- mm/memcontrol.c | 2 -- 1 file changed, 2 deletions(-)
mm/memcontrol.c +0 -2
@@ -77,8 +77,6 @@#include <trace/events/memcg.h> #undef CREATE_TRACE_POINTS -#include <trace/events/vmscan.h> -struct cgroup_subsys memory_cgrp_subsys__read_mostly ;EXPORT_SYMBOL (memory_cgrp_subsys);
Signature · 1 line
2.43.0
Re: [PATCH v5 1/6] mm/memcontrol: drop unused vmscan tracepoint include
Quoted 9 lines · Ridong Chen
From: Ridong Chen <[email protected]>
memcontrol.cincludes <trace/events/vmscan.h> but does not use any of the tracepoints or macros it defines: all tracepoints invoked here come frommemcg.handwriteback.h.Drop it ahead of moving the vmscan trace header under
mm/, after which <trace/events/vmscan.h> will no longer exist.
Quoted 8 lines
No functional change.
Acked-by Muchun Song <[email protected]>Signed-off-by Ridong Chen <[email protected]>Reviewed-by Barry Song <[email protected]>--- mm/memcontrol.c | 2 -- 1 file changed, 2 deletions(-)
Quoted 17 lines
mm/memcontrol.c +0
-2@@ -77,8 +77,6 @@#include <trace/events/memcg.h> #undef CREATE_TRACE_POINTS -#include <trace/events/vmscan.h> -struct cgroup_subsys memory_cgrp_subsys__read_mostly ;EXPORT_SYMBOL (memory_cgrp_subsys);Signature · 1 line
2.43.0
PATCH v5 2/6 mm/vmscan: move vmscan tracepoints to a local header
In reply to Ridong ChenFrom: Ridong Chen <[email protected]>
Move include/trace/events/vmscan.h to mm/trace_vmscan.h so that the tracepoints can be extended to reference vmscan-internal structures such as struct scan_control, which is not visible outside mm/. This mirrors what several other subsystems already do with their trace headers (e.g. fs/fuse/fuse_trace.h, fs/gfs2/trace_gfs2.h, fs/lockd/trace.h). As they do, set TRACE_INCLUDE_PATH to "." and add mm/ to the include path of vmscan.o, the only user that defines CREATE_TRACE_POINTS.
No functional change.
--- MAINTAINERS | 2 ++ mm/Makefile | 1 + mm/shrinker.c | 2 +- include/trace/events/vmscan.h => mm/trace_vmscan.h | 4 ++++ mm/vmscan.c | 2 +- 5 files changed, 9 insertions(+), 2 deletions(-) rename include/trace/events/vmscan.h => mm/trace_vmscan.h (99%)
MAINTAINERS +2 -0
@@ -17653,6 +17653,7 @@ F: Documentation/admin-guide/mm/multigen_lru.rst F: Documentation/mm/multigen_lru.rst F: include/linux/mm_inline.h F: include/linux/mmzone.h +F: mm/trace_vmscan.h F: mm/vmscan.c F: mm/workingset.c
@@ -17802,6 +17803,7 @@ R: Shakeel Butt <[email protected]> R: Lorenzo Stoakes <[email protected]> L: [email protected] S: Maintained +F: mm/trace_vmscan.h F: mm/vmscan.c F: mm/workingset.c
mm/Makefile +1 -0
@@ -32,6 +32,7 @@ KCOV_INSTRUMENT_vmstat.o := n KCOV_INSTRUMENT_failslab.o := n CFLAGS_init-mm.o += -Wno-override-init +CFLAGS_vmscan.o += -I$(src) mmu-y := nommu.o mmu-$(CONFIG_MMU) := highmem.o memory.o mincore.o \
mm/shrinker.c +1 -1
@@ -3,7 +3,7 @@#include <linux/rwsem.h> #include <linux/shrinker.h> #include <linux/rculist.h> -#include <trace/events/vmscan.h> +#include "trace_vmscan.h" #include "internal.h"
mm/trace_vmscan.h renamed from include/trace/events/vmscan.h +4 -0
@@ -648,4 +648,8 @@ TRACE_EVENT(mm_vmscan_kswapd_clear_hopeless,#endif /* _TRACE_VMSCAN_H */ /* This part must be outside protection */ +#undef TRACE_INCLUDE_FILE +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_FILE trace_vmscan#include <trace/define_trace.h>
mm/vmscan.c +1 -1
@@ -71,7 +71,7 @@#include "swap.h" #define CREATE_TRACE_POINTS -#include <trace/events/vmscan.h> +#include "trace_vmscan.h" struct scan_control {/* How many pages shrink_list() should reclaim */
Signature · 1 line
2.43.0
Re: [PATCH v5 2/6] mm/vmscan: move vmscan tracepoints to a local header
Quoted 14 lines · Ridong Chen
From: Ridong Chen <[email protected]>
Move
include/trace/events/vmscan.htomm/trace_vmscan.hso that the tracepoints can be extended to reference vmscan-internal structures such as structscan_control, which is not visible outsidemm/. This mirrors what several other subsystems already do with their trace headers (e.g.fs/fuse/fuse_trace.h,fs/gfs2/trace_gfs2.h,fs/lockd/trace.h). As they do, setTRACE_INCLUDE_PATHto "." and addmm/to the include path of vmscan.o, the only user that definesCREATE_TRACE_POINTS.
Quoted 4 lines
No functional change.
Signed-off-by Ridong Chen <[email protected]>Reviewed-by Barry Song <[email protected]>
Thanks
Re: [PATCH v5 2/6] mm/vmscan: move vmscan tracepoints to a local header
In reply to Ridong ChenQuoted 48 lines · Ridong Chen
From: Ridong Chen <[email protected]>
Move
include/trace/events/vmscan.htomm/trace_vmscan.hso that the tracepoints can be extended to reference vmscan-internal structures such as structscan_control, which is not visible outsidemm/. This mirrors what several other subsystems already do with their trace headers (e.g.fs/fuse/fuse_trace.h,fs/gfs2/trace_gfs2.h,fs/lockd/trace.h). As they do, setTRACE_INCLUDE_PATHto "." and addmm/to the include path of vmscan.o, the only user that definesCREATE_TRACE_POINTS.No functional change.
Signed-off-by Ridong Chen <[email protected]>Reviewed-by Barry Song <[email protected]>--- MAINTAINERS | 2 ++ mm/Makefile | 1 + mm/shrinker.c | 2 +- include/trace/events/vmscan.h => mm/trace_vmscan.h | 4 ++++ mm/vmscan.c | 2 +- 5 files changed, 9 insertions(+), 2 deletions(-) rename include/trace/events/vmscan.h => mm/trace_vmscan.h (99%)MAINTAINERS +2
-0@@ -17653,6 +17653,7 @@ F: Documentation/admin-guide/mm/multigen_lru.rst F: Documentation/mm/multigen_lru.rst F: include/linux/mm_inline.h F: include/linux/mmzone.h +F: mm/trace_vmscan.h F: mm/vmscan.c F: mm/workingset.c@@ -17802,6 +17803,7 @@ R: Shakeel Butt <[email protected]> R: Lorenzo Stoakes <[email protected]> L: [email protected] S: Maintained +F: mm/trace_vmscan.h F: mm/vmscan.c F: mm/workingset.cmm/Makefile +0
-0
mm/Makefile KCOV_INSTRUMENT_vmstat.o := n · line 35
Quoted 4 lines
mm/Makefile +1
-0@@ -32,6 +32,7 @@ KCOV_INSTRUMENT_vmstat.o := n KCOV_INSTRUMENT_failslab.o := n CFLAGS_init-mm.o += -Wno-override-init +CFLAGS_vmscan.o += -I$(src)
One nit:
Does it really need the above line? It affects mm/shrinker.c too, did your shrinker.o building fail?
Seems it's redundant because kbuild adds -I$(src) -I$(obj) for every object in the directory since commit cdd750bfb1f7 ("kbuild: remove 'addtree' and 'flags' magic for header search paths"). Please correct me if I am wrong.
Other than the nit, this patch looks good to me,
Thanks.
Quoted 48 lines
mm/Makefile +0
-0mmu-y := nommu.o mmu-$(CONFIG_MMU) := highmem.o memory.o mincore.o \mm/shrinker.c +1
-1@@ -3,7 +3,7 @@#include <linux/rwsem.h> #include <linux/shrinker.h> #include <linux/rculist.h> -#include <trace/events/vmscan.h> +#include "trace_vmscan.h" #include "internal.h" mm/trace_vmscan.h renamed from include/trace/events/vmscan.h +4
-0@@ -648,4 +648,8 @@ TRACE_EVENT(mm_vmscan_kswapd_clear_hopeless,#endif /* _TRACE_VMSCAN_H */ /* This part must be outside protection */ +#undef TRACE_INCLUDE_FILE +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_FILE trace_vmscan#include <trace/define_trace.h> mm/vmscan.c +1
-1@@ -71,7 +71,7 @@#include "swap.h" #define CREATE_TRACE_POINTS -#include <trace/events/vmscan.h> +#include "trace_vmscan.h" struct scan_control {/* How many pages shrink_list() should reclaim */ Signature · 1 line
2.43.0
Re: [PATCH v5 2/6] mm/vmscan: move vmscan tracepoints to a local header
Quoted 56 lines · Baoquan He
Quoted 48 lines · Ridong Chen
From: Ridong Chen <[email protected]>
Move
include/trace/events/vmscan.htomm/trace_vmscan.hso that the tracepoints can be extended to reference vmscan-internal structures such as structscan_control, which is not visible outsidemm/. This mirrors what several other subsystems already do with their trace headers (e.g.fs/fuse/fuse_trace.h,fs/gfs2/trace_gfs2.h,fs/lockd/trace.h). As they do, setTRACE_INCLUDE_PATHto "." and addmm/to the include path of vmscan.o, the only user that definesCREATE_TRACE_POINTS.No functional change.
Signed-off-by Ridong Chen <[email protected]>Reviewed-by Barry Song <[email protected]>--- MAINTAINERS | 2 ++ mm/Makefile | 1 + mm/shrinker.c | 2 +- include/trace/events/vmscan.h => mm/trace_vmscan.h | 4 ++++ mm/vmscan.c | 2 +- 5 files changed, 9 insertions(+), 2 deletions(-)rename
include/trace/events/vmscan.h=>mm/trace_vmscan.h(99%)MAINTAINERS +2
-0@@ -17653,6 +17653,7 @@ F: Documentation/admin-guide/mm/multigen_lru.rst F: Documentation/mm/multigen_lru.rst F: include/linux/mm_inline.h F: include/linux/mmzone.h +F: mm/trace_vmscan.h F: mm/vmscan.c F: mm/workingset.c@@ -17802,6 +17803,7 @@ R: Shakeel Butt <[email protected]> R: Lorenzo Stoakes <[email protected]> L: [email protected] S: Maintained +F: mm/trace_vmscan.h F: mm/vmscan.c F: mm/workingset.cmm/Makefile +0
-0mm/Makefile KCOV_INSTRUMENT_vmstat.o := n · line 35
Quoted 4 lines
mm/Makefile +1
-0@@ -32,6 +32,7 @@ KCOV_INSTRUMENT_vmstat.o := n KCOV_INSTRUMENT_failslab.o := n CFLAGS_init-mm.o += -Wno-override-init +CFLAGS_vmscan.o += -I$(src)One nit:
Quoted 8 lines
Does it really need the above line? It affects
mm/shrinker.ctoo, did your shrinker.o building fail?Seems it's redundant because kbuild adds -I$(src) -I$(obj) for every object in the directory since commit
cdd750bfb1f7("kbuild: remove 'addtree' and 'flags' magic for header search paths"). Please correct me if I am wrong.
Hi Baoquan,
Thanks for the review.
The line is needed for vmscan.o, and mm/shrinker.c is fine without it, because the two files include trace_vmscan.h in different ways.
mm/shrinker.c only does a plain #include "trace_vmscan.h". The lookup starts from mm/, where the header lives, so no -I is needed.
mm/vmscan.c defines CREATE_TRACE_POINTS before including the header. In that case <trace/define_trace.h>, included at the end of trace_vmscan.h, includes the header again to generate the tracepoint definitions:
#include TRACE_INCLUDE(TRACE_INCLUDE_FILE) /* -> "./trace_vmscan.h" */
This #include is in include/trace/define_trace.h, so the lookup starts from include/trace/ and only finds the header through -I. Without the flag, vmscan.o fails to build:
./include/trace/define_trace.h:118:42: fatal error: ./trace_vmscan.h: No such file or directory
As for cdd750bfb1f7, it doesn't add -I$(src) for every build. kbuild only adds -I$(src) -I$(obj) automatically when building with O= (see "ifdef building_out_of_srctree" in scripts/Makefile.lib). So with O= the line is indeed redundant and vmscan.o builds fine without it, but a plain in-tree "make" fails with the error above. The explicit flag keeps both working.
To be honest, I had the same worry at first: I thought every .c file that includes the header would need a Makefile change. That's why v3 used a relative TRACE_INCLUDE_PATH (../../mm) instead. Steven corrected me and pointed out that the proper way is TRACE_INCLUDE_PATH "." plus -I$(src) for the one object that defines CREATE_TRACE_POINTS [1]. That turned out to need only this one line. Thanks again to Steven for that.
[1] https://lore.kernel.org/all/[email protected]/
Quoted 53 lines
Other than the nit, this patch looks good to me,
Reviewed-by Baoquan He <[email protected]>Thanks.
Quoted 48 lines
mm/Makefile +0
-0mmu-y := nommu.o mmu-$(CONFIG_MMU) := highmem.o memory.o mincore.o \mm/shrinker.c +1
-1@@ -3,7 +3,7 @@#include <linux/rwsem.h> #include <linux/shrinker.h> #include <linux/rculist.h> -#include <trace/events/vmscan.h> +#include "trace_vmscan.h" #include "internal.h" mm/trace_vmscan.h renamed from include/trace/events/vmscan.h +4
-0@@ -648,4 +648,8 @@ TRACE_EVENT(mm_vmscan_kswapd_clear_hopeless,#endif /* _TRACE_VMSCAN_H */ /* This part must be outside protection */ +#undef TRACE_INCLUDE_FILE +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_FILE trace_vmscan#include <trace/define_trace.h> mm/vmscan.c +1
-1@@ -71,7 +71,7 @@#include "swap.h" #define CREATE_TRACE_POINTS -#include <trace/events/vmscan.h> +#include "trace_vmscan.h" struct scan_control {/* How many pages shrink_list() should reclaim */ Signature · 1 line
2.43.0
Signature · 2 lines
Best regards Ridong
Baoquan He
reviewerRe: [PATCH v5 2/6] mm/vmscan: move vmscan tracepoints to a local header
Quoted 96 lines · Ridong Chen
Quoted 53 lines · Baoquan He
Quoted 46 lines · Ridong Chen
From: Ridong Chen <[email protected]>
Move
include/trace/events/vmscan.htomm/trace_vmscan.hso that the tracepoints can be extended to reference vmscan-internal structures such as structscan_control, which is not visible outsidemm/. This mirrors what several other subsystems already do with their trace headers (e.g.fs/fuse/fuse_trace.h,fs/gfs2/trace_gfs2.h,fs/lockd/trace.h). As they do, setTRACE_INCLUDE_PATHto "." and addmm/to the include path of vmscan.o, the only user that definesCREATE_TRACE_POINTS.No functional change.
Signed-off-by Ridong Chen <[email protected]>Reviewed-by Barry Song <[email protected]>--- MAINTAINERS | 2 ++ mm/Makefile | 1 + mm/shrinker.c | 2 +- include/trace/events/vmscan.h => mm/trace_vmscan.h | 4 ++++ mm/vmscan.c | 2 +- 5 files changed, 9 insertions(+), 2 deletions(-)rename
include/trace/events/vmscan.h=>mm/trace_vmscan.h(99%)MAINTAINERS +2
-0@@ -17653,6 +17653,7 @@ F: Documentation/admin-guide/mm/multigen_lru.rst F: Documentation/mm/multigen_lru.rst F: include/linux/mm_inline.h F: include/linux/mmzone.h +F: mm/trace_vmscan.h F: mm/vmscan.c F: mm/workingset.c@@ -17802,6 +17803,7 @@ R: Shakeel Butt <[email protected]> R: Lorenzo Stoakes <[email protected]> L: [email protected] S: Maintained +F: mm/trace_vmscan.h F: mm/vmscan.c F: mm/workingset.cmm/Makefile +0
-0mm/Makefile KCOV_INSTRUMENT_vmstat.o := n · line 34
Quoted 3 lines
mm/Makefile +1
-0@@ -32,6 +32,7 @@ KCOV_INSTRUMENT_vmstat.o := n KCOV_INSTRUMENT_failslab.o := n CFLAGS_init-mm.o += -Wno-override-init +CFLAGS_vmscan.o += -I$(src)One nit:
Quoted 8 lines
Does it really need the above line? It affects
mm/shrinker.ctoo, did your shrinker.o building fail?Seems it's redundant because kbuild adds -I$(src) -I$(obj) for every object in the directory since commit
cdd750bfb1f7("kbuild: remove 'addtree' and 'flags' magic for header search paths"). Please correct me if I am wrong.Hi Baoquan,
Thanks for the review.
The line is needed for vmscan.o, and
mm/shrinker.cis fine without it, because the two files includetrace_vmscan.hin different ways.
mm/shrinker.conly does a plain #include "trace_vmscan.h". The lookup starts frommm/, where the header lives, so no -I is needed.
mm/vmscan.cdefinesCREATE_TRACE_POINTSbefore including the header. In that case <trace/define_trace.h>, included at the end oftrace_vmscan.h, includes the header again to generate the tracepoint definitions:#include
TRACE_INCLUDE(TRACE_INCLUDE_FILE)/* -> "./trace_vmscan.h" */This #include is in
include/trace/define_trace.h, so the lookup starts frominclude/trace/and only finds the header through -I. Without the flag, vmscan.o fails to build:
./include/trace/define_trace.h:118:42: fatal error: ./trace_vmscan.h: No such file or directoryAs for
cdd750bfb1f7, it doesn't add -I$(src) for every build. kbuild only adds -I$(src) -I$(obj) automatically when building with O= (see "ifdefbuilding_out_of_srctree" inscripts/Makefile.lib). So with O= the line is indeed redundant and vmscan.o builds fine without it, but a plain in-tree "make" fails with the error above. The explicit flag keeps both working.
Quoted 7 lines
To be honest, I had the same worry at first: I thought every .c file that includes the header would need a
Makefilechange. That's why v3 used a relativeTRACE_INCLUDE_PATH(../../mm) instead. Steven corrected me and pointed out that the proper way isTRACE_INCLUDE_PATH"." plus -I$(src) for the one object that definesCREATE_TRACE_POINTS[1]. That turned out to need only this one line. Thanks again to Steven for that.
You are right, my nit was wrong. Learned it from you, thanks for the detailed explanation.
Quoted 56 lines
[1] https://lore.kernel.org/all/[email protected]/
Quoted 47 lines
Other than the nit, this patch looks good to me,
Reviewed-by Baoquan He <[email protected]>Thanks.
Quoted 42 lines
mm/Makefile +0
-0mmu-y := nommu.o mmu-$(CONFIG_MMU) := highmem.o memory.o mincore.o \mm/shrinker.c +1
-1@@ -3,7 +3,7 @@#include <linux/rwsem.h> #include <linux/shrinker.h> #include <linux/rculist.h> -#include <trace/events/vmscan.h> +#include "trace_vmscan.h" #include "internal.h" mm/trace_vmscan.h renamed from include/trace/events/vmscan.h +4
-0@@ -648,4 +648,8 @@ TRACE_EVENT(mm_vmscan_kswapd_clear_hopeless,#endif /* _TRACE_VMSCAN_H */ /* This part must be outside protection */ +#undef TRACE_INCLUDE_FILE +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_FILE trace_vmscan#include <trace/define_trace.h> mm/vmscan.c +1
-1@@ -71,7 +71,7 @@#include "swap.h" #define CREATE_TRACE_POINTS -#include <trace/events/vmscan.h> +#include "trace_vmscan.h" struct scan_control {/* How many pages shrink_list() should reclaim */ Signature · 1 line
2.43.0Signature · 2 lines
Best regards Ridong
PATCH v5 3/6 mm/vmscan: move struct scan_control to a dedicated header
In reply to Ridong ChenFrom: Ridong Chen <[email protected]>
The definition of struct scan_control has lived inline at the top of mm/vmscan.c. Move it out to a new mm/vmscan.h so the reclaim-internal state can be referenced from other reclaim translation units without duplicating the definition.
The header uses an ordinary include guard and pulls in only what the struct needs (nodemask_t, gfp_t and struct reclaim_state).
mm/trace_vmscan.h includes the new header directly so that any translation unit pulling in the trace events (mm/vmscan.c, mm/shrinker.c) sees the full definition of struct scan_control regardless of include order.
No functional change.
--- MAINTAINERS | 2 + mm/trace_vmscan.h | 2 + mm/vmscan.c | 108 ------------------------------------------ mm/vmscan.h | 118 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 122 insertions(+), 108 deletions(-) create mode 100644 mm/vmscan.h
MAINTAINERS +2 -0
@@ -17655,6 +17655,7 @@ F: include/linux/mm_inline.h F: include/linux/mmzone.h F: mm/trace_vmscan.h F: mm/vmscan.c +F: mm/vmscan.h F: mm/workingset.c MEMORY MANAGEMENT - MIGRATION
@@ -17805,6 +17806,7 @@ L: [email protected] S: Maintained F: mm/trace_vmscan.h F: mm/vmscan.c +F: mm/vmscan.h F: mm/workingset.c MEMORY MANAGEMENT - RMAP (REVERSE MAPPING)
mm/trace_vmscan.h +2 -0
@@ -11,6 +11,8 @@#include <linux/memcontrol.h> #include <trace/events/mmflags.h> +#include "vmscan.h" +#define RECLAIM_WB_ANON 0x0001u#define RECLAIM_WB_FILE 0x0002u#define RECLAIM_WB_MIXED 0x0010u
mm/vmscan.c +0 -108
114 lines not on this page · raw message
mm/vmscan.h new file +118 -0
118 lines not on this page · raw message
Signature · 1 line
2.43.0
Re: [PATCH v5 3/6] mm/vmscan: move struct scan_control to a dedicated header
Quoted 14 lines · Ridong Chen
From: Ridong Chen <[email protected]>
The definition of struct
scan_controlhas lived inline at the top ofmm/vmscan.c. Move it out to a newmm/vmscan.hso the reclaim-internal state can be referenced from other reclaim translation units without duplicating the definition.The header uses an ordinary include guard and pulls in only what the struct needs (
nodemask_t,gfp_tand structreclaim_state).
Quoted 8 lines
mm/trace_vmscan.hincludes the new header directly so that any translation unit pulling in the trace events (mm/vmscan.c,mm/shrinker.c) sees the full definition of structscan_controlregardless of include order.No functional change.
Signed-off-by Ridong Chen <[email protected]>
Thanks
Re: [PATCH v5 3/6] mm/vmscan: move struct scan_control to a dedicated header
In reply to Ridong ChenQuoted 17 lines · Ridong Chen
From: Ridong Chen <[email protected]>
The definition of struct
scan_controlhas lived inline at the top ofmm/vmscan.c. Move it out to a newmm/vmscan.hso the reclaim-internal state can be referenced from other reclaim translation units without duplicating the definition.The header uses an ordinary include guard and pulls in only what the struct needs (
nodemask_t,gfp_tand structreclaim_state).
mm/trace_vmscan.hincludes the new header directly so that any translation unit pulling in the trace events (mm/vmscan.c,mm/shrinker.c) sees the full definition of structscan_controlregardless of include order.No functional change.
Quoted 8 lines
Signed-off-by Ridong Chen <[email protected]>--- MAINTAINERS | 2 + mm/trace_vmscan.h | 2 + mm/vmscan.c | 108 ------------------------------------------ mm/vmscan.h | 118 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 122 insertions(+), 108 deletions(-) create mode 100644 mm/vmscan.h
Quoted 280 lines
MAINTAINERS +2
-0@@ -17655,6 +17655,7 @@ F: include/linux/mm_inline.h F: include/linux/mmzone.h F: mm/trace_vmscan.h F: mm/vmscan.c +F: mm/vmscan.h F: mm/workingset.c MEMORY MANAGEMENT - MIGRATION@@ -17805,6 +17806,7 @@ L: [email protected] S: Maintained F: mm/trace_vmscan.h F: mm/vmscan.c +F: mm/vmscan.h F: mm/workingset.c MEMORY MANAGEMENT - RMAP (REVERSE MAPPING)mm/trace_vmscan.h +2
-0@@ -11,6 +11,8 @@#include <linux/memcontrol.h> #include <trace/events/mmflags.h> +#include "vmscan.h" +#define RECLAIM_WB_ANON 0x0001u#define RECLAIM_WB_FILE 0x0002u#define RECLAIM_WB_MIXED 0x0010umm/vmscan.c +0
-108@@ -73,114 +73,6 @@#define CREATE_TRACE_POINTS #include "trace_vmscan.h" -struct scan_control { -/* How many pages shrink_list() should reclaim */ -unsigned long nr_to_reclaim; - -/* -* Nodemask of nodes allowed by the caller. If NULL, all nodes -* are scanned. -*/ -const nodemask_t *nodemask; - -/* -* The memory cgroup that hit its limit and as a result is the -* primary target of this reclaim invocation. -*/ -struct mem_cgroup *target_mem_cgroup; - -/* -* Scan pressure balancing between anon and file LRUs -*/ -unsigned long anon_cost; -unsigned long file_cost; - -/* Swappiness value for proactive reclaim. Always use sc_swappiness()! */ -int *proactive_swappiness; - -/* Can active folios be deactivated as part of reclaim? */ -#define DEACTIVATE_ANON 1 -#define DEACTIVATE_FILE 2 -unsigned int may_deactivate:2 ; -unsigned int force_deactivate:1 ; -unsigned int skipped_deactivate:1 ; - -/* zone_reclaim_mode, boost reclaim */ -unsigned int may_writepage:1 ; - -/* zone_reclaim_mode */ -unsigned int may_unmap:1 ; - -/* zone_reclaim_mode, boost reclaim, cgroup restrictions */ -unsigned int may_swap:1 ; - -/* Not allow cache_trim_mode to be turned on as part of reclaim? */ -unsigned int no_cache_trim_mode:1 ; - -/* Has cache_trim_mode failed at least once? */ -unsigned int cache_trim_mode_failed:1 ; - -/* Proactive reclaim invoked by userspace */ -unsigned int proactive:1 ; - -/* -* Cgroup memory below memory.low is protected as long as we -* don't threaten to OOM. If any cgroup is reclaimed at -* reduced force or passed over entirely due to its memory.low -* setting (memcg_low_skipped), and nothing is reclaimed as a -* result, then go back for one more cycle that reclaims the protected -* memory (memcg_low_reclaim) to avert OOM. -*/ -unsigned int memcg_low_reclaim:1 ; -unsigned int memcg_low_skipped:1 ; - -/* Shared cgroup tree walk failed, rescan the whole tree */ -unsigned int memcg_full_walk:1 ; - -unsigned int hibernation_mode:1 ; - -/* One of the zones is ready for compaction */ -unsigned int compaction_ready:1 ; - -/* There is easily reclaimable cold cache in the current node */ -unsigned int cache_trim_mode:1 ; - -/* The file folios on the current node are dangerously low */ -unsigned int file_is_tiny:1 ; - -/* Always discard instead of demoting to lower tier memory */ -unsigned int no_demotion:1 ; - -/* Allocation order */ -s8 order; - -/* Scan (total_size >> priority) pages at once */ -s8 priority; - -/* The highest zone to isolate folios for reclaim from */ -s8 reclaim_idx; - -/* This context's GFP mask */ -gfp_t gfp_mask; - -/* Incremented by the number of inactive pages that were scanned */ -unsigned long nr_scanned; - -/* Number of pages freed so far during a call to shrink_zones() */ -unsigned long nr_reclaimed; - -struct { -unsigned int dirty; -unsigned int congested; -unsigned int writeback; -unsigned int immediate; -unsigned int taken; - } nr; - -/* for recording the reclaimed slab by now */ -struct reclaim_state reclaim_state; -}; -#ifdef ARCH_HAS_PREFETCHW static inline void prefetchw_prev_lru_folio (struct folio *folio,struct list_head *base)mm/vmscan.h new file +118
-0@@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _MM_VMSCAN_H +#define _MM_VMSCAN_H + +#include <linux/gfp_types.h> +#include <linux/nodemask.h> +#include <linux/swap.h> +#include <linux/types.h> + +struct scan_control { +/* How many pages shrink_list() should reclaim */ +unsigned long nr_to_reclaim; + +/* +* Nodemask of nodes allowed by the caller. If NULL, all nodes +* are scanned. +*/ +const nodemask_t *nodemask; + +/* +* The memory cgroup that hit its limit and as a result is the +* primary target of this reclaim invocation. +*/ +struct mem_cgroup *target_mem_cgroup; + +/* +* Scan pressure balancing between anon and file LRUs +*/ +unsigned long anon_cost; +unsigned long file_cost; + +/* Swappiness value for proactive reclaim. Always use sc_swappiness()! */ +int *proactive_swappiness; + +/* Can active folios be deactivated as part of reclaim? */ +#define DEACTIVATE_ANON 1 +#define DEACTIVATE_FILE 2 +unsigned int may_deactivate:2 ; +unsigned int force_deactivate:1 ; +unsigned int skipped_deactivate:1 ; + +/* zone_reclaim_mode, boost reclaim */ +unsigned int may_writepage:1 ; + +/* zone_reclaim_mode */ +unsigned int may_unmap:1 ; + +/* zone_reclaim_mode, boost reclaim, cgroup restrictions */ +unsigned int may_swap:1 ; + +/* Not allow cache_trim_mode to be turned on as part of reclaim? */ +unsigned int no_cache_trim_mode:1 ; + +/* Has cache_trim_mode failed at least once? */ +unsigned int cache_trim_mode_failed:1 ; + +/* Proactive reclaim invoked by userspace */ +unsigned int proactive:1 ; + +/* +* Cgroup memory below memory.low is protected as long as we +* don't threaten to OOM. If any cgroup is reclaimed at +* reduced force or passed over entirely due to its memory.low +* setting (memcg_low_skipped), and nothing is reclaimed as a +* result, then go back for one more cycle that reclaims the protected +* memory (memcg_low_reclaim) to avert OOM. +*/ +unsigned int memcg_low_reclaim:1 ; +unsigned int memcg_low_skipped:1 ; + +/* Shared cgroup tree walk failed, rescan the whole tree */ +unsigned int memcg_full_walk:1 ; + +unsigned int hibernation_mode:1 ; + +/* One of the zones is ready for compaction */ +unsigned int compaction_ready:1 ; + +/* There is easily reclaimable cold cache in the current node */ +unsigned int cache_trim_mode:1 ; + +/* The file folios on the current node are dangerously low */ +unsigned int file_is_tiny:1 ; + +/* Always discard instead of demoting to lower tier memory */ +unsigned int no_demotion:1 ; + +/* Allocation order */ +s8 order; + +/* Scan (total_size >> priority) pages at once */ +s8 priority; + +/* The highest zone to isolate folios for reclaim from */ +s8 reclaim_idx; + +/* This context's GFP mask */ +gfp_t gfp_mask; + +/* Incremented by the number of inactive pages that were scanned */ +unsigned long nr_scanned; + +/* Number of pages freed so far during a call to shrink_zones() */ +unsigned long nr_reclaimed; + +struct { +unsigned int dirty; +unsigned int congested; +unsigned int writeback; +unsigned int immediate; +unsigned int taken; + } nr; + +/* for recording the reclaimed slab by now */ +struct reclaim_state reclaim_state; +}; + +#endif /* _MM_VMSCAN_H */ Signature · 1 line
2.43.0
PATCH v5 4/6 mm/vmscan: pass scan_control to the vmscan tracepoints
In reply to Ridong ChenFrom: Ridong Chen <[email protected]>
Several vmscan tracepoints spell out reclaim state as individual arguments, all of which the caller already holds in struct scan_control. Now that the struct is visible from the trace header, pass sc directly and pick the fields out in TP_fast_assign instead of spelling them out at every callsite:
- mm_vmscan_direct_reclaim_begin, mm_vmscan_memcg_reclaim_begin and mm_vmscan_node_reclaim_begin: gfp_mask and order.
- mm_vmscan_lru_isolate, mm_vmscan_lru_shrink_inactive and mm_vmscan_lru_shrink_active: reclaim_idx, order and priority.
- mm_vmscan_reclaim_pages: nr_scanned. nr_reclaimed stays a separate
argument: it is the return value of shrink_folio_list(), which does not update sc->nr_reclaimed.
- mm_vmscan_balance_pgdat_end: order and nr_reclaimed. highest_zoneidx stays a separate argument: it is a balance_pgdat() parameter, and sc.reclaim_idx may differ from it by the time the end event fires.
No functional change. The tracefs event format (TP_STRUCT__entry and TP_printk) is unchanged, so tracefs-based consumers are unaffected. BPF programs attached via raw_tp/tp_btf see the new TP_PROTO and need to be adjusted; tracepoints are not a stable ABI (see Documentation/bpf/bpf_design_QA.rst).
--- mm/trace_vmscan.h | 74 +++++++++++++++++++++++------------------------ mm/vmscan.c | 26 ++++++++--------- 2 files changed, 48 insertions(+), 52 deletions(-)
mm/trace_vmscan.h +36 -38
@@ -124,10 +124,9 @@ TRACE_EVENT(mm_vmscan_balance_pgdat_begin, TRACE_EVENT(mm_vmscan_balance_pgdat_end, - TP_PROTO(int nid, int order, int highest_zoneidx, - unsigned long nr_reclaimed), + TP_PROTO(int nid, struct scan_control *sc, int highest_zoneidx), - TP_ARGS(nid, order, highest_zoneidx, nr_reclaimed), + TP_ARGS(nid, sc, highest_zoneidx), TP_STRUCT__entry( __field(int, nid)
@@ -138,9 +137,9 @@ TRACE_EVENT(mm_vmscan_balance_pgdat_end,TP_fast_assign ( __entry->nid = nid; - __entry->order = order; + __entry->order = sc->order; __entry->highest_zoneidx = highest_zoneidx; - __entry->nr_reclaimed = nr_reclaimed; + __entry->nr_reclaimed = sc->nr_reclaimed; ), TP_printk("nid=%d order=%d highest_zoneidx=%-8s nr_reclaimed=%lu",
@@ -178,9 +177,9 @@ TRACE_EVENT(mm_vmscan_wakeup_kswapd, DECLARE_EVENT_CLASS(mm_vmscan_direct_reclaim_begin_template, - TP_PROTO(gfp_t gfp_flags, int order, struct mem_cgroup *memcg), + TP_PROTO(struct scan_control *sc, struct mem_cgroup *memcg), - TP_ARGS(gfp_flags, order, memcg), + TP_ARGS(sc, memcg), TP_STRUCT__entry( __field( unsigned long, gfp_flags )
@@ -189,8 +188,8 @@ DECLARE_EVENT_CLASS(mm_vmscan_direct_reclaim_begin_template, ),TP_fast_assign ( - __entry->gfp_flags = (__force unsigned long)gfp_flags; - __entry->order = order; + __entry->gfp_flags = (__force unsigned long)sc->gfp_mask; + __entry->order = sc->order; __entry->memcg_id =mem_cgroup_id (memcg); ),
@@ -204,17 +203,17 @@ DECLARE_EVENT_CLASS(mm_vmscan_direct_reclaim_begin_template,DEFINE_EVENT (mm_vmscan_direct_reclaim_begin_template, mm_vmscan_direct_reclaim_begin, - TP_PROTO(gfp_t gfp_flags, int order, struct mem_cgroup *memcg), + TP_PROTO(struct scan_control *sc, struct mem_cgroup *memcg), -TP_ARGS (gfp_flags, order, memcg) +TP_ARGS (sc, memcg) );#ifdef CONFIG_MEMCG DEFINE_EVENT (mm_vmscan_direct_reclaim_begin_template, mm_vmscan_memcg_reclaim_begin, - TP_PROTO(gfp_t gfp_flags, int order, struct mem_cgroup *memcg), + TP_PROTO(struct scan_control *sc, struct mem_cgroup *memcg), -TP_ARGS (gfp_flags, order, memcg) +TP_ARGS (sc, memcg) );#endif /* CONFIG_MEMCG */
@@ -350,15 +349,14 @@ TRACE_EVENT(mm_shrink_slab_end, ); TRACE_EVENT(mm_vmscan_lru_isolate, - TP_PROTO(int highest_zoneidx, - int order, - unsigned long nr_requested, - unsigned long nr_scanned, - unsigned long nr_skipped, - unsigned long nr_taken, - int lru), + TP_PROTO(struct scan_control *sc, + unsigned long nr_requested, + unsigned long nr_scanned, + unsigned long nr_skipped, + unsigned long nr_taken, + int lru), - TP_ARGS(highest_zoneidx, order, nr_requested, nr_scanned, nr_skipped, nr_taken, lru), + TP_ARGS(sc, nr_requested, nr_scanned, nr_skipped, nr_taken, lru), TP_STRUCT__entry( __field(int, highest_zoneidx)
@@ -371,8 +369,8 @@ TRACE_EVENT(mm_vmscan_lru_isolate, ), TP_fast_assign( - __entry->highest_zoneidx = highest_zoneidx; - __entry->order = order; + __entry->highest_zoneidx = sc->reclaim_idx; + __entry->order = sc->order; __entry->nr_requested = nr_requested; __entry->nr_scanned = nr_scanned; __entry->nr_skipped = nr_skipped;
@@ -419,11 +417,11 @@ TRACE_EVENT(mm_vmscan_write_folio, TRACE_EVENT(mm_vmscan_reclaim_pages, - TP_PROTO(int nid, - unsigned long nr_scanned, unsigned long nr_reclaimed, - struct reclaim_stat *stat), + TP_PROTO(int nid, struct scan_control *sc, + unsigned long nr_reclaimed, + struct reclaim_stat *stat), - TP_ARGS(nid, nr_scanned, nr_reclaimed, stat), + TP_ARGS(nid, sc, nr_reclaimed, stat), TP_STRUCT__entry( __field(int, nid)
@@ -441,7 +439,7 @@ TRACE_EVENT(mm_vmscan_reclaim_pages,TP_fast_assign ( __entry->nid = nid; - __entry->nr_scanned = nr_scanned; + __entry->nr_scanned = sc->nr_scanned; __entry->nr_reclaimed = nr_reclaimed; __entry->nr_dirty = stat->nr_dirty; __entry->nr_writeback = stat->nr_writeback;
@@ -466,9 +464,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, struct scan_control *sc, int file), -TP_ARGS (nid, nr_scanned, nr_reclaimed, stat, priority, file), +TP_ARGS (nid, nr_scanned, nr_reclaimed, stat, sc, file),TP_STRUCT__entry ( __field(int, nid)
@@ -498,7 +496,7 @@ TRACE_EVENT(mm_vmscan_lru_shrink_inactive, __entry->nr_activate1 = stat->nr_activate[1 ]; __entry->nr_ref_keep = stat->nr_ref_keep; __entry->nr_unmap_fail = stat->nr_unmap_fail; - __entry->priority = priority; + __entry->priority = sc->priority; __entry->reclaim_flags =trace_reclaim_flags (file); ),
@@ -517,9 +515,9 @@ 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, struct scan_control *sc, int file), -TP_ARGS (nid, nr_taken, nr_active, nr_deactivated, nr_referenced, priority, file), +TP_ARGS (nid, nr_taken, nr_active, nr_deactivated, nr_referenced, sc, file),TP_STRUCT__entry ( __field(int, nid)
@@ -537,7 +535,7 @@ TRACE_EVENT(mm_vmscan_lru_shrink_active, __entry->nr_active = nr_active; __entry->nr_deactivated = nr_deactivated; __entry->nr_referenced = nr_referenced; - __entry->priority = priority; + __entry->priority = sc->priority; __entry->reclaim_flags =trace_reclaim_flags (file); ),
@@ -551,9 +549,9 @@ TRACE_EVENT(mm_vmscan_lru_shrink_active, TRACE_EVENT(mm_vmscan_node_reclaim_begin, - TP_PROTO(int nid, int order, gfp_t gfp_flags), + TP_PROTO(int nid, struct scan_control *sc), - TP_ARGS(nid, order, gfp_flags), + TP_ARGS(nid, sc), TP_STRUCT__entry( __field(int, nid)
@@ -563,8 +561,8 @@ TRACE_EVENT(mm_vmscan_node_reclaim_begin,TP_fast_assign ( __entry->nid = nid; - __entry->order = order; - __entry->gfp_flags = (__force unsigned long )gfp_flags; + __entry->order = sc->order; + __entry->gfp_flags = (__force unsigned long )sc->gfp_mask; ), TP_printk("nid=%d order=%d gfp_flags=%s",
mm/vmscan.c +12 -14
@@ -1764,7 +1764,7 @@ static unsigned long isolate_lru_folios(unsigned long nr_to_scan, } } *nr_scanned = total_scan; -trace_mm_vmscan_lru_isolate (sc->reclaim_idx, sc->order, nr_to_scan, +trace_mm_vmscan_lru_isolate (sc, nr_to_scan, total_scan, skipped, nr_taken, lru);update_lru_sizes (lruvec, lru, nr_zone_taken);return nr_taken;
@@ -2053,7 +2053,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, file);return nr_reclaimed; }
@@ -2160,7 +2160,7 @@ 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, file); } static unsigned int reclaim_folio_list(struct list_head *folio_list,
@@ -2183,7 +2183,7 @@ static unsigned int reclaim_folio_list(struct list_head *folio_list,list_del (&folio->lru);folio_putback_lru (folio); } -trace_mm_vmscan_reclaim_pages (pgdat->node_id, sc.nr_scanned, nr_reclaimed, &stat); +trace_mm_vmscan_reclaim_pages (pgdat->node_id, &sc, nr_reclaimed, &stat);return nr_reclaimed; }
@@ -4849,9 +4849,9 @@ static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec,mod_lruvec_state (lruvec, item, isolated);mod_lruvec_state (lruvec,PGREFILL , sorted);mod_lruvec_state (lruvec,PGSCAN_ANON + type, isolated); -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_vmscan_lru_isolate (sc, nr_to_scan, + scanned, skipped, isolated, + type ?LRU_INACTIVE_FILE :LRU_INACTIVE_ANON ); *isolatedp = isolated;return scanned;
@@ -4993,7 +4993,7 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,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_scanned, reclaimed, &stat, sc, type ?LRU_INACTIVE_FILE :LRU_INACTIVE_ANON );list_for_each_entry_safe_reverse (folio, next, &list, lru) {
@@ -6889,7 +6889,7 @@ unsigned long try_to_free_pages(struct zonelist *zonelist, int order,return 1 ;set_task_reclaim_state (current, &sc.reclaim_state); -trace_mm_vmscan_direct_reclaim_begin (sc.gfp_mask, order,NULL ); +trace_mm_vmscan_direct_reclaim_begin (&sc,NULL ); nr_reclaimed =do_try_to_free_pages (zonelist, &sc);
@@ -6930,7 +6930,7 @@ unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg,struct zonelist *zonelist =node_zonelist (numa_node_id (), sc.gfp_mask);set_task_reclaim_state (current, &sc.reclaim_state); -trace_mm_vmscan_memcg_reclaim_begin (sc.gfp_mask,0 , memcg); +trace_mm_vmscan_memcg_reclaim_begin (&sc, memcg); noreclaim_flag =memalloc_noreclaim_save (); nr_reclaimed =do_try_to_free_pages (zonelist, &sc);
@@ -7407,8 +7407,7 @@ static int balance_pgdat(pg_data_t *pgdat, int order, int highest_zoneidx)psi_memstall_leave (&pflags);set_task_reclaim_state (current,NULL ); -trace_mm_vmscan_balance_pgdat_end (pgdat->node_id, sc.order, - highest_zoneidx, sc.nr_reclaimed); +trace_mm_vmscan_balance_pgdat_end (pgdat->node_id, &sc, highest_zoneidx);/* * Return the order kswapd stopped reclaiming at as
@@ -7886,8 +7885,7 @@ static unsigned long __node_reclaim(struct pglist_data *pgdat,unsigned int noreclaim_flag;unsigned long pflags; -trace_mm_vmscan_node_reclaim_begin (pgdat->node_id, sc->order, - sc->gfp_mask); +trace_mm_vmscan_node_reclaim_begin (pgdat->node_id, sc);cond_resched ();if (!sc->proactive)
Signature · 1 line
2.43.0
Re: [PATCH v5 4/6] mm/vmscan: pass scan_control to the vmscan tracepoints
Quoted 26 lines · Ridong Chen
From: Ridong Chen <[email protected]>
Several vmscan tracepoints spell out reclaim state as individual arguments, all of which the caller already holds in struct
scan_control. Now that the struct is visible from the trace header, pass sc directly and pick the fields out inTP_fast_assigninstead of spelling them out at every callsite:-
mm_vmscan_direct_reclaim_begin,mm_vmscan_memcg_reclaim_beginandmm_vmscan_node_reclaim_begin:gfp_maskand order.-
mm_vmscan_lru_isolate,mm_vmscan_lru_shrink_inactiveandmm_vmscan_lru_shrink_active:reclaim_idx, order and priority.-
mm_vmscan_reclaim_pages:nr_scanned.nr_reclaimedstays a separate
argument: it is the return value ofshrink_folio_list(), which does not updatesc->nr_reclaimed.-
mm_vmscan_balance_pgdat_end: order andnr_reclaimed.highest_zoneidxstays a separate argument: it is abalance_pgdat()parameter, and sc.reclaim_idx may differ from it by the time the end event fires.
Quoted 7 lines
No functional change. The tracefs event format (
TP_STRUCT__entryandTP_printk) is unchanged, so tracefs-based consumers are unaffected. BPF programs attached via raw_tp/tp_btf see the newTP_PROTOand need to be adjusted; tracepoints are not a stable ABI (seeDocumentation/bpf/bpf_design_QA.rst).Signed-off-by Ridong Chen <[email protected]>
Thanks
Re: [PATCH v5 4/6] mm/vmscan: pass scan_control to the vmscan tracepoints
In reply to Ridong ChenQuoted 28 lines · Ridong Chen
From: Ridong Chen <[email protected]>
Several vmscan tracepoints spell out reclaim state as individual arguments, all of which the caller already holds in struct
scan_control. Now that the struct is visible from the trace header, pass sc directly and pick the fields out inTP_fast_assigninstead of spelling them out at every callsite:-
mm_vmscan_direct_reclaim_begin,mm_vmscan_memcg_reclaim_beginandmm_vmscan_node_reclaim_begin:gfp_maskand order.-
mm_vmscan_lru_isolate,mm_vmscan_lru_shrink_inactiveandmm_vmscan_lru_shrink_active:reclaim_idx, order and priority.-
mm_vmscan_reclaim_pages:nr_scanned.nr_reclaimedstays a separate
argument: it is the return value ofshrink_folio_list(), which does not updatesc->nr_reclaimed.-
mm_vmscan_balance_pgdat_end: order andnr_reclaimed.highest_zoneidxstays a separate argument: it is abalance_pgdat()parameter, and sc.reclaim_idx may differ from it by the time the end event fires.No functional change. The tracefs event format (
TP_STRUCT__entryandTP_printk) is unchanged, so tracefs-based consumers are unaffected. BPF programs attached via raw_tp/tp_btf see the newTP_PROTOand need to be adjusted; tracepoints are not a stable ABI (seeDocumentation/bpf/bpf_design_QA.rst).
Quoted 5 lines
Signed-off-by Ridong Chen <[email protected]>--- mm/trace_vmscan.h | 74 +++++++++++++++++++++++------------------------ mm/vmscan.c | 26 ++++++++--------- 2 files changed, 48 insertions(+), 52 deletions(-)
LGMT,
Quoted 302 lines
mm/trace_vmscan.h +36
-38@@ -124,10 +124,9 @@ TRACE_EVENT(mm_vmscan_balance_pgdat_begin, TRACE_EVENT(mm_vmscan_balance_pgdat_end, - TP_PROTO(int nid, int order, int highest_zoneidx, - unsigned long nr_reclaimed), + TP_PROTO(int nid, struct scan_control *sc, int highest_zoneidx), - TP_ARGS(nid, order, highest_zoneidx, nr_reclaimed), + TP_ARGS(nid, sc, highest_zoneidx), TP_STRUCT__entry( __field(int, nid)@@ -138,9 +137,9 @@ TRACE_EVENT(mm_vmscan_balance_pgdat_end,TP_fast_assign ( __entry->nid = nid; - __entry->order = order; + __entry->order = sc->order; __entry->highest_zoneidx = highest_zoneidx; - __entry->nr_reclaimed = nr_reclaimed; + __entry->nr_reclaimed = sc->nr_reclaimed; ), TP_printk("nid=%d order=%d highest_zoneidx=%-8s nr_reclaimed=%lu",@@ -178,9 +177,9 @@ TRACE_EVENT(mm_vmscan_wakeup_kswapd, DECLARE_EVENT_CLASS(mm_vmscan_direct_reclaim_begin_template, - TP_PROTO(gfp_t gfp_flags, int order, struct mem_cgroup *memcg), + TP_PROTO(struct scan_control *sc, struct mem_cgroup *memcg), - TP_ARGS(gfp_flags, order, memcg), + TP_ARGS(sc, memcg), TP_STRUCT__entry( __field( unsigned long, gfp_flags )@@ -189,8 +188,8 @@ DECLARE_EVENT_CLASS(mm_vmscan_direct_reclaim_begin_template, ),TP_fast_assign ( - __entry->gfp_flags = (__force unsigned long)gfp_flags; - __entry->order = order; + __entry->gfp_flags = (__force unsigned long)sc->gfp_mask; + __entry->order = sc->order; __entry->memcg_id =mem_cgroup_id (memcg); ),@@ -204,17 +203,17 @@ DECLARE_EVENT_CLASS(mm_vmscan_direct_reclaim_begin_template,DEFINE_EVENT (mm_vmscan_direct_reclaim_begin_template, mm_vmscan_direct_reclaim_begin, - TP_PROTO(gfp_t gfp_flags, int order, struct mem_cgroup *memcg), + TP_PROTO(struct scan_control *sc, struct mem_cgroup *memcg), -TP_ARGS (gfp_flags, order, memcg) +TP_ARGS (sc, memcg) );#ifdef CONFIG_MEMCG DEFINE_EVENT (mm_vmscan_direct_reclaim_begin_template, mm_vmscan_memcg_reclaim_begin, - TP_PROTO(gfp_t gfp_flags, int order, struct mem_cgroup *memcg), + TP_PROTO(struct scan_control *sc, struct mem_cgroup *memcg), -TP_ARGS (gfp_flags, order, memcg) +TP_ARGS (sc, memcg) );#endif /* CONFIG_MEMCG */ @@ -350,15 +349,14 @@ TRACE_EVENT(mm_shrink_slab_end, ); TRACE_EVENT(mm_vmscan_lru_isolate, - TP_PROTO(int highest_zoneidx, - int order, - unsigned long nr_requested, - unsigned long nr_scanned, - unsigned long nr_skipped, - unsigned long nr_taken, - int lru), + TP_PROTO(struct scan_control *sc, + unsigned long nr_requested, + unsigned long nr_scanned, + unsigned long nr_skipped, + unsigned long nr_taken, + int lru), - TP_ARGS(highest_zoneidx, order, nr_requested, nr_scanned, nr_skipped, nr_taken, lru), + TP_ARGS(sc, nr_requested, nr_scanned, nr_skipped, nr_taken, lru), TP_STRUCT__entry( __field(int, highest_zoneidx)@@ -371,8 +369,8 @@ TRACE_EVENT(mm_vmscan_lru_isolate, ), TP_fast_assign( - __entry->highest_zoneidx = highest_zoneidx; - __entry->order = order; + __entry->highest_zoneidx = sc->reclaim_idx; + __entry->order = sc->order; __entry->nr_requested = nr_requested; __entry->nr_scanned = nr_scanned; __entry->nr_skipped = nr_skipped;@@ -419,11 +417,11 @@ TRACE_EVENT(mm_vmscan_write_folio, TRACE_EVENT(mm_vmscan_reclaim_pages, - TP_PROTO(int nid, - unsigned long nr_scanned, unsigned long nr_reclaimed, - struct reclaim_stat *stat), + TP_PROTO(int nid, struct scan_control *sc, + unsigned long nr_reclaimed, + struct reclaim_stat *stat), - TP_ARGS(nid, nr_scanned, nr_reclaimed, stat), + TP_ARGS(nid, sc, nr_reclaimed, stat), TP_STRUCT__entry( __field(int, nid)@@ -441,7 +439,7 @@ TRACE_EVENT(mm_vmscan_reclaim_pages,TP_fast_assign ( __entry->nid = nid; - __entry->nr_scanned = nr_scanned; + __entry->nr_scanned = sc->nr_scanned; __entry->nr_reclaimed = nr_reclaimed; __entry->nr_dirty = stat->nr_dirty; __entry->nr_writeback = stat->nr_writeback;@@ -466,9 +464,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, struct scan_control *sc, int file), -TP_ARGS (nid, nr_scanned, nr_reclaimed, stat, priority, file), +TP_ARGS (nid, nr_scanned, nr_reclaimed, stat, sc, file),TP_STRUCT__entry ( __field(int, nid)@@ -498,7 +496,7 @@ TRACE_EVENT(mm_vmscan_lru_shrink_inactive, __entry->nr_activate1 = stat->nr_activate[1 ]; __entry->nr_ref_keep = stat->nr_ref_keep; __entry->nr_unmap_fail = stat->nr_unmap_fail; - __entry->priority = priority; + __entry->priority = sc->priority; __entry->reclaim_flags =trace_reclaim_flags (file); ),@@ -517,9 +515,9 @@ 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, struct scan_control *sc, int file), -TP_ARGS (nid, nr_taken, nr_active, nr_deactivated, nr_referenced, priority, file), +TP_ARGS (nid, nr_taken, nr_active, nr_deactivated, nr_referenced, sc, file),TP_STRUCT__entry ( __field(int, nid)@@ -537,7 +535,7 @@ TRACE_EVENT(mm_vmscan_lru_shrink_active, __entry->nr_active = nr_active; __entry->nr_deactivated = nr_deactivated; __entry->nr_referenced = nr_referenced; - __entry->priority = priority; + __entry->priority = sc->priority; __entry->reclaim_flags =trace_reclaim_flags (file); ),@@ -551,9 +549,9 @@ TRACE_EVENT(mm_vmscan_lru_shrink_active, TRACE_EVENT(mm_vmscan_node_reclaim_begin, - TP_PROTO(int nid, int order, gfp_t gfp_flags), + TP_PROTO(int nid, struct scan_control *sc), - TP_ARGS(nid, order, gfp_flags), + TP_ARGS(nid, sc), TP_STRUCT__entry( __field(int, nid)@@ -563,8 +561,8 @@ TRACE_EVENT(mm_vmscan_node_reclaim_begin,TP_fast_assign ( __entry->nid = nid; - __entry->order = order; - __entry->gfp_flags = (__force unsigned long )gfp_flags; + __entry->order = sc->order; + __entry->gfp_flags = (__force unsigned long )sc->gfp_mask; ), TP_printk("nid=%d order=%d gfp_flags=%s",mm/vmscan.c +12
-14@@ -1764,7 +1764,7 @@ static unsigned long isolate_lru_folios(unsigned long nr_to_scan, } } *nr_scanned = total_scan; -trace_mm_vmscan_lru_isolate (sc->reclaim_idx, sc->order, nr_to_scan, +trace_mm_vmscan_lru_isolate (sc, nr_to_scan, total_scan, skipped, nr_taken, lru);update_lru_sizes (lruvec, lru, nr_zone_taken);return nr_taken;@@ -2053,7 +2053,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, file);return nr_reclaimed; }@@ -2160,7 +2160,7 @@ 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, file); } static unsigned int reclaim_folio_list(struct list_head *folio_list,@@ -2183,7 +2183,7 @@ static unsigned int reclaim_folio_list(struct list_head *folio_list,list_del (&folio->lru);folio_putback_lru (folio); } -trace_mm_vmscan_reclaim_pages (pgdat->node_id, sc.nr_scanned, nr_reclaimed, &stat); +trace_mm_vmscan_reclaim_pages (pgdat->node_id, &sc, nr_reclaimed, &stat);return nr_reclaimed; }@@ -4849,9 +4849,9 @@ static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec,mod_lruvec_state (lruvec, item, isolated);mod_lruvec_state (lruvec,PGREFILL , sorted);mod_lruvec_state (lruvec,PGSCAN_ANON + type, isolated); -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_vmscan_lru_isolate (sc, nr_to_scan, + scanned, skipped, isolated, + type ?LRU_INACTIVE_FILE :LRU_INACTIVE_ANON ); *isolatedp = isolated;return scanned;@@ -4993,7 +4993,7 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,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_scanned, reclaimed, &stat, sc, type ?LRU_INACTIVE_FILE :LRU_INACTIVE_ANON );list_for_each_entry_safe_reverse (folio, next, &list, lru) {@@ -6889,7 +6889,7 @@ unsigned long try_to_free_pages(struct zonelist *zonelist, int order,return 1 ;set_task_reclaim_state (current, &sc.reclaim_state); -trace_mm_vmscan_direct_reclaim_begin (sc.gfp_mask, order,NULL ); +trace_mm_vmscan_direct_reclaim_begin (&sc,NULL ); nr_reclaimed =do_try_to_free_pages (zonelist, &sc);@@ -6930,7 +6930,7 @@ unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg,struct zonelist *zonelist =node_zonelist (numa_node_id (), sc.gfp_mask);set_task_reclaim_state (current, &sc.reclaim_state); -trace_mm_vmscan_memcg_reclaim_begin (sc.gfp_mask,0 , memcg); +trace_mm_vmscan_memcg_reclaim_begin (&sc, memcg); noreclaim_flag =memalloc_noreclaim_save (); nr_reclaimed =do_try_to_free_pages (zonelist, &sc);@@ -7407,8 +7407,7 @@ static int balance_pgdat(pg_data_t *pgdat, int order, int highest_zoneidx)psi_memstall_leave (&pflags);set_task_reclaim_state (current,NULL ); -trace_mm_vmscan_balance_pgdat_end (pgdat->node_id, sc.order, - highest_zoneidx, sc.nr_reclaimed); +trace_mm_vmscan_balance_pgdat_end (pgdat->node_id, &sc, highest_zoneidx);/* * Return the order kswapd stopped reclaiming at as @@ -7886,8 +7885,7 @@ static unsigned long __node_reclaim(struct pglist_data *pgdat,unsigned int noreclaim_flag;unsigned long pflags; -trace_mm_vmscan_node_reclaim_begin (pgdat->node_id, sc->order, - sc->gfp_mask); +trace_mm_vmscan_node_reclaim_begin (pgdat->node_id, sc);cond_resched ();if (!sc->proactive)Signature · 1 line
2.43.0
PATCH v5 5/6 mm/mglru: add tracepoint for scan_folios()
In reply to Ridong ChenFrom: Ridong Chen <[email protected]>
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 context the classic tracepoint lacks: the node id and memcg id of the lruvec, and the max_seq, tier and min_seq of the type being scanned.
Now that struct scan_control is visible to the trace header, pass sc, lruvec, type and tier straight in and derive everything else in TP_fast_assign(), so nothing is computed at the call site when the event is disabled. The int fields are grouped at the end of the entry to avoid padding on 64-bit.
scan_folios() is the MGLRU-specific layer where folios are actually scanned, so it has no classic-LRU counterpart. The existing tracepoint is left unchanged.
--- mm/trace_vmscan.h | 66 +++++++++++++++++++++++++++++++++++++++++++++++ mm/vmscan.c | 2 ++ 2 files changed, 68 insertions(+)
mm/trace_vmscan.h +66 -0
@@ -392,6 +392,72 @@ TRACE_EVENT(mm_vmscan_lru_isolate, __print_symbolic(__entry->lru, LRU_NAMES)) ); +#ifdef CONFIG_LRU_GEN +TRACE_EVENT (mm_mglru_scan_folios, + + TP_PROTO(struct lruvec *lruvec, + struct scan_control *sc, + unsigned long nr_requested, + unsigned long nr_scanned, + unsigned long nr_sorted, + unsigned long nr_skipped, + unsigned long nr_taken, + int type, + int tier), + +TP_ARGS (lruvec, sc, nr_requested, nr_scanned, nr_sorted, nr_skipped, + nr_taken, type, tier), + +TP_STRUCT__entry ( + __field(u64, memcg_id) + __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, nid) + __field(int, highest_zoneidx) + __field(int, order) + __field(int, lru) +__field (int, tier) + ), + +TP_fast_assign ( + __entry->memcg_id = mem_cgroup_id(lruvec_memcg(lruvec)); + __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->max_seq = lruvec->lrugen.max_seq; + __entry->min_seq = lruvec->lrugen.min_seq[type]; + __entry->nid = lruvec_pgdat(lruvec)->node_id; + __entry->highest_zoneidx = sc->reclaim_idx; + __entry->order = sc->order; + __entry->lru = type ? LRU_INACTIVE_FILE : LRU_INACTIVE_ANON; + __entry->tier = tier; + ), + +TP_printk ("nid=%d 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->nid, + __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) +); + +#endif /* CONFIG_LRU_GEN */ + TRACE_EVENT(mm_vmscan_write_folio, TP_PROTO(struct folio *folio),
mm/vmscan.c +2 -0
@@ -4852,6 +4852,8 @@ static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec,trace_mm_vmscan_lru_isolate (sc, nr_to_scan, scanned, skipped, isolated, type ?LRU_INACTIVE_FILE :LRU_INACTIVE_ANON ); +trace_mm_mglru_scan_folios (lruvec, sc, nr_to_scan, scanned, sorted, + skipped, isolated, type, tier); *isolatedp = isolated;return scanned;
Signature · 1 line
2.43.0
Re: [PATCH v5 5/6] mm/mglru: add tracepoint for scan_folios()
Quoted 22 lines · Ridong Chen
From: Ridong Chen <[email protected]>
MGLRU's
scan_folios()emits the classic-LRU tracepointtrace_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_foliosnext to it, reporting the same counters plusnr_sorted(folios moved to a younger generation bysort_folio()) and the context the classic tracepoint lacks: the node id and memcg id of the lruvec, and themax_seq, tier andmin_seqof the type being scanned.Now that struct
scan_controlis visible to the trace header, pass sc, lruvec, type and tier straight in and derive everything else inTP_fast_assign(), so nothing is computed at the call site when the event is disabled. The int fields are grouped at the end of the entry to avoid padding on 64-bit.
Quoted 6 lines
scan_folios()is the MGLRU-specific layer where folios are actually scanned, so it has no classic-LRU counterpart. The existing tracepoint is left unchanged.Signed-off-by Ridong Chen <[email protected]>---
Ran a test on this:
/sys/kernel/tracing/events/vmscan/mm_mglru_scan_folios # cat format
name: mm_mglru_scan_folios
ID: 491
format:
field:unsigned short common_type; offset:0; size:2; signed:0; field:unsigned char common_flags; offset:2; size:1; signed:0; field:unsigned char common_preempt_count; offset:3; size:1; signed:0; field:int common_pid; offset:4; size:4; signed:1;
field:u64 memcg_id; offset:8; size:8; signed:0; field:unsigned long nr_requested; offset:16; size:8; signed:0; field:unsigned long nr_scanned; offset:24; size:8; signed:0; field:unsigned long nr_sorted; offset:32; size:8; signed:0; field:unsigned long nr_skipped; offset:40; size:8; signed:0; field:unsigned long nr_taken; offset:48; size:8; signed:0; field:unsigned long max_seq; offset:56; size:8; signed:0; field:unsigned long min_seq; offset:64; size:8; signed:0; field:int nid; offset:72; size:4; signed:1; field:int highest_zoneidx; offset:76; size:4; signed:1; field:int order; offset:80; size:4; signed:1; field:int lru; offset:84; size:4; signed:1; field:int tier; offset:88; size:4; signed:1;
print fmt: "nid=%d 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", REC->nid, REC->memcg_id, REC->highest_zoneidx, REC->order, REC->nr_requested, REC->nr_scanned, REC->nr_sorted, REC->nr_skipped, REC->nr_taken, __print_symbolic(REC->lru, {0, "inactive_anon"}, {1, "active_anon"}, {2, "inactive_file"}, {3, "active_file"}, {4, "unevictable"}), REC->max_seq, REC->tier, REC->min_seq
~ # echo 1 > /sys/kernel/tracing/events/vmscan/mm_mglru_scan_folios/enable ~ # ./a.out
[+] mapped 500 MB at 0xffff9f000000 (128000 pages)
[+] duration: 60 seconds
[+] first pass done, start looping writes
^C
~ # cat /sys/kernel/tracing/trace | grep mglru_scan_folios | head
kswapd0-50 [000] d..1. 650.877105:
mm_mglru_scan_folios: nid=0 memcg_id=1 classzone=2 order=0
nr_requested=64 nr_scanned=0 nr_sorted=0 nr_skipped=0 nr_taken=0
lru=inactive_anon max_seq=3 tier=3 min_seq=1
kswapd0-50 [000] d..1. 650.877686:
mm_mglru_scan_folios: nid=0 memcg_id=1 classzone=2 order=0
nr_requested=64 nr_scanned=64 nr_sorted=0 nr_skipped=0 nr_taken=64
lru=inactive_file max_seq=3 tier=3 min_seq=0
kswapd0-50 [000] d..1. 650.907558:
mm_mglru_scan_folios: nid=0 memcg_id=1 classzone=2 order=0
nr_requested=64 nr_scanned=0 nr_sorted=0 nr_skipped=0 nr_taken=0
lru=inactive_anon max_seq=3 tier=3 min_seq=1
kswapd0-50 [000] d..1. 650.907729:
mm_mglru_scan_folios: nid=0 memcg_id=1 classzone=2 order=0
nr_requested=64 nr_scanned=64 nr_sorted=0 nr_skipped=0 nr_taken=64
lru=inactive_file max_seq=3 tier=3 min_seq=0
kswapd0-50 [000] d..1. 650.909040:
mm_mglru_scan_folios: nid=0 memcg_id=1 classzone=2 order=0
nr_requested=64 nr_scanned=0 nr_sorted=0 nr_skipped=0 nr_taken=0
lru=inactive_anon max_seq=3 tier=3 min_seq=1
kswapd0-50 [000] d..1. 650.909198:
mm_mglru_scan_folios: nid=0 memcg_id=1 classzone=2 order=0
nr_requested=64 nr_scanned=64 nr_sorted=0 nr_skipped=0 nr_taken=64
lru=inactive_file max_seq=3 tier=3 min_seq=0
kswapd0-50 [000] d..1. 650.910520:
mm_mglru_scan_folios: nid=0 memcg_id=1 classzone=2 order=0
nr_requested=64 nr_scanned=0 nr_sorted=0 nr_skipped=0 nr_taken=0
lru=inactive_anon max_seq=3 tier=3 min_seq=1
kswapd0-50 [000] d..1. 650.910710:
mm_mglru_scan_folios: nid=0 memcg_id=1 classzone=2 order=0
nr_requested=64 nr_scanned=64 nr_sorted=4 nr_skipped=0 nr_taken=60
lru=inactive_file max_seq=3 tier=3 min_seq=0
kswapd0-50 [000] d..1. 650.911866:
mm_mglru_scan_folios: nid=0 memcg_id=1 classzone=2 order=0
nr_requested=64 nr_scanned=0 nr_sorted=0 nr_skipped=0 nr_taken=0
lru=inactive_anon max_seq=3 tier=3 min_seq=1
kswapd0-50 [000] d..1. 650.911988:mm_mglru_scan_folios: nid=0 memcg_id=1 classzone=2 order=0 nr_requested=64 nr_scanned=66 nr_sorted=0 nr_skipped=0 nr_taken=66 lru=inactive_file max_seq=3 tier=3 min_seq=0
The LRU naming feels a bit weird to me. We have "inactive_file" and "inactive_anon", but MGLRU doesn't have active/inactive lists. I guess it's not a big issue, though, since we also use the same names in trace_mm_vmscan_lru_isolate() in scan_folios().
For the patch:
Re: [PATCH v5 5/6] mm/mglru: add tracepoint for scan_folios()
In reply to Ridong ChenQuoted 55 lines · Ridong Chen
From: Ridong Chen <[email protected]>
MGLRU's
scan_folios()emits the classic-LRU tracepointtrace_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_foliosnext to it, reporting the same counters plusnr_sorted(folios moved to a younger generation bysort_folio()) and the context the classic tracepoint lacks: the node id and memcg id of the lruvec, and themax_seq, tier andmin_seqof the type being scanned.Now that struct
scan_controlis visible to the trace header, pass sc, lruvec, type and tier straight in and derive everything else inTP_fast_assign(), so nothing is computed at the call site when the event is disabled. The int fields are grouped at the end of the entry to avoid padding on 64-bit.
scan_folios()is the MGLRU-specific layer where folios are actually scanned, so it has no classic-LRU counterpart. The existing tracepoint is left unchanged.Signed-off-by Ridong Chen <[email protected]>--- mm/trace_vmscan.h | 66 +++++++++++++++++++++++++++++++++++++++++++++++ mm/vmscan.c | 2 ++ 2 files changed, 68 insertions(+)mm/trace_vmscan.h +16
-0@@ -392,6 +392,72 @@ TRACE_EVENT(mm_vmscan_lru_isolate, __print_symbolic(__entry->lru, LRU_NAMES)) ); +#ifdef CONFIG_LRU_GEN +TRACE_EVENT(mm_mglru_scan_folios, + + TP_PROTO(struct lruvec *lruvec, + struct scan_control *sc, + unsigned long nr_requested, + unsigned long nr_scanned, + unsigned long nr_sorted, + unsigned long nr_skipped, + unsigned long nr_taken, + int type, + int tier), + + TP_ARGS(lruvec, sc, nr_requested, nr_scanned, nr_sorted, nr_skipped, + nr_taken, type, tier), +
mm/trace_vmscan.h TRACE_EVENT(mm_vmscan_lru_isolate, · line 420
Quoted 10 lines
mm/trace_vmscan.h +10
-0+TP_STRUCT__entry ( +__field (u64 ,memcg_id ) + __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, nid)
Now it has nid printed out, it's cool.
This patch looks good to me, thanks.
Quoted 59 lines
mm/trace_vmscan.h +40
-0+ __field(int, highest_zoneidx) + __field(int, order) + __field(int, lru) +__field (int, tier) + ), + +TP_fast_assign ( + __entry->memcg_id = mem_cgroup_id(lruvec_memcg(lruvec)); + __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->max_seq = lruvec->lrugen.max_seq; + __entry->min_seq = lruvec->lrugen.min_seq[type]; + __entry->nid =lruvec_pgdat (lruvec)->node_id; + __entry->highest_zoneidx = sc->reclaim_idx; + __entry->order = sc->order; + __entry->lru = type ?LRU_INACTIVE_FILE :LRU_INACTIVE_ANON ; + __entry->tier = tier; + ), + +TP_printk ("nid=%d 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->nid, + __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) +); + +#endif /* CONFIG_LRU_GEN */ + TRACE_EVENT(mm_vmscan_write_folio, TP_PROTO(struct folio *folio),mm/vmscan.c +2
-0@@ -4852,6 +4852,8 @@ static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec,trace_mm_vmscan_lru_isolate (sc, nr_to_scan, scanned, skipped, isolated, type ?LRU_INACTIVE_FILE :LRU_INACTIVE_ANON ); +trace_mm_mglru_scan_folios (lruvec, sc, nr_to_scan, scanned, sorted, + skipped, isolated, type, tier); *isolatedp = isolated;return scanned;Signature · 1 line
2.43.0
PATCH v5 6/6 mm/mglru: add tracepoint for inc_max_seq()
In reply to Ridong ChenFrom: Ridong Chen <[email protected]>
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 while the lruvec lock is still held, with the node id, 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 and clamped to >= 0 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.
Only the lruvec is passed in; all of the above is collected in TP_fast_assign(), so inc_max_seq() does no extra work when the event is disabled. Paired with mm_mglru_scan_folios it makes the full aging-to-eviction window observable per memcg.
--- mm/trace_vmscan.h | 47 +++++++++++++++++++++++++++++++++++++++++++++++ mm/vmscan.c | 2 ++ 2 files changed, 49 insertions(+)
mm/trace_vmscan.h +47 -0
@@ -456,6 +456,53 @@ TRACE_EVENT(mm_mglru_scan_folios, __entry->min_seq) ); +TRACE_EVENT (mm_mglru_inc_max_seq, + + TP_PROTO(struct lruvec *lruvec), + +TP_ARGS (lruvec), + +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) +__field (int, nid) + ), + +TP_fast_assign ( + struct lru_gen_folio *lrugen = &lruvec->lrugen; + int gen, zone; + + __entry->memcg_id = mem_cgroup_id(lruvec_memcg(lruvec)); + __entry->max_seq = lrugen->max_seq; + __entry->anon_min_seq = lrugen->min_seq[LRU_GEN_ANON]; + __entry->file_min_seq = lrugen->min_seq[LRU_GEN_FILE]; + /* summed over zones and clamped to >= 0, as debugfs lru_gen does */ + for (gen = 0; gen < MAX_NR_GENS; gen++) { + __entry->nr_anon[gen] =0 ; + __entry->nr_file[gen] =0 ; +for (zone =0 ; zone <MAX_NR_ZONES ; zone++) { + __entry->nr_anon[gen] += +max (lrugen->nr_pages[gen][LRU_GEN_ANON ][zone],0L ); + __entry->nr_file[gen] += +max (lrugen->nr_pages[gen][LRU_GEN_FILE ][zone],0L ); + } + } + __entry->nid = lruvec_pgdat(lruvec)->node_id; + ), + +TP_printk ("nid=%d memcg_id=%llu max_seq=%lu anon_min_seq=%lu file_min_seq=%lu nr_anon=%s nr_file=%s" , + __entry->nid, + __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 ))) +);#endif /* CONFIG_LRU_GEN */ TRACE_EVENT(mm_vmscan_write_folio,
mm/vmscan.c +2 -0
@@ -4134,6 +4134,8 @@ static bool inc_max_seq(struct lruvec *lruvec, unsigned long seq, int swappinessWRITE_ONCE (lrugen->timestamps[next], jiffies);/* make sure preceding modifications appear */ smp_store_release (&lrugen->max_seq, lrugen->max_seq +1 ); + +trace_mm_mglru_inc_max_seq (lruvec); unlock:lruvec_unlock_irq (lruvec);
Signature · 1 line
2.43.0
Barry Song
reviewerRe: [PATCH v5 6/6] mm/mglru: add tracepoint for inc_max_seq()
Quoted 61 lines · Ridong Chen
From: Ridong Chen <[email protected]>
Aging in MGLRU advances
max_seqviainc_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 themin_seqof each type trails behind it, cannot be observed as it happens.Add
mm_mglru_inc_max_seq, emitted right aftermax_seqis bumped while the lruvec lock is still held, with the node id, memcg id, the newmax_seq, the anon and filemin_seq, and the number of pages in each generation for both types, summed over zones and clamped to >= 0 the same way the debugfslru_genfile 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 followsMAX_NR_GENSautomatically.Only the lruvec is passed in; all of the above is collected in
TP_fast_assign(), soinc_max_seq()does no extra work when the event is disabled. Paired withmm_mglru_scan_foliosit makes the full aging-to-eviction window observable per memcg.Signed-off-by Ridong Chen <[email protected]>--- mm/trace_vmscan.h | 47 +++++++++++++++++++++++++++++++++++++++++++++++ mm/vmscan.c | 2 ++ 2 files changed, 49 insertions(+)mm/trace_vmscan.h +25
-0@@ -456,6 +456,53 @@ TRACE_EVENT(mm_mglru_scan_folios, __entry->min_seq) ); +TRACE_EVENT(mm_mglru_inc_max_seq, + + TP_PROTO(struct lruvec *lruvec), + + TP_ARGS(lruvec), + + 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) + __field(int, nid) + ), + + TP_fast_assign( + struct lru_gen_folio *lrugen = &lruvec->lrugen; + int gen, zone; + + __entry->memcg_id = mem_cgroup_id(lruvec_memcg(lruvec)); + __entry->max_seq = lrugen->max_seq; + __entry->anon_min_seq = lrugen->min_seq[LRU_GEN_ANON]; + __entry->file_min_seq = lrugen->min_seq[LRU_GEN_FILE]; +/* summed over zones and clamped to >= 0, as debugfs lru_gen does */
mm/trace_vmscan.h TRACE_EVENT(mm_mglru_scan_folios, · line 493
Quoted 10 lines
mm/trace_vmscan.h +10
-0+for (gen =0 ; gen <MAX_NR_GENS ; gen++) { + __entry->nr_anon[gen] =0 ; + __entry->nr_file[gen] =0 ; +for (zone =0 ; zone <MAX_NR_ZONES ; zone++) { + __entry->nr_anon[gen] += +max (lrugen->nr_pages[gen][LRU_GEN_ANON ][zone],0L ); + __entry->nr_file[gen] += +max (lrugen->nr_pages[gen][LRU_GEN_FILE ][zone],0L ); + } + }
I feel this is wrong. Can we calculate the newest and oldest generations from seq, then print them in order from newest to oldest?
The nr_pages[] array isn't ordered from newest to oldest or vice versa, as we have:
static inline int lru_gen_from_seq(unsigned long seq)
{
return seq % MAX_NR_GENS;
}And I added a helper here:
https://lore.kernel.org/linux-mm/[email protected]/
+static inline unsigned long lruvec_gen_size(struct lru_gen_folio *lrugen,
+ int type, unsigned long seq)
+{
+ int gen = lru_gen_from_seq(seq);
+ unsigned long size = 0;
+
+ for (int zone = 0; zone < MAX_NR_ZONES; zone++)
+ size += max(READ_ONCE(lrugen->nr_pages[gen][type][zone]), 0L);
+ return size;
+}
Maybe you could port it here as a separate patch so it can land earlier as part of your tracepoints series. We could then use it in the next version of the swappiness series.
Best,
Barry
Baoquan He
reviewerRe: [PATCH v5 6/6] mm/mglru: add tracepoint for inc_max_seq()
Quoted 68 lines · Barry Song
Quoted 67 lines · Ridong Chen
From: Ridong Chen <[email protected]>
Aging in MGLRU advances
max_seqviainc_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 themin_seqof each type trails behind it, cannot be observed as it happens.Add
mm_mglru_inc_max_seq, emitted right aftermax_seqis bumped while the lruvec lock is still held, with the node id, memcg id, the newmax_seq, the anon and filemin_seq, and the number of pages in each generation for both types, summed over zones and clamped to >= 0 the same way the debugfslru_genfile 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 followsMAX_NR_GENSautomatically.Only the lruvec is passed in; all of the above is collected in
TP_fast_assign(), soinc_max_seq()does no extra work when the event is disabled. Paired withmm_mglru_scan_foliosit makes the full aging-to-eviction window observable per memcg.Signed-off-by Ridong Chen <[email protected]>--- mm/trace_vmscan.h | 47 +++++++++++++++++++++++++++++++++++++++++++++++ mm/vmscan.c | 2 ++ 2 files changed, 49 insertions(+)mm/trace_vmscan.h +31
-0@@ -456,6 +456,53 @@ TRACE_EVENT(mm_mglru_scan_folios, __entry->min_seq) ); +TRACE_EVENT(mm_mglru_inc_max_seq, + + TP_PROTO(struct lruvec *lruvec), + + TP_ARGS(lruvec), + + 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) + __field(int, nid) + ), + + TP_fast_assign( + struct lru_gen_folio *lrugen = &lruvec->lrugen; + int gen, zone; + + __entry->memcg_id = mem_cgroup_id(lruvec_memcg(lruvec)); + __entry->max_seq = lrugen->max_seq; + __entry->anon_min_seq = lrugen->min_seq[LRU_GEN_ANON]; + __entry->file_min_seq = lrugen->min_seq[LRU_GEN_FILE]; + /* summed over zones and clamped to >= 0, as debugfs lru_gen does */ + for (gen = 0; gen < MAX_NR_GENS; gen++) { + __entry->nr_anon[gen] = 0; + __entry->nr_file[gen] = 0; + for (zone = 0; zone < MAX_NR_ZONES; zone++) { + __entry->nr_anon[gen] += + max(lrugen->nr_pages[gen][LRU_GEN_ANON][zone], 0L);
Quoted 8 lines
mm/trace_vmscan.h TRACE_EVENT(mm_mglru_scan_folios, · line 493
Quoted 4 lines
mm/trace_vmscan.h +4
-0+ __entry->nr_file[gen] += +max (lrugen->nr_pages[gen][LRU_GEN_FILE ][zone],0L ); + } + }I feel this is wrong. Can we calculate the newest and oldest generations from seq, then print them in order from newest to oldest?
Agree, from newest to oldest or oldest to newest makes it clearer.
Printing from lrugen->max_seq - MAX_NR_GENS + 1, or from MAX_NR_GENS, both looks good.
Quoted 30 lines
The nr_pages[] array isn't ordered from newest to oldest or vice versa, as we have:
static inline int
lru_gen_from_seq(unsigned long seq){ return seq % MAX_NR_GENS; }And I added a helper here:
https://lore.kernel.org/linux-mm/[email protected]/(quoted diff) +10
-0+static inline unsigned long lruvec_gen_size(struct lru_gen_folio *lrugen, + int type, unsigned long seq) +{ + int gen = lru_gen_from_seq(seq); + unsigned long size = 0; + + for (int zone = 0; zone < MAX_NR_ZONES; zone++) + size += max(READ_ONCE(lrugen->nr_pages[gen][type][zone]), 0L); + return size; +}Maybe you could port it here as a separate patch so it can land earlier as part of your tracepoints series. We could then use it in the next version of the swappiness series.
Best,
Barry
Re: [PATCH v5 6/6] mm/mglru: add tracepoint for inc_max_seq()
Quoted 78 lines · Baoquan He
Quoted 68 lines · Barry Song
Quoted 67 lines · Ridong Chen
From: Ridong Chen <[email protected]>
Aging in MGLRU advances
max_seqviainc_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 themin_seqof each type trails behind it, cannot be observed as it happens.Add
mm_mglru_inc_max_seq, emitted right aftermax_seqis bumped while the lruvec lock is still held, with the node id, memcg id, the newmax_seq, the anon and filemin_seq, and the number of pages in each generation for both types, summed over zones and clamped to >= 0 the same way the debugfslru_genfile 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 followsMAX_NR_GENSautomatically.Only the lruvec is passed in; all of the above is collected in
TP_fast_assign(), soinc_max_seq()does no extra work when the event is disabled. Paired withmm_mglru_scan_foliosit makes the full aging-to-eviction window observable per memcg.Signed-off-by Ridong Chen <[email protected]>--- mm/trace_vmscan.h | 47 +++++++++++++++++++++++++++++++++++++++++++++++ mm/vmscan.c | 2 ++ 2 files changed, 49 insertions(+)mm/trace_vmscan.h +31
-0@@ -456,6 +456,53 @@ TRACE_EVENT(mm_mglru_scan_folios, __entry->min_seq) ); +TRACE_EVENT(mm_mglru_inc_max_seq, + + TP_PROTO(struct lruvec *lruvec), + + TP_ARGS(lruvec), + + 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) + __field(int, nid) + ), + + TP_fast_assign( + struct lru_gen_folio *lrugen = &lruvec->lrugen; + int gen, zone; + + __entry->memcg_id = mem_cgroup_id(lruvec_memcg(lruvec)); + __entry->max_seq = lrugen->max_seq; + __entry->anon_min_seq = lrugen->min_seq[LRU_GEN_ANON]; + __entry->file_min_seq = lrugen->min_seq[LRU_GEN_FILE]; + /* summed over zones and clamped to >= 0, as debugfs lru_gen does */ + for (gen = 0; gen < MAX_NR_GENS; gen++) { + __entry->nr_anon[gen] = 0; + __entry->nr_file[gen] = 0; + for (zone = 0; zone < MAX_NR_ZONES; zone++) { + __entry->nr_anon[gen] += + max(lrugen->nr_pages[gen][LRU_GEN_ANON][zone], 0L);Quoted 8 lines
mm/trace_vmscan.h TRACE_EVENT(mm_mglru_scan_folios, · line 493
Quoted 4 lines
mm/trace_vmscan.h +4
-0+ __entry->nr_file[gen] += +max (lrugen->nr_pages[gen][LRU_GEN_FILE ][zone],0L ); + } + }I feel this is wrong. Can we calculate the newest and oldest generations from seq, then print them in order from newest to oldest?
Quoted 5 lines
Agree, from newest to oldest or oldest to newest makes it clearer.
Printing from
lrugen->max_seq-MAX_NR_GENS+ 1, or fromMAX_NR_GENS, both looks good.
Hi Barry, Baoquan,
You are right, thanks. In v6 the arrays are ordered by seq, from the oldest to the newest, i.e. the same order in which the debugfs lru_gen file lists the generations from top to bottom.
Quoted 23 lines
Quoted 23 lines
The nr_pages[] array isn't ordered from newest to oldest or vice versa, as we have:
static inline int
lru_gen_from_seq(unsigned long seq){ return seq % MAX_NR_GENS; }And I added a helper here:
https://lore.kernel.org/linux-mm/[email protected]/(quoted diff) +10
-0+static inline unsigned long lruvec_gen_size(struct lru_gen_folio *lrugen, + int type, unsigned long seq) +{ + int gen = lru_gen_from_seq(seq); + unsigned long size = 0; + + for (int zone = 0; zone < MAX_NR_ZONES; zone++) + size += max(READ_ONCE(lrugen->nr_pages[gen][type][zone]), 0L); + return size; +}
Quoted 5 lines
Quoted 5 lines
Maybe you could port it here as a separate patch so it can land earlier as part of your tracepoints series. We could then use it in the next version of the swappiness series.
Sure. I will port lruvec_gen_size() as a separate patch with your Suggested-by, and put it in mm/vmscan.h so the tracepoint can use it.
Signature · 2 lines
Best regards Ridong