[PATCH 3/6] mm/page_owner: add open-coded page_owner iterator
From: Zhen Ni
Date: Fri Oct 09 2026 - 09:52:27 EST
Add an open-coded bpf_iter over the pages eligible for page_owner
output, reusing page_owner_next_eligible().
A sleepable BPF program drives it with
bpf_iter_page_owner_new()/next()/destroy(); each next() returns the
page_owner_scan. po_snap is copied inside the page_ext RCU window, so
the program needs no RCU section when it reads it.
Also add bpf_page_owner_stack_snprint(), which renders the stack trace
for a stack_depot handle into a caller-provided buffer.
Signed-off-by: Zhen Ni <zhen.ni@xxxxxxxxxxxx>
---
kernel/bpf/helpers.c | 6 +++++
mm/page_owner_iter.c | 52 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 58 insertions(+)
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index 712dca5a2c5b..23a2d2d71f4d 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -4946,6 +4946,12 @@ BTF_ID_FLAGS(func, bpf_iter_dmabuf_new, KF_ITER_NEW | KF_SLEEPABLE)
BTF_ID_FLAGS(func, bpf_iter_dmabuf_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLEEPABLE)
BTF_ID_FLAGS(func, bpf_iter_dmabuf_destroy, KF_ITER_DESTROY | KF_SLEEPABLE)
#endif
+#ifdef CONFIG_PAGE_OWNER
+BTF_ID_FLAGS(func, bpf_iter_page_owner_new, KF_ITER_NEW | KF_SLEEPABLE)
+BTF_ID_FLAGS(func, bpf_iter_page_owner_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLEEPABLE)
+BTF_ID_FLAGS(func, bpf_iter_page_owner_destroy, KF_ITER_DESTROY | KF_SLEEPABLE)
+BTF_ID_FLAGS(func, bpf_page_owner_stack_snprint, KF_SLEEPABLE)
+#endif
BTF_ID_FLAGS(func, __bpf_trap)
BTF_ID_FLAGS(func, bpf_strcmp, KF_PERFMON);
BTF_ID_FLAGS(func, bpf_strcasecmp, KF_PERFMON);
diff --git a/mm/page_owner_iter.c b/mm/page_owner_iter.c
index 33dc9dcd59de..80693d417407 100644
--- a/mm/page_owner_iter.c
+++ b/mm/page_owner_iter.c
@@ -4,6 +4,7 @@
#include <linux/memblock.h>
#include <linux/mm.h>
#include <linux/seq_file.h>
+#include <linux/stackdepot.h>
#include "page_owner.h"
@@ -114,3 +115,54 @@ int page_owner_iter_register(void)
return bpf_iter_reg_target(&page_owner_iter_reg_info);
}
+
+/* opaque to BPF programs */
+struct bpf_iter_page_owner {
+ __u64 __opaque[16];
+} __attribute__((aligned(8)));
+
+struct bpf_iter_page_owner_kern {
+ struct page_owner_scan scan;
+} __attribute__((aligned(8)));
+
+__bpf_kfunc_start_defs();
+
+__bpf_kfunc int bpf_iter_page_owner_new(struct bpf_iter_page_owner *it)
+{
+ struct bpf_iter_page_owner_kern *kit = (void *)it;
+
+ BUILD_BUG_ON(sizeof(*kit) > sizeof(*it));
+ BUILD_BUG_ON(__alignof__(*kit) != __alignof__(*it));
+
+ kit->scan.pfn = min_low_pfn;
+ kit->scan.page = NULL;
+ return 0;
+}
+
+__bpf_kfunc struct page_owner_scan *
+bpf_iter_page_owner_next(struct bpf_iter_page_owner *it)
+{
+ struct bpf_iter_page_owner_kern *kit = (void *)it;
+
+ cond_resched();
+
+ if (kit->scan.page)
+ kit->scan.pfn++;
+
+ if (!page_owner_next_eligible(&kit->scan))
+ return NULL;
+
+ return &kit->scan;
+}
+
+__bpf_kfunc void bpf_iter_page_owner_destroy(struct bpf_iter_page_owner *it)
+{
+}
+
+__bpf_kfunc int bpf_page_owner_stack_snprint(depot_stack_handle_t handle,
+ char *buf, u32 buf_size)
+{
+ return stack_depot_snprint(handle, buf, buf_size, 0);
+}
+
+__bpf_kfunc_end_defs();
--
2.20.1