From: Song Liu <song@kernel.org>
To: bpf@vger.kernel.org
Cc: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org,
eddyz87@gmail.com, memxor@gmail.com, kernel-team@meta.com,
Song Liu <song@kernel.org>
Subject: [PATCH v2 bpf-next 2/2] selftests/bpf: Add mmap/munmap benchmark for array maps
Date: Wed, 22 Jul 2026 13:50:32 -0700 [thread overview]
Message-ID: <20260722205032.1245094-3-song@kernel.org> (raw)
In-Reply-To: <20260722205032.1245094-1-song@kernel.org>
Add a benchmark that repeatedly mmap()s and munmap()s an mmap-able BPF
array map (BPF_F_MMAPABLE). The number of worker threads is controlled
by the standard -p/--producers option, and the size of the map by
--map-size (default 8MiB).
The benchmark is useful to observe how the cost of mmap()/munmap()
scales with the mapping size, e.g. eager vs lazy page-table population.
Signed-off-by: Song Liu <song@kernel.org>
Assisted-by: Claude:claude-opus-4-8
---
tools/testing/selftests/bpf/Makefile | 1 +
tools/testing/selftests/bpf/bench.c | 4 +
.../bpf/benchs/bench_arraymap_mmap.c | 127 ++++++++++++++++++
.../bpf/benchs/run_bench_arraymap_mmap.sh | 15 +++
4 files changed, 147 insertions(+)
create mode 100644 tools/testing/selftests/bpf/benchs/bench_arraymap_mmap.c
create mode 100755 tools/testing/selftests/bpf/benchs/run_bench_arraymap_mmap.sh
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index 55d394438705..b6593fb1135b 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -1003,6 +1003,7 @@ $(OUTPUT)/bench: $(OUTPUT)/bench.o \
$(OUTPUT)/bench_bpf_timing.o \
$(OUTPUT)/bench_bpf_nop.o \
$(OUTPUT)/bench_xdp_lb.o \
+ $(OUTPUT)/bench_arraymap_mmap.o \
$(OUTPUT)/usdt_1.o \
$(OUTPUT)/usdt_2.o \
#
diff --git a/tools/testing/selftests/bpf/bench.c b/tools/testing/selftests/bpf/bench.c
index 3d9d2cd7764b..192d5fcb7a74 100644
--- a/tools/testing/selftests/bpf/bench.c
+++ b/tools/testing/selftests/bpf/bench.c
@@ -287,6 +287,7 @@ extern struct argp bench_crypto_argp;
extern struct argp bench_sockmap_argp;
extern struct argp bench_lpm_trie_map_argp;
extern struct argp bench_xdp_lb_argp;
+extern struct argp bench_arraymap_mmap_argp;
static const struct argp_child bench_parsers[] = {
{ &bench_ringbufs_argp, 0, "Ring buffers benchmark", 0 },
@@ -304,6 +305,7 @@ static const struct argp_child bench_parsers[] = {
{ &bench_sockmap_argp, 0, "bpf sockmap benchmark", 0 },
{ &bench_lpm_trie_map_argp, 0, "LPM trie map benchmark", 0 },
{ &bench_xdp_lb_argp, 0, "XDP load-balancer benchmark", 0 },
+ { &bench_arraymap_mmap_argp, 0, "arraymap-mmap benchmark", 0 },
{},
};
@@ -582,6 +584,7 @@ extern const struct bench bench_lpm_trie_delete;
extern const struct bench bench_lpm_trie_free;
extern const struct bench bench_bpf_nop;
extern const struct bench bench_xdp_lb;
+extern const struct bench bench_arraymap_mmap;
static const struct bench *benchs[] = {
&bench_count_global,
@@ -665,6 +668,7 @@ static const struct bench *benchs[] = {
&bench_lpm_trie_free,
&bench_bpf_nop,
&bench_xdp_lb,
+ &bench_arraymap_mmap,
};
static void find_benchmark(void)
diff --git a/tools/testing/selftests/bpf/benchs/bench_arraymap_mmap.c b/tools/testing/selftests/bpf/benchs/bench_arraymap_mmap.c
new file mode 100644
index 000000000000..d7c7615e2183
--- /dev/null
+++ b/tools/testing/selftests/bpf/benchs/bench_arraymap_mmap.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Meta Platforms, Inc. */
+#include <argp.h>
+#include <sys/mman.h>
+#include "bench.h"
+
+/* Benchmark mmap()/munmap() of an mmap-able BPF array map from N threads. */
+
+static struct ctx {
+ int map_fd;
+ size_t mmap_sz;
+} ctx;
+
+static struct {
+ __u64 map_size;
+} args = {
+ .map_size = 8 * 1024 * 1024, /* 8 MiB */
+};
+
+enum {
+ ARG_MAP_SIZE = 5000,
+};
+
+static const struct argp_option opts[] = {
+ { "map-size", ARG_MAP_SIZE, "BYTES", 0,
+ "Size of the BPF array map in bytes (default 8388608)" },
+ {},
+};
+
+static error_t parse_arg(int key, char *arg, struct argp_state *state)
+{
+ switch (key) {
+ case ARG_MAP_SIZE:
+ args.map_size = strtoull(arg, NULL, 0);
+ if (args.map_size == 0) {
+ fprintf(stderr, "Invalid map-size: %s\n", arg);
+ argp_usage(state);
+ }
+ break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return 0;
+}
+
+/* exported into benchmark runner */
+const struct argp bench_arraymap_mmap_argp = {
+ .options = opts,
+ .parser = parse_arg,
+};
+
+static void validate(void)
+{
+ if (env.consumer_cnt != 0) {
+ fprintf(stderr, "benchmark doesn't support consumer!\n");
+ exit(1);
+ }
+}
+
+static long hits;
+
+static void *producer(void *input)
+{
+ while (true) {
+ void *addr;
+
+ addr = mmap(NULL, ctx.mmap_sz, PROT_READ | PROT_WRITE,
+ MAP_SHARED, ctx.map_fd, 0);
+ if (addr == MAP_FAILED) {
+ fprintf(stderr, "mmap failed: %d\n", -errno);
+ exit(1);
+ }
+ if (munmap(addr, ctx.mmap_sz)) {
+ fprintf(stderr, "munmap failed: %d\n", -errno);
+ exit(1);
+ }
+ atomic_inc(&hits);
+ }
+
+ return NULL;
+}
+
+static void measure(struct bench_res *res)
+{
+ res->hits = atomic_swap(&hits, 0);
+}
+
+static void setup(void)
+{
+ LIBBPF_OPTS(bpf_map_create_opts, opts, .map_flags = BPF_F_MMAPABLE);
+ long page_sz = sysconf(_SC_PAGESIZE);
+ __u32 value_size = sizeof(__u64);
+ __u32 max_entries;
+
+ setup_libbpf();
+
+ /*
+ * mmap-able array maps round the value size up to 8 bytes and expose
+ * PAGE_ALIGN(max_entries * elem_size) bytes to user space.
+ */
+ max_entries = (args.map_size + value_size - 1) / value_size;
+ ctx.mmap_sz = ((__u64)max_entries * value_size + page_sz - 1) &
+ ~(page_sz - 1);
+
+ ctx.map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "mmap_array",
+ sizeof(__u32), value_size, max_entries,
+ &opts);
+ if (ctx.map_fd < 0) {
+ fprintf(stderr, "failed to create map: %d\n", -errno);
+ exit(1);
+ }
+
+ printf("array map: %u entries, mmap size %zu bytes, %d threads\n",
+ max_entries, ctx.mmap_sz, env.producer_cnt);
+}
+
+const struct bench bench_arraymap_mmap = {
+ .name = "arraymap-mmap",
+ .argp = &bench_arraymap_mmap_argp,
+ .validate = validate,
+ .setup = setup,
+ .producer_thread = producer,
+ .measure = measure,
+ .report_progress = ops_report_progress,
+ .report_final = ops_report_final,
+};
diff --git a/tools/testing/selftests/bpf/benchs/run_bench_arraymap_mmap.sh b/tools/testing/selftests/bpf/benchs/run_bench_arraymap_mmap.sh
new file mode 100755
index 000000000000..22c569311137
--- /dev/null
+++ b/tools/testing/selftests/bpf/benchs/run_bench_arraymap_mmap.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+source ./benchs/run_common.sh
+
+set -eufo pipefail
+
+for t in 1 4 8 16; do
+for s in 1048576 8388608 67108864; do
+subtitle "nr_threads: $t, map_size: $s"
+ summarize_ops "arraymap-mmap: " \
+ "$($RUN_BENCH -p $t --map-size $s arraymap-mmap)"
+ printf "\n"
+done
+done
--
2.53.0-Meta
prev parent reply other threads:[~2026-07-22 20:50 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-22 20:50 [PATCH v2 bpf-next 0/2] bpf: Populate mmap-able array maps lazily Song Liu
2026-07-22 20:50 ` [PATCH v2 bpf-next 1/2] bpf: Populate mmap-able array map memory lazily Song Liu
2026-07-22 20:50 ` Song Liu [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260722205032.1245094-3-song@kernel.org \
--to=song@kernel.org \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=kernel-team@meta.com \
--cc=memxor@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.