* [PATCH bpf-next 1/3] bpf: Add memory usage for arena
@ 2026-07-16 14:24 Jiayuan Chen
2026-07-16 14:24 ` [PATCH bpf-next 2/3] selftests/bpf: Run arena tests serially Jiayuan Chen
` (3 more replies)
0 siblings, 4 replies; 7+ messages in thread
From: Jiayuan Chen @ 2026-07-16 14:24 UTC (permalink / raw)
To: bpf, memxor
Cc: Jiayuan Chen, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, Eduard Zingerman, Martin KaFai Lau, Song Liu,
Yonghong Song, Jiri Olsa, Emil Tsalapatis, Shuah Khan, Woojin Ji,
Puranjay Mohan, Saket Kumar Bhaskar, Ihor Solodrai, linux-kernel,
linux-kselftest
arena is the only map type whose map_mem_usage() still returns 0, so
"bpftool map show" and fdinfo always showed 0 memlock for an arena no
matter how many pages it had.
Count the pages that are actually mapped into the arena: bump a counter in
apply_range_set_cb() when a page goes in and drop it in
apply_range_clear_cb() when a page goes out, both under the arena spinlock.
map_mem_usage() then just returns nr_pages << PAGE_SHIFT.
Only real data pages are counted, not the scratch page.
Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
---
kernel/bpf/arena.c | 27 ++++++++++++++++-----------
1 file changed, 16 insertions(+), 11 deletions(-)
diff --git a/kernel/bpf/arena.c b/kernel/bpf/arena.c
index 80b7b8a69446..bc3460211604 100644
--- a/kernel/bpf/arena.c
+++ b/kernel/bpf/arena.c
@@ -55,8 +55,10 @@ struct bpf_arena {
struct vm_struct *kern_vm;
struct page *scratch_page;
struct range_tree rt;
- /* protects rt */
+ /* protects rt and nr_pages */
rqspinlock_t spinlock;
+ /* number of pages currently populated in the arena */
+ u64 nr_pages;
struct list_head vma_list;
/* protects vma_list */
struct mutex lock;
@@ -143,14 +145,14 @@ static long compute_pgoff(struct bpf_arena *arena, long uaddr)
}
struct apply_range_data {
+ struct bpf_arena *arena;
struct page **pages;
- struct page *scratch_page;
int i;
};
struct clear_range_data {
+ struct bpf_arena *arena;
struct llist_head *free_pages;
- struct page *scratch_page;
};
static int apply_range_set_cb(pte_t *pte, unsigned long addr, void *data)
@@ -180,7 +182,7 @@ static int apply_range_set_cb(pte_t *pte, unsigned long addr, void *data)
if (pte_none(old))
continue;
- if (WARN_ON_ONCE(pte_page(old) != d->scratch_page))
+ if (WARN_ON_ONCE(pte_page(old) != d->arena->scratch_page))
return -EBUSY;
ptep_get_and_clear(&init_mm, addr, pte);
flush_tlb_before_set(addr);
@@ -196,6 +198,7 @@ static int apply_range_set_cb(pte_t *pte, unsigned long addr, void *data)
set_pte_at(&init_mm, addr, pte, pteval);
#endif
d->i++;
+ d->arena->nr_pages++;
return 0;
}
@@ -227,10 +230,11 @@ static int apply_range_clear_cb(pte_t *pte, unsigned long addr, void *data)
* scratches its PTE. A later bpf_arena_free_pages() over that range walks
* here. Without the skip, scratch_page would be freed.
*/
- if (page == d->scratch_page)
+ if (page == d->arena->scratch_page)
return 0;
__llist_add(&page->pcp_llist, d->free_pages);
+ d->arena->nr_pages--;
return 0;
}
@@ -413,7 +417,9 @@ static int arena_map_check_btf(struct bpf_map *map, const struct btf *btf,
static u64 arena_map_mem_usage(const struct bpf_map *map)
{
- return 0;
+ struct bpf_arena *arena = container_of(map, struct bpf_arena, map);
+
+ return (u64)READ_ONCE(arena->nr_pages) << PAGE_SHIFT;
}
struct vma_list {
@@ -506,8 +512,7 @@ static vm_fault_t arena_vm_fault(struct vm_fault *vmf)
if (ret)
goto out_sigsegv_memcg;
- struct apply_range_data data = { .pages = &page, .i = 0,
- .scratch_page = arena->scratch_page };
+ struct apply_range_data data = { .arena = arena, .pages = &page, .i = 0 };
/* Account into memcg of the process that created bpf_arena */
ret = bpf_map_alloc_pages(map, NUMA_NO_NODE, 1, &page);
if (ret) {
@@ -696,8 +701,8 @@ static long arena_alloc_pages(struct bpf_arena *arena, long uaddr, long page_cnt
bpf_map_memcg_exit(old_memcg, new_memcg);
return 0;
}
+ data.arena = arena;
data.pages = pages;
- data.scratch_page = arena->scratch_page;
if (raw_res_spin_lock_irqsave(&arena->spinlock, flags))
goto out_free_pages;
@@ -873,8 +878,8 @@ static void arena_free_pages(struct bpf_arena *arena, long uaddr, long page_cnt,
range_tree_set(&arena->rt, pgoff, page_cnt);
init_llist_head(&free_pages);
+ cdata.arena = arena;
cdata.free_pages = &free_pages;
- cdata.scratch_page = arena->scratch_page;
/* clear ptes and collect struct pages */
apply_to_existing_page_range(&init_mm, kaddr, page_cnt << PAGE_SHIFT,
apply_range_clear_cb, &cdata);
@@ -981,8 +986,8 @@ static void arena_free_worker(struct work_struct *work)
bpf_map_memcg_enter(&arena->map, &old_memcg, &new_memcg);
init_llist_head(&free_pages);
+ cdata.arena = arena;
cdata.free_pages = &free_pages;
- cdata.scratch_page = arena->scratch_page;
arena_vm_start = bpf_arena_get_kern_vm_start(arena);
user_vm_start = bpf_arena_get_user_vm_start(arena);
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH bpf-next 2/3] selftests/bpf: Run arena tests serially
2026-07-16 14:24 [PATCH bpf-next 1/3] bpf: Add memory usage for arena Jiayuan Chen
@ 2026-07-16 14:24 ` Jiayuan Chen
2026-07-16 15:27 ` sashiko-bot
2026-07-16 14:25 ` [PATCH bpf-next 3/3] selftests/bpf: Add tests for memory usage for arena Jiayuan Chen
` (2 subsequent siblings)
3 siblings, 1 reply; 7+ messages in thread
From: Jiayuan Chen @ 2026-07-16 14:24 UTC (permalink / raw)
To: bpf, memxor
Cc: Jiayuan Chen, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, Eduard Zingerman, Martin KaFai Lau, Song Liu,
Yonghong Song, Jiri Olsa, Emil Tsalapatis, Shuah Khan, Woojin Ji,
Puranjay Mohan, Saket Kumar Bhaskar, Ihor Solodrai, linux-kernel,
linux-kselftest
Every arena reserves a 4GB kernel vmalloc region at creation. On a KASAN
build that eagerly populates ~512MB of shadow memory per arena. Running the
arena tests in parallel keeps several arenas alive at once, which exhausts
the memory of a small CI VM and trips the OOM killer during map creation.
Make the arena tests serial so at most one arena is alive at a time.
Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
---
tools/testing/selftests/bpf/prog_tests/arena_atomics.c | 2 +-
tools/testing/selftests/bpf/prog_tests/arena_direct_value.c | 2 +-
tools/testing/selftests/bpf/prog_tests/arena_htab.c | 2 +-
tools/testing/selftests/bpf/prog_tests/arena_list.c | 2 +-
tools/testing/selftests/bpf/prog_tests/arena_spin_lock.c | 2 +-
tools/testing/selftests/bpf/prog_tests/arena_strsearch.c | 2 +-
tools/testing/selftests/bpf/prog_tests/libarena.c | 2 +-
tools/testing/selftests/bpf/prog_tests/libarena_asan.c | 2 +-
8 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/arena_atomics.c b/tools/testing/selftests/bpf/prog_tests/arena_atomics.c
index d98577a6babc..1ad5d03d07ad 100644
--- a/tools/testing/selftests/bpf/prog_tests/arena_atomics.c
+++ b/tools/testing/selftests/bpf/prog_tests/arena_atomics.c
@@ -222,7 +222,7 @@ static void test_store_release(struct arena_atomics *skel)
"store_release64_result");
}
-void test_arena_atomics(void)
+void serial_test_arena_atomics(void)
{
struct arena_atomics *skel;
int err;
diff --git a/tools/testing/selftests/bpf/prog_tests/arena_direct_value.c b/tools/testing/selftests/bpf/prog_tests/arena_direct_value.c
index 4b4adb3f4b71..01fcf4965ea4 100644
--- a/tools/testing/selftests/bpf/prog_tests/arena_direct_value.c
+++ b/tools/testing/selftests/bpf/prog_tests/arena_direct_value.c
@@ -66,7 +66,7 @@ static void test_arena_direct_value_one_past_end(void)
close(map_fd);
}
-void test_arena_direct_value(void)
+void serial_test_arena_direct_value(void)
{
if (test__start_subtest("one_past_end"))
test_arena_direct_value_one_past_end();
diff --git a/tools/testing/selftests/bpf/prog_tests/arena_htab.c b/tools/testing/selftests/bpf/prog_tests/arena_htab.c
index d69fd2465f53..91ccf0402980 100644
--- a/tools/testing/selftests/bpf/prog_tests/arena_htab.c
+++ b/tools/testing/selftests/bpf/prog_tests/arena_htab.c
@@ -81,7 +81,7 @@ static void test_arena_htab_asm(void)
arena_htab_asm__destroy(skel);
}
-void test_arena_htab(void)
+void serial_test_arena_htab(void)
{
if (test__start_subtest("arena_htab_llvm"))
test_arena_htab_llvm();
diff --git a/tools/testing/selftests/bpf/prog_tests/arena_list.c b/tools/testing/selftests/bpf/prog_tests/arena_list.c
index 4f2866a615ce..2648e06f53d0 100644
--- a/tools/testing/selftests/bpf/prog_tests/arena_list.c
+++ b/tools/testing/selftests/bpf/prog_tests/arena_list.c
@@ -68,7 +68,7 @@ static void test_arena_list_add_del(int cnt, bool nonsleepable)
arena_list__destroy(skel);
}
-void test_arena_list(void)
+void serial_test_arena_list(void)
{
if (test__start_subtest("arena_list_1"))
test_arena_list_add_del(1, false);
diff --git a/tools/testing/selftests/bpf/prog_tests/arena_spin_lock.c b/tools/testing/selftests/bpf/prog_tests/arena_spin_lock.c
index acb9d53b5973..545b05d7a0aa 100644
--- a/tools/testing/selftests/bpf/prog_tests/arena_spin_lock.c
+++ b/tools/testing/selftests/bpf/prog_tests/arena_spin_lock.c
@@ -101,7 +101,7 @@ static void test_arena_spin_lock_size(int size)
return;
}
-void test_arena_spin_lock(void)
+void serial_test_arena_spin_lock(void)
{
repeat = 1000;
if (test__start_subtest("arena_spin_lock_1"))
diff --git a/tools/testing/selftests/bpf/prog_tests/arena_strsearch.c b/tools/testing/selftests/bpf/prog_tests/arena_strsearch.c
index f81a0c066505..0c1c6cbfa0f1 100644
--- a/tools/testing/selftests/bpf/prog_tests/arena_strsearch.c
+++ b/tools/testing/selftests/bpf/prog_tests/arena_strsearch.c
@@ -23,7 +23,7 @@ static void test_arena_str(void)
arena_strsearch__destroy(skel);
}
-void test_arena_strsearch(void)
+void serial_test_arena_strsearch(void)
{
if (test__start_subtest("arena_strsearch"))
test_arena_str();
diff --git a/tools/testing/selftests/bpf/prog_tests/libarena.c b/tools/testing/selftests/bpf/prog_tests/libarena.c
index ba5a5a50f7c0..df7e4b8dc394 100644
--- a/tools/testing/selftests/bpf/prog_tests/libarena.c
+++ b/tools/testing/selftests/bpf/prog_tests/libarena.c
@@ -202,7 +202,7 @@ static void run_libarena_parallel_test(struct libarena *skel, struct bpf_program
run_libarena_parallel_fini(skel, name, prefixlen);
}
-void test_libarena(void)
+void serial_test_libarena(void)
{
struct arena_alloc_reserve_args args;
struct libarena *skel;
diff --git a/tools/testing/selftests/bpf/prog_tests/libarena_asan.c b/tools/testing/selftests/bpf/prog_tests/libarena_asan.c
index f897405f701d..9c31b17dbf39 100644
--- a/tools/testing/selftests/bpf/prog_tests/libarena_asan.c
+++ b/tools/testing/selftests/bpf/prog_tests/libarena_asan.c
@@ -85,7 +85,7 @@ static void run_test(void)
* Run the test depending on whether LLVM can compile arena ASAN
* programs.
*/
-void test_libarena_asan(void)
+void serial_test_libarena_asan(void)
{
#ifdef HAS_BPF_ARENA_ASAN
run_test();
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH bpf-next 3/3] selftests/bpf: Add tests for memory usage for arena
2026-07-16 14:24 [PATCH bpf-next 1/3] bpf: Add memory usage for arena Jiayuan Chen
2026-07-16 14:24 ` [PATCH bpf-next 2/3] selftests/bpf: Run arena tests serially Jiayuan Chen
@ 2026-07-16 14:25 ` Jiayuan Chen
2026-07-16 15:40 ` sashiko-bot
2026-07-16 14:48 ` [PATCH bpf-next 1/3] bpf: Add " sashiko-bot
2026-07-16 15:31 ` bot+bpf-ci
3 siblings, 1 reply; 7+ messages in thread
From: Jiayuan Chen @ 2026-07-16 14:25 UTC (permalink / raw)
To: bpf, memxor
Cc: Jiayuan Chen, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, Eduard Zingerman, Martin KaFai Lau, Song Liu,
Yonghong Song, Jiri Olsa, Emil Tsalapatis, Shuah Khan, Woojin Ji,
Puranjay Mohan, Saket Kumar Bhaskar, Ihor Solodrai, linux-kernel,
linux-kselftest
Allocate and free arena pages, both from BPF and via user-space fault-in,
and check that the map's memlock in fdinfo tracks the number of pages that
are actually populated.
Like the other arena tests it runs serially.
test:
./test_progs -a arena_mem_usage
#5 arena_mem_usage:OK
Summary: 1/0 PASSED, 0 SKIPPED, 0 FAILED
Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
---
.../bpf/prog_tests/arena_mem_usage.c | 122 ++++++++++++++++++
.../selftests/bpf/progs/arena_mem_usage.c | 39 ++++++
2 files changed, 161 insertions(+)
create mode 100644 tools/testing/selftests/bpf/prog_tests/arena_mem_usage.c
create mode 100644 tools/testing/selftests/bpf/progs/arena_mem_usage.c
diff --git a/tools/testing/selftests/bpf/prog_tests/arena_mem_usage.c b/tools/testing/selftests/bpf/prog_tests/arena_mem_usage.c
new file mode 100644
index 000000000000..14c2d1a1d673
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/arena_mem_usage.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <test_progs.h>
+#include <sys/user.h>
+#ifndef PAGE_SIZE /* on some archs it comes in sys/user.h */
+#include <unistd.h>
+#define PAGE_SIZE getpagesize()
+#endif
+
+#include "arena_mem_usage.skel.h"
+
+/*
+ * arena_map_mem_usage() is surfaced to user space through the map's
+ * /proc/<pid>/fdinfo/<fd> "memlock:" line (the same value bpftool map show
+ * prints). Read it directly so the test has no external dependency.
+ */
+static long map_memlock(int map_fd)
+{
+ char path[64], line[128];
+ long memlock = -1;
+ FILE *f;
+
+ snprintf(path, sizeof(path), "/proc/self/fdinfo/%d", map_fd);
+ f = fopen(path, "r");
+ if (!ASSERT_OK_PTR(f, "open_fdinfo"))
+ return -1;
+ while (fgets(line, sizeof(line), f)) {
+ if (sscanf(line, "memlock:\t%ld", &memlock) == 1)
+ break;
+ }
+ fclose(f);
+ ASSERT_NEQ(memlock, -1, "parse_memlock");
+ return memlock;
+}
+
+static int run(struct bpf_program *prog, const char *name)
+{
+ LIBBPF_OPTS(bpf_test_run_opts, opts);
+ int err = bpf_prog_test_run_opts(bpf_program__fd(prog), &opts);
+
+ if (!ASSERT_OK(err, name))
+ return -1;
+ if (!ASSERT_OK(opts.retval, name))
+ return -1;
+ return 0;
+}
+
+void serial_test_arena_mem_usage(void)
+{
+ struct arena_mem_usage *skel;
+ const long ps = PAGE_SIZE;
+ char *base;
+ size_t sz;
+ int fd, i;
+
+ skel = arena_mem_usage__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "open_load"))
+ return;
+ fd = bpf_map__fd(skel->maps.arena);
+
+ /* Fresh arena: no data pages, and the scratch page is not counted. */
+ ASSERT_EQ(map_memlock(fd), 0, "initial");
+
+ /* BPF-side allocation of 17 pages. */
+ skel->bss->alloc_cnt = 17;
+ if (run(skel->progs.alloc, "alloc"))
+ goto out;
+ /*
+ * A NULL ptr means bpf_arena_alloc_pages() itself failed (e.g. the host
+ * is under memory pressure), not a miscount -- flag it distinctly so a
+ * red CI run is not mistaken for a counting bug.
+ */
+ if (!ASSERT_OK_PTR(skel->bss->ptr, "arena_alloc_pages"))
+ goto out;
+ ASSERT_EQ(map_memlock(fd), 17 * ps, "after_alloc");
+
+ /* Free a single page (arena_free_pages page_cnt==1 path). */
+ skel->bss->free_byte_off = 0;
+ skel->bss->free_cnt = 1;
+ if (run(skel->progs.free_pages, "free_one"))
+ goto out;
+ ASSERT_EQ(map_memlock(fd), 16 * ps, "after_free_one");
+
+ /* Free ten pages in one call (bulk path); only the freed pages count. */
+ skel->bss->free_byte_off = 1 * ps;
+ skel->bss->free_cnt = 10;
+ if (run(skel->progs.free_pages, "free_bulk"))
+ goto out;
+ ASSERT_EQ(map_memlock(fd), 6 * ps, "after_free_bulk");
+
+ /* Free the remaining six -> arena empty again. */
+ skel->bss->free_byte_off = 11 * ps;
+ skel->bss->free_cnt = 6;
+ if (run(skel->progs.free_pages, "free_rest"))
+ goto out;
+ ASSERT_EQ(map_memlock(fd), 0, "after_free_rest");
+
+ /*
+ * User-space fault-in: touching unallocated arena pages allocates them
+ * through arena_vm_fault(). libbpf mmap()s the arena at map_extra during
+ * load, so bpf_map__initial_value() hands back that base.
+ */
+ base = bpf_map__initial_value(skel->maps.arena, &sz);
+ if (!ASSERT_OK_PTR(base, "arena_base"))
+ goto out;
+ for (i = 0; i < 8; i++)
+ base[i * ps] = 1;
+ ASSERT_EQ(map_memlock(fd), 8 * ps, "after_faultin");
+
+ /*
+ * Free the faulted-in pages from BPF. They are mapped into the user vma
+ * (elevated refcount), so this also exercises the zap path.
+ */
+ skel->bss->ptr = base;
+ skel->bss->free_byte_off = 0;
+ skel->bss->free_cnt = 8;
+ if (run(skel->progs.free_pages, "free_faulted"))
+ goto out;
+ ASSERT_EQ(map_memlock(fd), 0, "after_free_faulted");
+out:
+ arena_mem_usage__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/progs/arena_mem_usage.c b/tools/testing/selftests/bpf/progs/arena_mem_usage.c
new file mode 100644
index 000000000000..50d9d6ac86f6
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/arena_mem_usage.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include "bpf_arena_common.h"
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARENA);
+ __uint(map_flags, BPF_F_MMAPABLE);
+ __uint(max_entries, 1000); /* number of pages */
+#ifdef __TARGET_ARCH_arm64
+ __ulong(map_extra, 0x1ull << 32); /* start of mmap() region */
+#else
+ __ulong(map_extra, 0x1ull << 44); /* start of mmap() region */
+#endif
+} arena SEC(".maps");
+
+void __arena *ptr;
+int alloc_cnt; /* in: pages to allocate */
+long free_byte_off; /* in: byte offset within ptr to start freeing */
+int free_cnt; /* in: pages to free */
+
+SEC("syscall")
+int alloc(void *ctx)
+{
+ ptr = bpf_arena_alloc_pages(&arena, NULL, alloc_cnt, NUMA_NO_NODE, 0);
+ return ptr ? 0 : 1;
+}
+
+SEC("syscall")
+int free_pages(void *ctx)
+{
+ if (!ptr)
+ return 1;
+ bpf_arena_free_pages(&arena, (char __arena *)ptr + free_byte_off, free_cnt);
+ return 0;
+}
+
+char _license[] SEC("license") = "GPL";
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH bpf-next 1/3] bpf: Add memory usage for arena
2026-07-16 14:24 [PATCH bpf-next 1/3] bpf: Add memory usage for arena Jiayuan Chen
2026-07-16 14:24 ` [PATCH bpf-next 2/3] selftests/bpf: Run arena tests serially Jiayuan Chen
2026-07-16 14:25 ` [PATCH bpf-next 3/3] selftests/bpf: Add tests for memory usage for arena Jiayuan Chen
@ 2026-07-16 14:48 ` sashiko-bot
2026-07-16 15:31 ` bot+bpf-ci
3 siblings, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-07-16 14:48 UTC (permalink / raw)
To: Jiayuan Chen; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [Medium] The `nr_pages` counter is modified using plain writes while being read concurrently locklessly, constituting a data race.
Pre-existing issues:
- [High] Functions that can sleep and acquire sleepable locks are called while holding a raw spinlock with interrupts disabled.
--
--- Patch [1]: [PATCH bpf-next 1/3] bpf: Add memory usage for arena ---
commit 68cbc4739a7a9be56d49db779d319ec002523097
Author: Jiayuan Chen <jiayuan.chen@linux.dev>
Subject: bpf: Add memory usage for arena
This patch adds memory usage tracking for the arena map type by counting
the pages mapped into the arena. It bumps a counter in apply_range_set_cb()
when a page is added, drops it in apply_range_clear_cb() when removed, and
returns the calculated usage in map_mem_usage().
> diff --git a/kernel/bpf/arena.c b/kernel/bpf/arena.c
> index 80b7b8a694464..bc34602116040 100644
> --- a/kernel/bpf/arena.c
> +++ b/kernel/bpf/arena.c
[ ... ]
> @@ -196,6 +198,7 @@ static int apply_range_set_cb(pte_t *pte, unsigned long addr, void *data)
> set_pte_at(&init_mm, addr, pte, pteval);
> #endif
> d->i++;
> + d->arena->nr_pages++;
> return 0;
> }
[Severity: Medium]
Is it safe to use a plain increment here since nr_pages is read concurrently?
In arena_map_mem_usage(), arena->nr_pages is read locklessly via
READ_ONCE(arena->nr_pages). The Linux Kernel Memory Model requires that writes
pairing with a lockless READ_ONCE must be marked with WRITE_ONCE to prevent
the compiler from splitting the writes or assuming no concurrent access.
Could this lead to incorrect statistics or torn writes?
[ ... ]
> @@ -227,10 +230,11 @@ static int apply_range_clear_cb(pte_t *pte, unsigned long addr, void *data)
> * scratches its PTE. A later bpf_arena_free_pages() over that range walks
> * here. Without the skip, scratch_page would be freed.
> */
> - if (page == d->scratch_page)
> + if (page == d->arena->scratch_page)
> return 0;
>
> __llist_add(&page->pcp_llist, d->free_pages);
> + d->arena->nr_pages--;
[Severity: Medium]
Similar to the increment above, does this plain decrement need WRITE_ONCE() to
safely pair with the READ_ONCE() in arena_map_mem_usage()?
[ ... ]
> @@ -506,8 +512,7 @@ static vm_fault_t arena_vm_fault(struct vm_fault *vmf)
> if (ret)
> goto out_sigsegv_memcg;
>
> - struct apply_range_data data = { .pages = &page, .i = 0,
> - .scratch_page = arena->scratch_page };
> + struct apply_range_data data = { .arena = arena, .pages = &page, .i = 0 };
> /* Account into memcg of the process that created bpf_arena */
> ret = bpf_map_alloc_pages(map, NUMA_NO_NODE, 1, &page);
[Severity: High]
This isn't a bug introduced by this patch, but does this function call
sleepable allocators while holding a raw spinlock?
In arena_vm_fault(), the raw spinlock is acquired before this block:
if (raw_res_spin_lock_irqsave(&arena->spinlock, flags))
Then bpf_map_alloc_pages() performs sleepable memory allocations (GFP_USER
with __GFP_DIRECT_RECLAIM). Furthermore, apply_to_page_range() is called
shortly after, which may dynamically allocate kernel page tables using
GFP_PGTABLE_KERNEL while taking init_mm.page_table_lock (a sleepable
spinlock on PREEMPT_RT).
Can this cause a kernel panic (BUG: sleeping function called from invalid
context) or potential deadlocks?
[ ... ]
> @@ -696,8 +701,8 @@ static long arena_alloc_pages(struct bpf_arena *arena, long uaddr, long page_cnt
> bpf_map_memcg_exit(old_memcg, new_memcg);
> return 0;
> }
> + data.arena = arena;
> data.pages = pages;
> - data.scratch_page = arena->scratch_page;
>
> if (raw_res_spin_lock_irqsave(&arena->spinlock, flags))
[Severity: High]
This isn't a bug introduced by this patch, but are sleepable functions also
called inside this atomic section in arena_alloc_pages()?
After the lock is acquired, the code enters a loop that calls
bpf_map_alloc_pages() and apply_to_page_range(), both of which can sleep
or acquire sleepable locks.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260716142746.8794-1-jiayuan.chen@linux.dev?part=1
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH bpf-next 2/3] selftests/bpf: Run arena tests serially
2026-07-16 14:24 ` [PATCH bpf-next 2/3] selftests/bpf: Run arena tests serially Jiayuan Chen
@ 2026-07-16 15:27 ` sashiko-bot
0 siblings, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-07-16 15:27 UTC (permalink / raw)
To: Jiayuan Chen; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Incomplete serialization of BPF arena tests leaves the CI environment vulnerable to OOM.
--
--- Patch [2]: [PATCH bpf-next 2/3] selftests/bpf: Run arena tests serially ---
commit 4661bf4c3b39723c6187b74928c316a1e721ac60
Author: Jiayuan Chen <jiayuan.chen@linux.dev>
selftests/bpf: Run arena tests serially
This commit changes several BPF arena tests to run serially to prevent memory
exhaustion during KASAN builds. By renaming the test functions to start with
serial_test_, the selftests framework ensures at most one arena is alive at a
time.
> Make the arena tests serial so at most one arena is alive at a time.
[Severity: High]
This is a pre-existing issue, but there are other tests that allocate
BPF_MAP_TYPE_ARENA maps which are still running in parallel.
For example, in tools/testing/selftests/bpf/prog_tests/verifier.c:
void test_verifier_arena(void) { RUN(verifier_arena); }
void test_verifier_arena_large(void) { RUN(verifier_arena_large); }
In tools/testing/selftests/bpf/prog_tests/stream.c:
void test_stream_success(void)
{
RUN_TESTS(stream);
return;
}
In tools/testing/selftests/bpf/prog_tests/compute_live_registers.c:
void test_compute_live_registers(void)
{
RUN_TESTS(compute_live_registers);
}
Since these remain prefixed with test_ rather than serial_test_, won't they
still run concurrently and potentially exhaust memory on KASAN builds?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260716142746.8794-1-jiayuan.chen@linux.dev?part=2
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH bpf-next 1/3] bpf: Add memory usage for arena
2026-07-16 14:24 [PATCH bpf-next 1/3] bpf: Add memory usage for arena Jiayuan Chen
` (2 preceding siblings ...)
2026-07-16 14:48 ` [PATCH bpf-next 1/3] bpf: Add " sashiko-bot
@ 2026-07-16 15:31 ` bot+bpf-ci
3 siblings, 0 replies; 7+ messages in thread
From: bot+bpf-ci @ 2026-07-16 15:31 UTC (permalink / raw)
To: jiayuan.chen, bpf, memxor
Cc: jiayuan.chen, ast, daniel, andrii, eddyz87, martin.lau, song,
yonghong.song, jolsa, emil, shuah, random6.xyz, puranjay, skb99,
ihor.solodrai, linux-kernel, linux-kselftest, ast, andrii, daniel,
martin.lau, eddyz87, yonghong.song, clm, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 1207 bytes --]
> bpf: Add memory usage for arena
>
> arena is the only map type whose map_mem_usage() still returns 0, so
> "bpftool map show" and fdinfo always showed 0 memlock for an arena no
> matter how many pages it had.
>
> Count the pages that are actually mapped into the arena: bump a counter in
> apply_range_set_cb() when a page goes in and drop it in
> apply_range_clear_cb() when a page goes out, both under the arena spinlock.
> map_mem_usage() then just returns nr_pages << PAGE_SHIFT.
>
> Only real data pages are counted, not the scratch page.
>
> Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
This looks like a fix for a user-visible bug: arena's map_mem_usage()
always returned 0, so bpftool map show and fdinfo reported 0 memlock
regardless of how many pages the arena had. The returns-0 stub was added
together with kernel/bpf/arena.c. Should this include:
Fixes: 317460317a02 ("bpf: Introduce bpf_arena.")
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/29508297958
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH bpf-next 3/3] selftests/bpf: Add tests for memory usage for arena
2026-07-16 14:25 ` [PATCH bpf-next 3/3] selftests/bpf: Add tests for memory usage for arena Jiayuan Chen
@ 2026-07-16 15:40 ` sashiko-bot
0 siblings, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-07-16 15:40 UTC (permalink / raw)
To: Jiayuan Chen; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The test logic unintentionally bypasses the explicit check for allocation failure, defeating its documented purpose to distinctly flag memory pressure CI failures.
--
--- Patch [3]: [PATCH bpf-next 3/3] selftests/bpf: Add tests for memory usage for arena ---
commit f5d331845302b77cab3c4b06daa82ffb16675b30
Author: Jiayuan Chen <jiayuan.chen@linux.dev>
selftests/bpf: Add tests for memory usage for arena
This commit adds tests to verify that memory usage for BPF arenas is tracked
correctly in the map's fdinfo memlock field. It allocates and frees arena
pages from both BPF and user-space fault-in paths.
> diff --git a/tools/testing/selftests/bpf/prog_tests/arena_mem_usage.c b/tools/testing/selftests/bpf/prog_tests/arena_mem_usage.c
> new file mode 100644
> index 0000000000000..14c2d1a1d673c
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/prog_tests/arena_mem_usage.c
[ ... ]
> +static int run(struct bpf_program *prog, const char *name)
> +{
> + LIBBPF_OPTS(bpf_test_run_opts, opts);
> + int err = bpf_prog_test_run_opts(bpf_program__fd(prog), &opts);
> +
> + if (!ASSERT_OK(err, name))
> + return -1;
> + if (!ASSERT_OK(opts.retval, name))
> + return -1;
> + return 0;
> +}
[ ... ]
> +void serial_test_arena_mem_usage(void)
> +{
[ ... ]
> + /* BPF-side allocation of 17 pages. */
> + skel->bss->alloc_cnt = 17;
> + if (run(skel->progs.alloc, "alloc"))
> + goto out;
> + /*
> + * A NULL ptr means bpf_arena_alloc_pages() itself failed (e.g. the host
> + * is under memory pressure), not a miscount -- flag it distinctly so a
> + * red CI run is not mistaken for a counting bug.
> + */
> + if (!ASSERT_OK_PTR(skel->bss->ptr, "arena_alloc_pages"))
> + goto out;
[Severity: Medium]
Does this logic inadvertently bypass the explicit check for allocation failure?
Looking at the alloc() BPF program in arena_mem_usage.c, it returns 1 on
failure:
SEC("syscall")
int alloc(void *ctx)
{
ptr = bpf_arena_alloc_pages(&arena, NULL, alloc_cnt, NUMA_NO_NODE, 0);
return ptr ? 0 : 1;
}
Because of this, if bpf_arena_alloc_pages() fails, opts.retval will be 1,
causing run() to assert and return -1.
The test will then take the "goto out" path immediately after run(), making
the ASSERT_OK_PTR() check unreachable and defeating its documented purpose
to distinctly flag memory pressure.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260716142746.8794-1-jiayuan.chen@linux.dev?part=3
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-07-16 15:40 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-16 14:24 [PATCH bpf-next 1/3] bpf: Add memory usage for arena Jiayuan Chen
2026-07-16 14:24 ` [PATCH bpf-next 2/3] selftests/bpf: Run arena tests serially Jiayuan Chen
2026-07-16 15:27 ` sashiko-bot
2026-07-16 14:25 ` [PATCH bpf-next 3/3] selftests/bpf: Add tests for memory usage for arena Jiayuan Chen
2026-07-16 15:40 ` sashiko-bot
2026-07-16 14:48 ` [PATCH bpf-next 1/3] bpf: Add " sashiko-bot
2026-07-16 15:31 ` bot+bpf-ci
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.