All of lore.kernel.org
 help / color / mirror / Atom feed
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 bpf-next 2/2] selftests/bpf: Add mmap/munmap benchmark for array maps
Date: Tue, 21 Jul 2026 23:53:08 -0700	[thread overview]
Message-ID: <20260722065308.4116186-3-song@kernel.org> (raw)
In-Reply-To: <20260722065308.4116186-1-song@kernel.org>

Add a benchmark that repeatedly mmap()s and munmap()s an mmap-able BPF
array map (BPF_F_MMAPABLE) from a configurable number of threads. Both
the number of worker threads and the size of the map are tunable via
--nr-threads and --map-size, defaulting to 16 threads and 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          | 146 ++++++++++++++++++
 .../bpf/benchs/run_bench_arraymap_mmap.sh     |  15 ++
 4 files changed, 166 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..356d80a85cf0
--- /dev/null
+++ b/tools/testing/selftests/bpf/benchs/bench_arraymap_mmap.c
@@ -0,0 +1,146 @@
+// 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 {
+	__u32 nr_threads;
+	__u64 map_size;
+} args = {
+	.nr_threads = 16,
+	.map_size = 8 * 1024 * 1024, /* 8 MiB */
+};
+
+enum {
+	ARG_NR_THREADS = 5000,
+	ARG_MAP_SIZE = 5001,
+};
+
+static const struct argp_option opts[] = {
+	{ "nr-threads", ARG_NR_THREADS, "NR_THREADS", 0,
+		"Number of threads that mmap/munmap the map (default 16)" },
+	{ "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_NR_THREADS:
+		args.nr_threads = strtoul(arg, NULL, 0);
+		if (args.nr_threads == 0) {
+			fprintf(stderr, "Invalid nr-threads: %s\n", arg);
+			argp_usage(state);
+		}
+		break;
+	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);
+	}
+
+	/*
+	 * The number of worker threads is controlled by --nr-threads and
+	 * drives the framework's producer machinery, so per-producer stats
+	 * are reported correctly.
+	 */
+	env.producer_cnt = args.nr_threads;
+}
+
+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, %u threads\n",
+	       max_entries, ctx.mmap_sz, args.nr_threads);
+}
+
+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..3efd441cd653
--- /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 --nr-threads $t --map-size $s arraymap-mmap)"
+	printf "\n"
+done
+done
-- 
2.53.0-Meta


  parent reply	other threads:[~2026-07-22  6:53 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-22  6:53 [PATCH bpf-next 0/2] bpf: Populate mmap-able array maps lazily Song Liu
2026-07-22  6:53 ` [PATCH bpf-next 1/2] bpf: Populate mmap-able array map memory lazily Song Liu
2026-07-22  7:16   ` sashiko-bot
2026-07-22  6:53 ` Song Liu [this message]
2026-07-22  7:05   ` [PATCH bpf-next 2/2] selftests/bpf: Add mmap/munmap benchmark for array maps sashiko-bot

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=20260722065308.4116186-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.