| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/08/17 19:43 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"KMSANReasoning": "",
"NeedsKMSAN": false,
"Reasoning": "The patch only modifies BPF selftests and benchmarks under tools/testing/selftests/bpf/. It does not make any functional changes to the core kernel, drivers, or architectures.",
"WorthFuzzing": false
} |
| 1/1 | 2026/08/17 19:43 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit d1a2286caa98fa86503d3a920eed3cdf35a66d83\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Mon Aug 17 19:43:42 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile\nindex d3655a7064826..22e9eb16c1511 100644\n--- a/tools/testing/selftests/bpf/Makefile\n+++ b/tools/testing/selftests/bpf/Makefile\n@@ -826,18 +826,22 @@ LIBARENA_MAKE_ARGS = \\\n \t\tBPF_TARGET_ENDIAN=\"$(BPF_TARGET_ENDIAN)\" \\\n \t\tQ=\"$(Q)\"\n \n-LIBARENA_BPF_DEPS := $(wildcard libarena/Makefile\t\t\\\n-\t\t\t\t libarena/include/*\t\t\\\n-\t\t\t\t libarena/include/libarena/*\t\\\n-\t\t\t\t libarena/src/*\t\t\t\\\n-\t\t\t\t libarena/selftests/*\t\t\\\n-\t\t\t\t libarena/*.bpf.o)\n+LIBARENA_COMMON_DEPS := $(wildcard libarena/Makefile\t\t\\\n+\t\t\t\t libarena/include/*\t\t\\\n+\t\t\t\t libarena/include/libarena/*\t\\\n+\t\t\t\t libarena/src/*)\n+LIBARENA_BPF_DEPS := $(LIBARENA_COMMON_DEPS) $(wildcard libarena/selftests/*)\n+LIBARENA_BENCH_BPF_DEPS := $(LIBARENA_COMMON_DEPS) $(wildcard libarena/benchs/*)\n \n LIBARENA_SKEL := libarena/libarena.skel.h\n+LIBARENA_BENCH_SKEL := libarena/libarena_bench.skel.h\n \n $(LIBARENA_SKEL): $(INCLUDE_DIR)/vmlinux.h $(BPFOBJ) $(LIBARENA_BPF_DEPS)\n \t+$(MAKE) -C libarena libarena.skel.h $(LIBARENA_MAKE_ARGS)\n \n+$(LIBARENA_BENCH_SKEL): $(INCLUDE_DIR)/vmlinux.h $(BPFOBJ) $(LIBARENA_BENCH_BPF_DEPS) | $(LIBARENA_SKEL)\n+\t+$(MAKE) -C libarena benchmarks $(LIBARENA_MAKE_ARGS)\n+\n ifneq ($(CLANG_HAS_ARENA_ASAN),)\n LIBARENA_ASAN_SKEL := libarena/libarena_asan.skel.h\n CFLAGS += -DHAS_BPF_ARENA_ASAN\n@@ -987,6 +991,7 @@ $(OUTPUT)/bench_sockmap.o: $(OUTPUT)/bench_sockmap_prog.skel.h\n $(OUTPUT)/bench_lpm_trie_map.o: $(OUTPUT)/lpm_trie_bench.skel.h $(OUTPUT)/lpm_trie_map.skel.h\n $(OUTPUT)/bench_bpf_nop.o: $(OUTPUT)/bpf_nop_bench.skel.h bench_bpf_timing.h\n $(OUTPUT)/bench_xdp_lb.o: $(OUTPUT)/xdp_lb_bench.skel.h bench_bpf_timing.h\n+$(OUTPUT)/bench_libarena.o: $(LIBARENA_BENCH_SKEL)\n $(OUTPUT)/bench_bpf_timing.o: bench_bpf_timing.h\n $(OUTPUT)/bench.o: bench.h testing_helpers.h $(BPFOBJ)\n $(OUTPUT)/bench: LDLIBS += -lm\n@@ -1014,6 +1019,7 @@ $(OUTPUT)/bench: $(OUTPUT)/bench.o \\\n \t\t $(OUTPUT)/bench_bpf_timing.o \\\n \t\t $(OUTPUT)/bench_bpf_nop.o \\\n \t\t $(OUTPUT)/bench_xdp_lb.o \\\n+\t\t $(OUTPUT)/bench_libarena.o \\\n \t\t $(OUTPUT)/usdt_1.o \\\n \t\t $(OUTPUT)/usdt_2.o \\\n \t\t #\ndiff --git a/tools/testing/selftests/bpf/bench.c b/tools/testing/selftests/bpf/bench.c\nindex b86b73456d3ca..de672f61d4abe 100644\n--- a/tools/testing/selftests/bpf/bench.c\n+++ b/tools/testing/selftests/bpf/bench.c\n@@ -288,6 +288,7 @@ extern struct argp bench_crypto_argp;\n extern struct argp bench_sockmap_argp;\n extern struct argp bench_lpm_trie_map_argp;\n extern struct argp bench_xdp_lb_argp;\n+extern struct argp bench_libarena_argp;\n \n static const struct argp_child bench_parsers[] = {\n \t{ \u0026bench_ringbufs_argp, 0, \"Ring buffers benchmark\", 0 },\n@@ -306,6 +307,7 @@ static const struct argp_child bench_parsers[] = {\n \t{ \u0026bench_sockmap_argp, 0, \"bpf sockmap benchmark\", 0 },\n \t{ \u0026bench_lpm_trie_map_argp, 0, \"LPM trie map benchmark\", 0 },\n \t{ \u0026bench_xdp_lb_argp, 0, \"XDP load-balancer benchmark\", 0 },\n+\t{ \u0026bench_libarena_argp, 0, \"libarena allocator benchmark\", 0 },\n \t{},\n };\n \n@@ -585,6 +587,8 @@ extern const struct bench bench_lpm_trie_delete;\n extern const struct bench bench_lpm_trie_free;\n extern const struct bench bench_bpf_nop;\n extern const struct bench bench_xdp_lb;\n+extern const struct bench bench_libarena_malloc;\n+extern const struct bench bench_libarena_calloc;\n \n static const struct bench *benchs[] = {\n \t\u0026bench_count_global,\n@@ -669,6 +673,8 @@ static const struct bench *benchs[] = {\n \t\u0026bench_lpm_trie_free,\n \t\u0026bench_bpf_nop,\n \t\u0026bench_xdp_lb,\n+\t\u0026bench_libarena_malloc,\n+\t\u0026bench_libarena_calloc,\n };\n \n static void find_benchmark(void)\ndiff --git a/tools/testing/selftests/bpf/benchs/bench_libarena.c b/tools/testing/selftests/bpf/benchs/bench_libarena.c\nnew file mode 100644\nindex 0000000000000..24e432244bf5d\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/benchs/bench_libarena.c\n@@ -0,0 +1,210 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */\n+#include \u003cargp.h\u003e\n+#include \u003climits.h\u003e\n+#include \u003cstring.h\u003e\n+\n+#include \"bench.h\"\n+\n+#include \u003clibarena/common.h\u003e\n+#include \u003clibarena/asan.h\u003e\n+#include \u003clibarena/buddy.h\u003e\n+#include \u003clibarena/userspace.h\u003e\n+\n+#include \"libarena/libarena_bench.skel.h\"\n+\n+static struct {\n+\t__u64 alloc_size;\n+\t__u64 nallocs;\n+} args = {\n+\t.alloc_size = 64,\n+\t.nallocs = 10000,\n+};\n+\n+static struct {\n+\tstruct libarena_bench *skel;\n+\tint bench_fd;\n+\tint reset_fd;\n+} ctx;\n+\n+enum {\n+\tARG_LIBARENA_ALLOC_SIZE = 12000,\n+\tARG_LIBARENA_NALLOCS,\n+};\n+\n+static const struct argp_option opts[] = {\n+\t{ \"alloc_size\", ARG_LIBARENA_ALLOC_SIZE, \"BYTES\", 0,\n+\t \"Size of each arena allocation\" },\n+\t{ \"nallocs\", ARG_LIBARENA_NALLOCS, \"ITERS\", 0,\n+\t \"Number of allocation per measurement\" },\n+\t{},\n+};\n+\n+static error_t parse_arg(int key, char *arg, struct argp_state *state)\n+{\n+\tunsigned long value;\n+\n+\tswitch (key) {\n+\tcase ARG_LIBARENA_ALLOC_SIZE:\n+\t\tvalue = strtoull(arg, NULL, 10);\n+\t\tif (!value || value \u003e= UINT_MAX) {\n+\t\t\tfprintf(stderr, \"invalid alloc_size: %ld\", value);\n+\t\t\targp_usage(state);\n+\t\t}\n+\t\targs.alloc_size = value;\n+\t\tbreak;\n+\tcase ARG_LIBARENA_NALLOCS:\n+\t\targs.nallocs = strtoull(arg, NULL, 10);\n+\t\tbreak;\n+\tdefault:\n+\t\treturn ARGP_ERR_UNKNOWN;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+const struct argp bench_libarena_argp = {\n+\t.options = opts,\n+\t.parser = parse_arg,\n+};\n+\n+static void validate(void)\n+{\n+\tif (env.consumer_cnt != 0) {\n+\t\tfprintf(stderr, \"benchmark doesn't support consumers\\n\");\n+\t\texit(1);\n+\t}\n+\n+\tif (env.producer_cnt != 1) {\n+\t\tfprintf(stderr, \"benchmark supports exactly one producer\\n\");\n+\t\texit(1);\n+\t}\n+}\n+\n+static void setup_common(void)\n+{\n+\tstruct arena_alloc_reserve_args reserve_args = {\n+\t\t.nr_pages = ARENA_RESERVE_PAGES_DFL,\n+\t};\n+\tint err;\n+\n+\tsetup_libbpf();\n+\n+\tctx.skel = libarena_bench__open_and_load();\n+\tif (!ctx.skel) {\n+\t\tfprintf(stderr, \"failed to open and load skeleton\\n\");\n+\t\texit(1);\n+\t}\n+\n+\terr = libarena_run_prog_args(\n+\t\tbpf_program__fd(ctx.skel-\u003eprogs.arena_alloc_reserve),\n+\t\t\u0026reserve_args, sizeof(reserve_args));\n+\tif (err) {\n+\t\tfprintf(stderr, \"failed to reserve arena pages: %d\\n\", err);\n+\t\texit(1);\n+\t}\n+\n+\terr = libarena_run_prog(\n+\t\tbpf_program__fd(ctx.skel-\u003eprogs.arena_buddy_reset));\n+\tif (err) {\n+\t\tfprintf(stderr, \"failed to initialize arena allocator: %d\\n\", err);\n+\t\texit(1);\n+\t}\n+\n+\tctx.skel-\u003ebss-\u003ebench_alloc_size = args.alloc_size;\n+\tctx.skel-\u003ebss-\u003ebench_nallocs = args.nallocs;\n+\tctx.reset_fd = bpf_program__fd(ctx.skel-\u003eprogs.arena_buddy_reset);\n+}\n+\n+static void malloc_setup(void)\n+{\n+\tsetup_common();\n+\tctx.bench_fd = bpf_program__fd(ctx.skel-\u003eprogs.bench_malloc);\n+}\n+\n+static void calloc_setup(void)\n+{\n+\tsetup_common();\n+\tctx.bench_fd = bpf_program__fd(ctx.skel-\u003eprogs.bench_calloc);\n+}\n+\n+static void *producer(void *input)\n+{\n+\tint err;\n+\n+\twhile (true) {\n+\t\terr = libarena_run_prog(ctx.bench_fd);\n+\t\tif (err) {\n+\t\t\tfprintf(stderr, \"libarena benchmark failed: %d\\n\", err);\n+\t\t\texit(1);\n+\t\t}\n+\n+\t\terr = libarena_run_prog(ctx.reset_fd);\n+\t\tif (err) {\n+\t\t\tfprintf(stderr, \"libarena alloc reset failed: %d\\n\", err);\n+\t\t\texit(1);\n+\t\t}\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+static void measure(struct bench_res *res)\n+{\n+\tres-\u003eduration_ns = atomic_swap(\u0026ctx.skel-\u003ebss-\u003ebench_duration_ns, 0);\n+\tres-\u003ehits = atomic_swap(\u0026ctx.skel-\u003ebss-\u003ebench_hits, 0);\n+}\n+\n+static void report_progress(int iter, struct bench_res *res, long delta_ns)\n+{\n+\tdouble latency_ns = 0.0;\n+\n+\tif (res-\u003ehits)\n+\t\tlatency_ns = res-\u003eduration_ns / (double)res-\u003ehits;\n+\n+\tprintf(\"Iter %3d (%7.3lfus): latency %8.3lf ns/op (%ld allocations)\\n\",\n+\t iter, (delta_ns - 1000000000) / 1000.0, latency_ns, res-\u003ehits);\n+}\n+\n+static void report_final(struct bench_res res[], int res_cnt)\n+{\n+\tunsigned long duration_ns = 0;\n+\tlong hits = 0;\n+\tint i;\n+\n+\tfor (i = 0; i \u003c res_cnt; i++) {\n+\t\tduration_ns += res[i].duration_ns;\n+\t\thits += res[i].hits;\n+\t}\n+\n+\tif (!hits || !res_cnt) {\n+\t\tprintf(\"Summary: no runs measured\\n\");\n+\t\treturn;\n+\t}\n+\n+\tprintf(\"Summary: %.3lf ns/op, %.0lf invocations for %u allocations/invocation)\\n\",\n+\t duration_ns / (double)hits, hits / (double)res_cnt,\n+\t ctx.skel-\u003ebss-\u003ebench_nallocs);\n+}\n+\n+const struct bench bench_libarena_malloc = {\n+\t.name = \"libarena-malloc\",\n+\t.argp = \u0026bench_libarena_argp,\n+\t.validate = validate,\n+\t.setup = malloc_setup,\n+\t.producer_thread = producer,\n+\t.measure = measure,\n+\t.report_progress = report_progress,\n+\t.report_final = report_final,\n+};\n+\n+const struct bench bench_libarena_calloc = {\n+\t.name = \"libarena-calloc\",\n+\t.argp = \u0026bench_libarena_argp,\n+\t.validate = validate,\n+\t.setup = calloc_setup,\n+\t.producer_thread = producer,\n+\t.measure = measure,\n+\t.report_progress = report_progress,\n+\t.report_final = report_final,\n+};\ndiff --git a/tools/testing/selftests/bpf/benchs/run_bench_libarena.sh b/tools/testing/selftests/bpf/benchs/run_bench_libarena.sh\nnew file mode 100755\nindex 0000000000000..10afe4d52ebf7\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/benchs/run_bench_libarena.sh\n@@ -0,0 +1,31 @@\n+#!/bin/bash\n+# SPDX-License-Identifier: GPL-2.0\n+\n+source ./benchs/run_common.sh\n+\n+set -eufo pipefail\n+\n+RUN_BENCH=\"./bench -d3 -q\"\n+\n+summarize_libarena()\n+{\n+\tlocal bench=\"$1\"\n+\tlocal summary\n+\n+\tsummary=$(printf '%s\\n' \"$2\" | tail -n1)\n+\tsummary=${summary#Summary: }\n+\tprintf \"%-20s %s\\n\" \"$bench\" \"$summary\"\n+}\n+\n+header \"libarena sequential malloc\\n\"\n+\n+for size in 16 64 256 1024 4096; do\n+subtitle \"allocation size: $size\"\n+printf \"\\t-------------------\\n\"\n+\tfor nallocs in 10 50 100 500 1000 5000 10000; do\n+\t\tsummarize_libarena \"malloc:\" \\\n+\t\t\t\"$($RUN_BENCH --alloc_size \"$size\" --nallocs \"$nallocs\" libarena-malloc)\"\n+\t\tsummarize_libarena \"calloc:\" \\\n+\t\t\t\"$($RUN_BENCH --alloc_size \"$size\" --nallocs \"$nallocs\" libarena-calloc)\"\n+\tdone\n+done\ndiff --git a/tools/testing/selftests/bpf/libarena/Makefile b/tools/testing/selftests/bpf/libarena/Makefile\nindex 5e2ab514805e4..91164d43bd612 100644\n--- a/tools/testing/selftests/bpf/libarena/Makefile\n+++ b/tools/testing/selftests/bpf/libarena/Makefile\n@@ -27,10 +27,17 @@ BPFDIR=$(abspath $(LIBARENA)/..)\n INCLUDE_DIR ?= $(BPFDIR)/tools/include\n LIBBPF_INCLUDE ?= $(INCLUDE_DIR)\n \n-# Scan src/ and selftests/ to generate the final binaries\n-LIBARENA_SOURCES = $(wildcard $(LIBARENA)/src/*.bpf.c) $(wildcard $(LIBARENA)/selftests/*.bpf.c)\n-LIBARENA_OBJECTS = $(notdir $(LIBARENA_SOURCES:.bpf.c=.bpf.o))\n-LIBARENA_OBJECTS_ASAN = $(notdir $(LIBARENA_SOURCES:.bpf.c=_asan.bpf.o))\n+# Build selftests and benchmarks into separate BPF objects and skeletons.\n+LIBARENA_CORE_SOURCES = $(wildcard $(LIBARENA)/src/*.bpf.c)\n+LIBARENA_TEST_SOURCES = $(wildcard $(LIBARENA)/selftests/*.bpf.c)\n+LIBARENA_BENCH_SOURCES = $(wildcard $(LIBARENA)/benchs/*.bpf.c)\n+\n+LIBARENA_OBJECTS = $(notdir $(LIBARENA_CORE_SOURCES:.bpf.c=.bpf.o) \\\n+\t\t\t $(LIBARENA_TEST_SOURCES:.bpf.c=.bpf.o))\n+LIBARENA_OBJECTS_ASAN = $(notdir $(LIBARENA_CORE_SOURCES:.bpf.c=_asan.bpf.o) \\\n+\t\t\t\t $(LIBARENA_TEST_SOURCES:.bpf.c=_asan.bpf.o))\n+LIBARENA_BENCH_OBJECTS = $(notdir $(LIBARENA_CORE_SOURCES:.bpf.c=.bpf.o) \\\n+\t\t\t\t $(LIBARENA_BENCH_SOURCES:.bpf.c=.bpf.o))\n \n INCLUDES = -I$(LIBARENA)/include -I$(BPFDIR)\n ifneq ($(INCLUDE_DIR),)\n@@ -53,17 +60,25 @@ override BPF_CFLAGS += -O2 -g\n override BPF_CFLAGS += -Wno-incompatible-pointer-types-discards-qualifiers\n # Required for suppressing harmless vmlinux.h-related warnings.\n override BPF_CFLAGS += -Wno-missing-declarations\n+override BPF_CFLAGS += -fno-strict-aliasing\n override BPF_CFLAGS += $(INCLUDES)\n \n CFLAGS = -O2 -no-pie\n CFLAGS += $(INCLUDES)\n \n-vpath %.bpf.c $(LIBARENA)/src $(LIBARENA)/selftests\n-vpath %.c $(LIBARENA)/src $(LIBARENA)/selftests\n+vpath %.bpf.c $(LIBARENA)/src $(LIBARENA)/selftests $(LIBARENA)/benchs\n+vpath %.c $(LIBARENA)/src $(LIBARENA)/selftests $(LIBARENA)/benchs\n \n skeletons: libarena.skel.h libarena_asan.skel.h\n .PHONY: skeletons\n \n+benchmarks: libarena_bench.skel.h\n+.PHONY: benchmarks\n+\n+libarena_bench.skel.h: libarena_bench.bpf.o\n+\t$(call msg,GEN-SKEL,libarena,$@)\n+\t$(Q)$(BPFTOOL) gen skeleton $\u003c name \"libarena_bench\" \u003e $@\n+\n libarena_asan.skel.h: libarena_asan.bpf.o\n \t$(call msg,GEN-SKEL,libarena,$@)\n \t$(Q)$(BPFTOOL) gen skeleton $\u003c name \"libarena_asan\" \u003e $@\n@@ -80,6 +95,10 @@ libarena.bpf.o: $(LIBARENA_OBJECTS)\n \t$(call msg,GEN-OBJ,libarena,$@)\n \t$(Q)$(BPFTOOL) gen object $@ $^\n \n+libarena_bench.bpf.o: $(LIBARENA_BENCH_OBJECTS)\n+\t$(call msg,GEN-OBJ,libarena,$@)\n+\t$(Q)$(BPFTOOL) gen object $@ $^\n+\n %_asan.bpf.o: %.bpf.c\n \t$(call msg,CLNG-BPF,libarena,$@)\n \t$(Q)$(CLANG) $(BPF_CFLAGS) $(ASAN_FLAGS) -DBPF_ARENA_ASAN $(BPF_TARGET_ENDIAN) -c $\u003c -o $@\ndiff --git a/tools/testing/selftests/bpf/libarena/benchs/bench_malloc.bpf.c b/tools/testing/selftests/bpf/libarena/benchs/bench_malloc.bpf.c\nnew file mode 100644\nindex 0000000000000..e06397dc9a40d\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/libarena/benchs/bench_malloc.bpf.c\n@@ -0,0 +1,51 @@\n+// SPDX-License-Identifier: LGPL-2.1 OR BSD-2-Clause\n+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */\n+#include \u003clibarena/common.h\u003e\n+\n+#include \u003clibarena/asan.h\u003e\n+#include \u003clibarena/buddy.h\u003e\n+\n+u32 bench_alloc_size;\n+u32 bench_nallocs;\n+long bench_hits;\n+long bench_duration_ns;\n+\n+SEC(\"syscall\")\n+int bench_malloc(void)\n+{\n+\tvoid __arena *mem;\n+\tu64 start_ns;\n+\tu32 i;\n+\n+\tstart_ns = bpf_ktime_get_ns();\n+\tfor (i = zero; i \u003c bench_nallocs \u0026\u0026 can_loop; i++) {\n+\t\tmem = arena_malloc(bench_alloc_size);\n+\t\tif (!mem)\n+\t\t\treturn -ENOMEM;\n+\t}\n+\n+\t__sync_add_and_fetch(\u0026bench_duration_ns,\n+\t\t\t bpf_ktime_get_ns() - start_ns);\n+\t__sync_add_and_fetch(\u0026bench_hits, i);\n+\treturn 0;\n+}\n+\n+SEC(\"syscall\")\n+int bench_calloc(void)\n+{\n+\tvoid __arena *mem;\n+\tu64 start_ns;\n+\tu32 i;\n+\n+\tstart_ns = bpf_ktime_get_ns();\n+\tfor (i = zero; i \u003c bench_nallocs \u0026\u0026 can_loop; i++) {\n+\t\tmem = arena_calloc(1, bench_alloc_size);\n+\t\tif (!mem)\n+\t\t\treturn -ENOMEM;\n+\t}\n+\n+\t__sync_add_and_fetch(\u0026bench_duration_ns,\n+\t\t\t bpf_ktime_get_ns() - start_ns);\n+\t__sync_add_and_fetch(\u0026bench_hits, i);\n+\treturn 0;\n+}\ndiff --git a/tools/testing/selftests/bpf/libarena/include/bpf_arena_spin_lock.h b/tools/testing/selftests/bpf/libarena/include/bpf_arena_spin_lock.h\nindex ae6b72d15bb67..872fa20f29a59 100644\n--- a/tools/testing/selftests/bpf/libarena/include/bpf_arena_spin_lock.h\n+++ b/tools/testing/selftests/bpf/libarena/include/bpf_arena_spin_lock.h\n@@ -1,4 +1,4 @@\n-// SPDX-License-Identifier: GPL-2.0\n+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause\n /* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */\n #ifndef BPF_ARENA_SPIN_LOCK_H\n #define BPF_ARENA_SPIN_LOCK_H\ndiff --git a/tools/testing/selftests/bpf/libarena/include/bpf_atomic.h b/tools/testing/selftests/bpf/libarena/include/bpf_atomic.h\nindex 43c306e17f198..65582b2010ad4 100644\n--- a/tools/testing/selftests/bpf/libarena/include/bpf_atomic.h\n+++ b/tools/testing/selftests/bpf/libarena/include/bpf_atomic.h\n@@ -1,4 +1,4 @@\n-// SPDX-License-Identifier: GPL-2.0\n+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause\n /* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */\n #ifndef BPF_ATOMIC_H\n #define BPF_ATOMIC_H\ndiff --git a/tools/testing/selftests/bpf/libarena/include/bpf_may_goto.h b/tools/testing/selftests/bpf/libarena/include/bpf_may_goto.h\nindex 9ba90689d6ba2..b32a420d4e1ad 100644\n--- a/tools/testing/selftests/bpf/libarena/include/bpf_may_goto.h\n+++ b/tools/testing/selftests/bpf/libarena/include/bpf_may_goto.h\n@@ -1,3 +1,4 @@\n+// SPDX-License-Identifier: LGPL-2.1 OR BSD-2-Clause\n #pragma once\n \n /*\ndiff --git a/tools/testing/selftests/bpf/libarena/include/libarena/bitmap.h b/tools/testing/selftests/bpf/libarena/include/libarena/bitmap.h\nindex e2431ea6fdd68..163e2b83d9431 100644\n--- a/tools/testing/selftests/bpf/libarena/include/libarena/bitmap.h\n+++ b/tools/testing/selftests/bpf/libarena/include/libarena/bitmap.h\n@@ -1,5 +1,8 @@\n+// SPDX-License-Identifier: LGPL-2.1 OR BSD-2-Clause\n #pragma once\n \n+#include \u003cbpf_atomic.h\u003e\n+\n #define BITS_PER_BYTE\t\t8\n #define BYTES_TO_BITS(nb)\t((nb) * BITS_PER_BYTE)\n \n@@ -15,11 +18,8 @@ struct arena_bitmap {\n struct arena_bitmap __arena *bmp_alloc(size_t bits);\n void bmp_free(struct arena_bitmap __arena *bmp);\n \n-void __bmp_set_bit(u32 bit, struct arena_bitmap __arena *bmp);\n-void __bmp_clear_bit(u32 bit, struct arena_bitmap __arena *bmp);\n void bmp_set_bit(u32 bit, struct arena_bitmap __arena *bmp);\n void bmp_clear_bit(u32 bit, struct arena_bitmap __arena *bmp);\n-bool bmp_test_bit(u32 bit, struct arena_bitmap __arena *bmp);\n bool bmp_test_and_clear_bit(u32 bit, struct arena_bitmap __arena *bmp);\n bool bmp_test_and_set_bit(u32 bit, struct arena_bitmap __arena *bmp);\n \n@@ -32,3 +32,27 @@ void bmp_copy(size_t bits, struct arena_bitmap __arena *dst, struct arena_bitmap\n bool bmp_intersects(size_t bits, struct arena_bitmap __arena *arg1, struct arena_bitmap __arena *arg2);\n bool bmp_subset(size_t bits, struct arena_bitmap __arena *big, struct arena_bitmap __arena *small);\n void bmp_print(size_t bits, struct arena_bitmap __arena *bmp);\n+\n+static __always_inline\n+void __bmp_set_bit(u32 bit, struct arena_bitmap __arena *bmp)\n+{\n+\tvolatile u64 __arena *word = \u0026bmp-\u003ebits[BIT_WORD(bit)];\n+\n+\t*word |= BIT_MASK(bit);\n+}\n+\n+static __always_inline\n+void __bmp_clear_bit(u32 bit, struct arena_bitmap __arena *bmp)\n+{\n+\tvolatile u64 __arena *word = \u0026bmp-\u003ebits[BIT_WORD(bit)];\n+\n+\t*word \u0026= ~BIT_MASK(bit);\n+}\n+\n+static __always_inline\n+bool bmp_test_bit(u32 bit, struct arena_bitmap __arena *bmp)\n+{\n+\tu64 word = READ_ONCE(bmp-\u003ebits[BIT_WORD(bit)]);\n+\n+\treturn word \u0026 BIT_MASK(bit);\n+}\ndiff --git a/tools/testing/selftests/bpf/libarena/include/libarena/common.h b/tools/testing/selftests/bpf/libarena/include/libarena/common.h\nindex 931ace9a49e2b..a6220355a6975 100644\n--- a/tools/testing/selftests/bpf/libarena/include/libarena/common.h\n+++ b/tools/testing/selftests/bpf/libarena/include/libarena/common.h\n@@ -49,6 +49,7 @@ extern volatile u64 asan_violated;\n int arena_fls(__u64 word);\n \n void __arena *arena_malloc(size_t size);\n+void __arena *arena_calloc(size_t ncount, size_t size);\n void arena_free(void __arena *ptr);\n \n /*\n@@ -61,6 +62,76 @@ void arena_free(void __arena *ptr);\n */\n #define arena_subprog_init() do { asm volatile (\"\" :: \"r\"(\u0026arena)); } while (0)\n \n+/*\n+ * BPF does not currently support the memset intrinsics. for large\n+ * sequential copies, or assignments of large data structures,\n+ * the frontend will generate an intrinsic that causes the BPF\n+ * backend to exit due to a missing implementation. Provide\n+ * implementations for the intrinsic.\n+ */\n+static inline int arena_memset(s8 __arena *dst, s8 val, size_t size)\n+{\n+\tsize_t headalign;\n+\tsize_t tailalign;\n+\tu8 uval = (u8)val;\n+\tsize_t val64;\n+\tsize_t i;\n+\n+\t/*\n+\t * Calculate how many bytes to the next word-aligned one.\n+\t * We get this by truncating the 2s complement of the\n+\t * pointer to the last 3 bits. Intuitively, since\n+\t *\n+\t * The N LSBs of dst and -dst add to 1 \u003c\u003c N, which\n+\t * is why dst + (-dst) = 0x0ULL through overflow. So the\n+\t * last N = 3 bits of the negative are the number of\n+\t * bytes to align dst on the last 3 bits.\n+\t *\n+\t */\n+\theadalign = -(u64)dst \u0026 (sizeof(u64) - 1);\n+\tif (!headalign || size \u003c headalign)\n+\t\tgoto ptraligned;\n+\n+\tfor (i = zero; i \u003c headalign \u0026\u0026 can_loop; i++)\n+\t\tdst[i] = uval;\n+\n+\tdst += headalign;\n+\tsize -= headalign;\n+\n+ptraligned:\n+\n+\t/*\n+\t * Make a word with all bytes equal to the byte we are setting.\n+\t * Since 1 byte -\u003e 2 hex digits.\n+\t *\n+\t * Shifting the value by a 0 bytes is equal to multiplication by 0x01\n+\t * Shifting by 1 bytes is equal to multiplication by 0x01 \u003c\u003c 8,\n+\t * ...\n+\t * Shifting by 7 bytes is equal to multiplication by 0x01 \u003c\u003c 56.\n+\t *\n+\t * End operation to replicate the byte into all the bytes of a word\n+\t * is (since a | b = a + b when a \u0026 b == 0):\n+\t *\n+\t * val + val * (1UL \u003c\u003c 8) + val * (1UL \u003c\u003c 16) + .. + val * (1UL \u003c\u003c 56)\n+\t * = val * (1UL \u003c\u003c 56 + 1UL \u003c\u003c 48 + ... + 1UL \u003c\u003c 0)\n+\t * = val * (0x01UL \u003c\u003c 56 | 0x01UL \u003c\u003c 48 + ... + 1UL \u003c\u003c 0)\n+\t * = val * 0x0101 0101 0101 0101\n+\t */\n+\tval64 = (u8)val * 0x0101010101010101ULL;\n+\n+\t/* Pointer is now aligned, use word-aligned assignments. */\n+\tfor (i = zero; i \u003c size / sizeof(u64) \u0026\u0026 can_loop; i++)\n+\t\t((u64 __arena *)dst)[i] = val64;\n+\n+\t/* Go back to byte-aligned for the tail. */\n+\ttailalign = size % sizeof(u64);\n+\tdst += size - tailalign;\n+\tfor (i = zero; i \u003c tailalign \u0026\u0026 can_loop; i++)\n+\t\tdst[i] = uval;\n+\n+\treturn 0;\n+}\n+\n #else /* ! __BPF__ */\n \n #include \u003cstdint.h\u003e\ndiff --git a/tools/testing/selftests/bpf/libarena/selftests/test_bitmap.bpf.c b/tools/testing/selftests/bpf/libarena/selftests/test_bitmap.bpf.c\nindex 76319a529f02b..e66b3a26ca3d6 100644\n--- a/tools/testing/selftests/bpf/libarena/selftests/test_bitmap.bpf.c\n+++ b/tools/testing/selftests/bpf/libarena/selftests/test_bitmap.bpf.c\n@@ -1,3 +1,6 @@\n+// SPDX-License-Identifier: LGPL-2.1 OR BSD-2-Clause\n+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */\n+\n #include \u003clibarena/common.h\u003e\n \n #include \u003clibarena/asan.h\u003e\ndiff --git a/tools/testing/selftests/bpf/libarena/src/asan.bpf.c b/tools/testing/selftests/bpf/libarena/src/asan.bpf.c\nindex 5135d5c72a46d..de656b69d13af 100644\n--- a/tools/testing/selftests/bpf/libarena/src/asan.bpf.c\n+++ b/tools/testing/selftests/bpf/libarena/src/asan.bpf.c\n@@ -103,23 +103,6 @@ volatile bool asan_inited = false;\n */\n volatile bool asan_report_once = false;\n \n-/*\n- * BPF does not currently support the memset/memcpy/memcmp intrinsics.\n- * For large sequential copies, or assignments of large data structures,\n- * the frontend will generate an intrinsic that causes the BPF backend\n- * to exit due to a missing implementation. Provide a simple implementation\n- * just for memset to use it for poisoning/unpoisoning the map.\n- */\n-__weak int asan_memset(s8 __arena *dst, s8 val, size_t size)\n-{\n-\tsize_t i;\n-\n-\tfor (i = zero; i \u003c size \u0026\u0026 can_loop; i++)\n-\t\tdst[i] = val;\n-\n-\treturn 0;\n-}\n-\n /* Validate a 1-byte access, always within a single byte. */\n static __always_inline bool memory_is_poisoned_1(s8 __arena *addr)\n {\n@@ -422,7 +405,7 @@ __hidden __noasan int asan_poison(void __arena *addr, s8 val, size_t size)\n \tshadow = mem_to_shadow(addr);\n \tlen = size \u003e\u003e ASAN_SHADOW_SHIFT;\n \n-\tasan_memset(shadow, val, len);\n+\tarena_memset(shadow, val, len);\n \n \treturn 0;\n }\n@@ -463,7 +446,7 @@ __hidden __noasan int asan_unpoison(void __arena *addr, size_t size)\n \tshadow = mem_to_shadow(addr);\n \tlen = size \u003e\u003e ASAN_SHADOW_SHIFT;\n \n-\tasan_memset(shadow, 0, len);\n+\tarena_memset(shadow, 0, len);\n \n \t/*\n \t * If we are allocating a non-granule aligned region, we need to adjust\ndiff --git a/tools/testing/selftests/bpf/libarena/src/bitmap.bpf.c b/tools/testing/selftests/bpf/libarena/src/bitmap.bpf.c\nindex 5ff8e688ddc76..0390f20ce366d 100644\n--- a/tools/testing/selftests/bpf/libarena/src/bitmap.bpf.c\n+++ b/tools/testing/selftests/bpf/libarena/src/bitmap.bpf.c\n@@ -34,24 +34,6 @@ void bmp_free(struct arena_bitmap __arena *bmp)\n \tarena_free(bmp);\n }\n \n-__weak\n-void __bmp_set_bit(u32 bit, struct arena_bitmap __arena *bmp)\n-{\n-\tbmp-\u003ebits[BIT_WORD(bit)] |= BIT_MASK(bit);\n-}\n-\n-__weak\n-void __bmp_clear_bit(u32 bit, struct arena_bitmap __arena *bmp)\n-{\n-\tbmp-\u003ebits[BIT_WORD(bit)] \u0026= ~BIT_MASK(bit);\n-}\n-\n-__weak\n-bool bmp_test_bit(u32 bit, struct arena_bitmap __arena *bmp)\n-{\n-\treturn bmp-\u003ebits[BIT_WORD(bit)] \u0026 BIT_MASK(bit);\n-}\n-\n __weak\n bool bmp_test_and_clear_bit(u32 bit, struct arena_bitmap __arena *bmp)\n {\ndiff --git a/tools/testing/selftests/bpf/libarena/src/buddy.bpf.c b/tools/testing/selftests/bpf/libarena/src/buddy.bpf.c\nindex c674ee5cfcc1d..2490ab1396de0 100644\n--- a/tools/testing/selftests/bpf/libarena/src/buddy.bpf.c\n+++ b/tools/testing/selftests/bpf/libarena/src/buddy.bpf.c\n@@ -5,6 +5,8 @@\n #include \u003clibarena/asan.h\u003e\n #include \u003clibarena/buddy.h\u003e\n \n+#include \u003cbpf_arena_spin_lock.h\u003e\n+\n /*\n * Buddy allocator arena-based implementation.\n *\n@@ -45,15 +47,8 @@ enum {\n \tBUDDY_CHUNK_PAGES\t= BUDDY_CHUNK_BYTES / __PAGE_SIZE\n };\n \n-static inline int buddy_lock(struct buddy __arena *buddy)\n-{\n-\treturn arena_spin_lock(\u0026buddy-\u003elock);\n-}\n-\n-static inline void buddy_unlock(struct buddy __arena *buddy)\n-{\n-\tarena_spin_unlock(\u0026buddy-\u003elock);\n-}\n+#define buddy_lock(buddy, flags) (arena_spin_lock_irqsave(\u0026(buddy)-\u003elock, (flags)))\n+#define buddy_unlock(buddy, flags) (arena_spin_unlock_irqrestore(\u0026(buddy)-\u003elock, (flags)))\n \n /*\n * Reserve part of the arena address space for the allocator. We use\n@@ -385,6 +380,7 @@ static struct buddy_chunk __arena *buddy_chunk_get(struct buddy __arena *buddy)\n {\n \tu64 order, ord, min_order, max_order;\n \tstruct buddy_chunk __arena *chunk;\n+\tunsigned long flags;\n \tsize_t left;\n \tint power2;\n \tu64 vaddr;\n@@ -416,7 +412,7 @@ static struct buddy_chunk __arena *buddy_chunk_get(struct buddy __arena *buddy)\n \t\treturn NULL;\n \t}\n \n-\tif (buddy_lock(buddy)) {\n+\tif (buddy_lock(buddy, flags)) {\n \t\t/*\n \t\t * We cannot reclaim the vaddr space, but that is ok - this\n \t\t * operation should always succeed. The error path is to catch\n@@ -520,7 +516,7 @@ static struct buddy_chunk __arena *buddy_chunk_get(struct buddy __arena *buddy)\n \t\t\tarena_stderr(\n \t\t\t\t\"chunk has size of 0x%lx bytes (left %lx bytes)\\n\",\n \t\t\t\tsizeof(*chunk), left);\n-\t\t\tbuddy_unlock(buddy);\n+\t\t\tbuddy_unlock(buddy, flags);\n \n \t\t\treturn NULL;\n \t\t}\n@@ -531,7 +527,7 @@ static struct buddy_chunk __arena *buddy_chunk_get(struct buddy __arena *buddy)\n \t\torder = (power2 \u003e= BUDDY_MIN_ALLOC_SHIFT) ? power2 - BUDDY_MIN_ALLOC_SHIFT : 0;\n \n \t\tif (idx_set_allocated(chunk, idx, true)) {\n-\t\t\tbuddy_unlock(buddy);\n+\t\t\tbuddy_unlock(buddy, flags);\n \t\t\treturn NULL;\n \t\t}\n \n@@ -547,7 +543,7 @@ static struct buddy_chunk __arena *buddy_chunk_get(struct buddy __arena *buddy)\n \t\t */\n \t\tmin_order = left ? order + 1 : order;\n \t\tif (add_leftovers_to_freelist(chunk, idx, min_order, max_order)) {\n-\t\t\tbuddy_unlock(buddy);\n+\t\t\tbuddy_unlock(buddy, flags);\n \t\t\treturn NULL;\n \t\t}\n \n@@ -556,7 +552,7 @@ static struct buddy_chunk __arena *buddy_chunk_get(struct buddy __arena *buddy)\n \t\tmax_order = order;\n \t}\n \n-\tbuddy_unlock(buddy);\n+\tbuddy_unlock(buddy, flags);\n \n \treturn chunk;\n }\n@@ -564,6 +560,7 @@ static struct buddy_chunk __arena *buddy_chunk_get(struct buddy __arena *buddy)\n __weak int buddy_init(struct buddy __arena *buddy)\n {\n \tstruct buddy_chunk __arena *chunk;\n+\tunsigned long flags;\n \tint ret;\n \n \tif (!asan_ready())\n@@ -579,7 +576,7 @@ __weak int buddy_init(struct buddy __arena *buddy)\n \n \tchunk = buddy_chunk_get(buddy);\n \n-\tif (buddy_lock(buddy)) {\n+\tif (buddy_lock(buddy, flags)) {\n \t\tbpf_arena_free_pages(\u0026arena, chunk, BUDDY_CHUNK_PAGES);\n \t\treturn -EINVAL;\n \t}\n@@ -591,7 +588,7 @@ __weak int buddy_init(struct buddy __arena *buddy)\n \t/* Put the chunk at the beginning of the list. */\n \tbuddy-\u003efirst_chunk = chunk;\n \n-\tbuddy_unlock(buddy);\n+\tbuddy_unlock(buddy, flags);\n \n \treturn chunk ? 0 : -ENOMEM;\n }\n@@ -730,9 +727,10 @@ static u64 buddy_alloc_from_existing_chunks(struct buddy __arena *buddy, int ord\n */\n static u64 buddy_alloc_from_new_chunk(struct buddy __arena *buddy, struct buddy_chunk __arena *chunk, int order)\n {\n+\tunsigned long flags;\n \tu64 address;\n \n-\tif (buddy_lock(buddy))\n+\tif (buddy_lock(buddy, flags))\n \t\treturn (u64)NULL;\n \n \n@@ -745,7 +743,7 @@ static u64 buddy_alloc_from_new_chunk(struct buddy __arena *buddy, struct buddy_\n \n \taddress = buddy_chunk_alloc(buddy-\u003efirst_chunk, order);\n \n-\tbuddy_unlock(buddy);\n+\tbuddy_unlock(buddy, flags);\n \n \treturn (u64)address;\n }\n@@ -754,6 +752,7 @@ void __arena *buddy_alloc(struct buddy __arena *buddy, size_t size)\n {\n \tvoid __arena *address = NULL;\n \tstruct buddy_chunk __arena *chunk;\n+\tunsigned long flags;\n \tint order;\n \n \tif (!buddy)\n@@ -765,11 +764,11 @@ void __arena *buddy_alloc(struct buddy __arena *buddy, size_t size)\n \t\treturn NULL;\n \t}\n \n-\tif (buddy_lock(buddy))\n+\tif (buddy_lock(buddy, flags))\n \t\treturn NULL;\n \n \taddress = (u8 __arena *)buddy_alloc_from_existing_chunks(buddy, order);\n-\tbuddy_unlock(buddy);\n+\tbuddy_unlock(buddy, flags);\n \tif (address)\n \t\tgoto done;\n \n@@ -880,6 +879,7 @@ static __always_inline int buddy_free_unlocked(struct buddy __arena *buddy, u64\n \n __weak int buddy_free(struct buddy __arena *buddy, void __arena *addr)\n {\n+\tunsigned long flags;\n \tint ret;\n \n \tif (!buddy)\n@@ -889,13 +889,13 @@ __weak int buddy_free(struct buddy __arena *buddy, void __arena *addr)\n \tif (!addr)\n \t\treturn 0;\n \n-\tret = buddy_lock(buddy);\n+\tret = buddy_lock(buddy, flags);\n \tif (ret)\n \t\treturn ret;\n \n \tret = buddy_free_unlocked(buddy, (u64)addr);\n \n-\tbuddy_unlock(buddy);\n+\tbuddy_unlock(buddy, flags);\n \n \treturn ret;\n }\ndiff --git a/tools/testing/selftests/bpf/libarena/src/common.bpf.c b/tools/testing/selftests/bpf/libarena/src/common.bpf.c\nindex 569f0f64d518b..785d4872cb494 100644\n--- a/tools/testing/selftests/bpf/libarena/src/common.bpf.c\n+++ b/tools/testing/selftests/bpf/libarena/src/common.bpf.c\n@@ -1,5 +1,7 @@\n // SPDX-License-Identifier: LGPL-2.1 OR BSD-2-Clause\n /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */\n+#include \u003climits.h\u003e\n+\n #include \u003clibarena/common.h\u003e\n #include \u003clibarena/asan.h\u003e\n #include \u003clibarena/buddy.h\u003e\n@@ -48,10 +50,33 @@ __weak void __arena *arena_malloc(size_t size)\n \treturn buddy_alloc(\u0026buddy, size);\n }\n \n+__weak void __arena *arena_calloc(size_t ncount, size_t size)\n+{\n+\tvoid __arena *mem;\n+\tsize_t total;\n+\n+\t/*\n+\t * Ideally we'd be using __builtin_mul_overflow here,\n+\t * but the BPF compiler backend doesn't implement __multi3.\n+\t * There are ways to optimize the division away from the\n+\t * overflow check, but any costs are dwarfed by the\n+\t * buddy_alloc() call. Keep it simple for now.\n+\t */\n+\tif (unlikely(ncount \u0026\u0026 size \u003e= ULLONG_MAX / ncount))\n+\t\treturn NULL;\n+\n+\ttotal = ncount * size;\n+\n+\tmem = buddy_alloc(\u0026buddy, total);\n+\tif (likely(mem))\n+\t\tarena_memset(mem, 0, total);\n+\n+\treturn mem;\n+}\n+\n __weak void arena_free(void __arena *ptr)\n {\n \tbuddy_free(\u0026buddy, ptr);\n }\n \n-\n char _license[] SEC(\"license\") = \"GPL\";\n"
} |
| 2/1 | 2026/08/17 19:43 | agent | patch-evaluator |
0mModel:best-expensive Args: null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch only modifies BPF selftests and benchmarks under tools/testing/selftests/bpf/. It does not make any functional changes to the core kernel, drivers, or architectures.",
"WorthFuzzing": false
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). - Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed, ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU. - Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove) that are executed only during PCI hot-unplug or sysfs driver unbind operations. If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. Prefer non-static, non-inlined API entrypoint functions over internal static helper functions (which are inlined by the compiler and do not have distinct symbol addresses). 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit d1a2286caa98fa86503d3a920eed3cdf35a66d83
Author: syz-cluster <triage@syzkaller.com>
Date: Mon Aug 17 19:43:42 2026 +0000
syz-cluster: applied patch under review
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index d3655a7064826..22e9eb16c1511 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -826,18 +826,22 @@ LIBARENA_MAKE_ARGS = \
BPF_TARGET_ENDIAN="$(BPF_TARGET_ENDIAN)" \
Q="$(Q)"
-LIBARENA_BPF_DEPS := $(wildcard libarena/Makefile \
- libarena/include/* \
- libarena/include/libarena/* \
- libarena/src/* \
- libarena/selftests/* \
- libarena/*.bpf.o)
+LIBARENA_COMMON_DEPS := $(wildcard libarena/Makefile \
+ libarena/include/* \
+ libarena/include/libarena/* \
+ libarena/src/*)
+LIBARENA_BPF_DEPS := $(LIBARENA_COMMON_DEPS) $(wildcard libarena/selftests/*)
+LIBARENA_BENCH_BPF_DEPS := $(LIBARENA_COMMON_DEPS) $(wildcard libarena/benchs/*)
LIBARENA_SKEL := libarena/libarena.skel.h
+LIBARENA_BENCH_SKEL := libarena/libarena_bench.skel.h
$(LIBARENA_SKEL): $(INCLUDE_DIR)/vmlinux.h $(BPFOBJ) $(LIBARENA_BPF_DEPS)
+$(MAKE) -C libarena libarena.skel.h $(LIBARENA_MAKE_ARGS)
+$(LIBARENA_BENCH_SKEL): $(INCLUDE_DIR)/vmlinux.h $(BPFOBJ) $(LIBARENA_BENCH_BPF_DEPS) | $(LIBARENA_SKEL)
+ +$(MAKE) -C libarena benchmarks $(LIBARENA_MAKE_ARGS)
+
ifneq ($(CLANG_HAS_ARENA_ASAN),)
LIBARENA_ASAN_SKEL := libarena/libarena_asan.skel.h
CFLAGS += -DHAS_BPF_ARENA_ASAN
@@ -987,6 +991,7 @@ $(OUTPUT)/bench_sockmap.o: $(OUTPUT)/bench_sockmap_prog.skel.h
$(OUTPUT)/bench_lpm_trie_map.o: $(OUTPUT)/lpm_trie_bench.skel.h $(OUTPUT)/lpm_trie_map.skel.h
$(OUTPUT)/bench_bpf_nop.o: $(OUTPUT)/bpf_nop_bench.skel.h bench_bpf_timing.h
$(OUTPUT)/bench_xdp_lb.o: $(OUTPUT)/xdp_lb_bench.skel.h bench_bpf_timing.h
+$(OUTPUT)/bench_libarena.o: $(LIBARENA_BENCH_SKEL)
$(OUTPUT)/bench_bpf_timing.o: bench_bpf_timing.h
$(OUTPUT)/bench.o: bench.h testing_helpers.h $(BPFOBJ)
$(OUTPUT)/bench: LDLIBS += -lm
@@ -1014,6 +1019,7 @@ $(OUTPUT)/bench: $(OUTPUT)/bench.o \
$(OUTPUT)/bench_bpf_timing.o \
$(OUTPUT)/bench_bpf_nop.o \
$(OUTPUT)/bench_xdp_lb.o \
+ $(OUTPUT)/bench_libarena.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 b86b73456d3ca..de672f61d4abe 100644
--- a/tools/testing/selftests/bpf/bench.c
+++ b/tools/testing/selftests/bpf/bench.c
@@ -288,6 +288,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_libarena_argp;
static const struct argp_child bench_parsers[] = {
{ &bench_ringbufs_argp, 0, "Ring buffers benchmark", 0 },
@@ -306,6 +307,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_libarena_argp, 0, "libarena allocator benchmark", 0 },
{},
};
@@ -585,6 +587,8 @@ 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_libarena_malloc;
+extern const struct bench bench_libarena_calloc;
static const struct bench *benchs[] = {
&bench_count_global,
@@ -669,6 +673,8 @@ static const struct bench *benchs[] = {
&bench_lpm_trie_free,
&bench_bpf_nop,
&bench_xdp_lb,
+ &bench_libarena_malloc,
+ &bench_libarena_calloc,
};
static void find_benchmark(void)
diff --git a/tools/testing/selftests/bpf/benchs/bench_libarena.c b/tools/testing/selftests/bpf/benchs/bench_libarena.c
new file mode 100644
index 0000000000000..24e432244bf5d
--- /dev/null
+++ b/tools/testing/selftests/bpf/benchs/bench_libarena.c
@@ -0,0 +1,210 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+#include <argp.h>
+#include <limits.h>
+#include <string.h>
+
+#include "bench.h"
+
+#include <libarena/common.h>
+#include <libarena/asan.h>
+#include <libarena/buddy.h>
+#include <libarena/userspace.h>
+
+#include "libarena/libarena_bench.skel.h"
+
+static struct {
+ __u64 alloc_size;
+ __u64 nallocs;
+} args = {
+ .alloc_size = 64,
+ .nallocs = 10000,
+};
+
+static struct {
+ struct libarena_bench *skel;
+ int bench_fd;
+ int reset_fd;
+} ctx;
+
+enum {
+ ARG_LIBARENA_ALLOC_SIZE = 12000,
+ ARG_LIBARENA_NALLOCS,
+};
+
+static const struct argp_option opts[] = {
+ { "alloc_size", ARG_LIBARENA_ALLOC_SIZE, "BYTES", 0,
+ "Size of each arena allocation" },
+ { "nallocs", ARG_LIBARENA_NALLOCS, "ITERS", 0,
+ "Number of allocation per measurement" },
+ {},
+};
+
+static error_t parse_arg(int key, char *arg, struct argp_state *state)
+{
+ unsigned long value;
+
+ switch (key) {
+ case ARG_LIBARENA_ALLOC_SIZE:
+ value = strtoull(arg, NULL, 10);
+ if (!value || value >= UINT_MAX) {
+ fprintf(stderr, "invalid alloc_size: %ld", value);
+ argp_usage(state);
+ }
+ args.alloc_size = value;
+ break;
+ case ARG_LIBARENA_NALLOCS:
+ args.nallocs = strtoull(arg, NULL, 10);
+ break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return 0;
+}
+
+const struct argp bench_libarena_argp = {
+ .options = opts,
+ .parser = parse_arg,
+};
+
+static void validate(void)
+{
+ if (env.consumer_cnt != 0) {
+ fprintf(stderr, "benchmark doesn't support consumers\n");
+ exit(1);
+ }
+
+ if (env.producer_cnt != 1) {
+ fprintf(stderr, "benchmark supports exactly one producer\n");
+ exit(1);
+ }
+}
+
+static void setup_common(void)
+{
+ struct arena_alloc_reserve_args reserve_args = {
+ .nr_pages = ARENA_RESERVE_PAGES_DFL,
+ };
+ int err;
+
+ setup_libbpf();
+
+ ctx.skel = libarena_bench__open_and_load();
+ if (!ctx.skel) {
+ fprintf(stderr, "failed to open and load skeleton\n");
+ exit(1);
+ }
+
+ err = libarena_run_prog_args(
+ bpf_program__fd(ctx.skel->progs.arena_alloc_reserve),
+ &reserve_args, sizeof(reserve_args));
+ if (err) {
+ fprintf(stderr, "failed to reserve arena pages: %d\n", err);
+ exit(1);
+ }
+
+ err = libarena_run_prog(
+ bpf_program__fd(ctx.skel->progs.arena_buddy_reset));
+ if (err) {
+ fprintf(stderr, "failed to initialize arena allocator: %d\n", err);
+ exit(1);
+ }
+
+ ctx.skel->bss->bench_alloc_size = args.alloc_size;
+ ctx.skel->bss->bench_nallocs = args.nallocs;
+ ctx.reset_fd = bpf_program__fd(ctx.skel->progs.arena_buddy_reset);
+}
+
+static void malloc_setup(void)
+{
+ setup_common();
+ ctx.bench_fd = bpf_program__fd(ctx.skel->progs.bench_malloc);
+}
+
+static void calloc_setup(void)
+{
+ setup_common();
+ ctx.bench_fd = bpf_program__fd(ctx.skel->progs.bench_calloc);
+}
+
+static void *producer(void *input)
+{
+ int err;
+
+ while (true) {
+ err = libarena_run_prog(ctx.bench_fd);
+ if (err) {
+ fprintf(stderr, "libarena benchmark failed: %d\n", err);
+ exit(1);
+ }
+
+ err = libarena_run_prog(ctx.reset_fd);
+ if (err) {
+ fprintf(stderr, "libarena alloc reset failed: %d\n", err);
+ exit(1);
+ }
+ }
+
+ return NULL;
+}
+
+static void measure(struct bench_res *res)
+{
+ res->duration_ns = atomic_swap(&ctx.skel->bss->bench_duration_ns, 0);
+ res->hits = atomic_swap(&ctx.skel->bss->bench_hits, 0);
+}
+
+static void report_progress(int iter, struct bench_res *res, long delta_ns)
+{
+ double latency_ns = 0.0;
+
+ if (res->hits)
+ latency_ns = res->duration_ns / (double)res->hits;
+
+ printf("Iter %3d (%7.3lfus): latency %8.3lf ns/op (%ld allocations)\n",
+ iter, (delta_ns - 1000000000) / 1000.0, latency_ns, res->hits);
+}
+
+static void report_final(struct bench_res res[], int res_cnt)
+{
+ unsigned long duration_ns = 0;
+ long hits = 0;
+ int i;
+
+ for (i = 0; i < res_cnt; i++) {
+ duration_ns += res[i].duration_ns;
+ hits += res[i].hits;
+ }
+
+ if (!hits || !res_cnt) {
+ printf("Summary: no runs measured\n");
+ return;
+ }
+
+ printf("Summary: %.3lf ns/op, %.0lf invocations for %u allocations/invocation)\n",
+ duration_ns / (double)hits, hits / (double)res_cnt,
+ ctx.skel->bss->bench_nallocs);
+}
+
+const struct bench bench_libarena_malloc = {
+ .name = "libarena-malloc",
+ .argp = &bench_libarena_argp,
+ .validate = validate,
+ .setup = malloc_setup,
+ .producer_thread = producer,
+ .measure = measure,
+ .report_progress = report_progress,
+ .report_final = report_final,
+};
+
+const struct bench bench_libarena_calloc = {
+ .name = "libarena-calloc",
+ .argp = &bench_libarena_argp,
+ .validate = validate,
+ .setup = calloc_setup,
+ .producer_thread = producer,
+ .measure = measure,
+ .report_progress = report_progress,
+ .report_final = report_final,
+};
diff --git a/tools/testing/selftests/bpf/benchs/run_bench_libarena.sh b/tools/testing/selftests/bpf/benchs/run_bench_libarena.sh
new file mode 100755
index 0000000000000..10afe4d52ebf7
--- /dev/null
+++ b/tools/testing/selftests/bpf/benchs/run_bench_libarena.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+source ./benchs/run_common.sh
+
+set -eufo pipefail
+
+RUN_BENCH="./bench -d3 -q"
+
+summarize_libarena()
+{
+ local bench="$1"
+ local summary
+
+ summary=$(printf '%s\n' "$2" | tail -n1)
+ summary=${summary#Summary: }
+ printf "%-20s %s\n" "$bench" "$summary"
+}
+
+header "libarena sequential malloc\n"
+
+for size in 16 64 256 1024 4096; do
+subtitle "allocation size: $size"
+printf "\t-------------------\n"
+ for nallocs in 10 50 100 500 1000 5000 10000; do
+ summarize_libarena "malloc:" \
+ "$($RUN_BENCH --alloc_size "$size" --nallocs "$nallocs" libarena-malloc)"
+ summarize_libarena "calloc:" \
+ "$($RUN_BENCH --alloc_size "$size" --nallocs "$nallocs" libarena-calloc)"
+ done
+done
diff --git a/tools/testing/selftests/bpf/libarena/Makefile b/tools/testing/selftests/bpf/libarena/Makefile
index 5e2ab514805e4..91164d43bd612 100644
--- a/tools/testing/selftests/bpf/libarena/Makefile
+++ b/tools/testing/selftests/bpf/libarena/Makefile
@@ -27,10 +27,17 @@ BPFDIR=$(abspath $(LIBARENA)/..)
INCLUDE_DIR ?= $(BPFDIR)/tools/include
LIBBPF_INCLUDE ?= $(INCLUDE_DIR)
-# Scan src/ and selftests/ to generate the final binaries
-LIBARENA_SOURCES = $(wildcard $(LIBARENA)/src/*.bpf.c) $(wildcard $(LIBARENA)/selftests/*.bpf.c)
-LIBARENA_OBJECTS = $(notdir $(LIBARENA_SOURCES:.bpf.c=.bpf.o))
-LIBARENA_OBJECTS_ASAN = $(notdir $(LIBARENA_SOURCES:.bpf.c=_asan.bpf.o))
+# Build selftests and benchmarks into separate BPF objects and skeletons.
+LIBARENA_CORE_SOURCES = $(wildcard $(LIBARENA)/src/*.bpf.c)
+LIBARENA_TEST_SOURCES = $(wildcard $(LIBARENA)/selftests/*.bpf.c)
+LIBARENA_BENCH_SOURCES = $(wildcard $(LIBARENA)/benchs/*.bpf.c)
+
+LIBARENA_OBJECTS = $(notdir $(LIBARENA_CORE_SOURCES:.bpf.c=.bpf.o) \
+ $(LIBARENA_TEST_SOURCES:.bpf.c=.bpf.o))
+LIBARENA_OBJECTS_ASAN = $(notdir $(LIBARENA_CORE_SOURCES:.bpf.c=_asan.bpf.o) \
+ $(LIBARENA_TEST_SOURCES:.bpf.c=_asan.bpf.o))
+LIBARENA_BENCH_OBJECTS = $(notdir $(LIBARENA_CORE_SOURCES:.bpf.c=.bpf.o) \
+ $(LIBARENA_BENCH_SOURCES:.bpf.c=.bpf.o))
INCLUDES = -I$(LIBARENA)/include -I$(BPFDIR)
ifneq ($(INCLUDE_DIR),)
@@ -53,17 +60,25 @@ override BPF_CFLAGS += -O2 -g
override BPF_CFLAGS += -Wno-incompatible-pointer-types-discards-qualifiers
# Required for suppressing harmless vmlinux.h-related warnings.
override BPF_CFLAGS += -Wno-missing-declarations
+override BPF_CFLAGS += -fno-strict-aliasing
override BPF_CFLAGS += $(INCLUDES)
CFLAGS = -O2 -no-pie
CFLAGS += $(INCLUDES)
-vpath %.bpf.c $(LIBARENA)/src $(LIBARENA)/selftests
-vpath %.c $(LIBARENA)/src $(LIBARENA)/selftests
+vpath %.bpf.c $(LIBARENA)/src $(LIBARENA)/selftests $(LIBARENA)/benchs
+vpath %.c $(LIBARENA)/src $(LIBARENA)/selftests $(LIBARENA)/benchs
skeletons: libarena.skel.h libarena_asan.skel.h
.PHONY: skeletons
+benchmarks: libarena_bench.skel.h
+.PHONY: benchmarks
+
+libarena_bench.skel.h: libarena_bench.bpf.o
+ $(call msg,GEN-SKEL,libarena,$@)
+ $(Q)$(BPFTOOL) gen skeleton $< name "libarena_bench" > $@
+
libarena_asan.skel.h: libarena_asan.bpf.o
$(call msg,GEN-SKEL,libarena,$@)
$(Q)$(BPFTOOL) gen skeleton $< name "libarena_asan" > $@
@@ -80,6 +95,10 @@ libarena.bpf.o: $(LIBARENA_OBJECTS)
$(call msg,GEN-OBJ,libarena,$@)
$(Q)$(BPFTOOL) gen object $@ $^
+libarena_bench.bpf.o: $(LIBARENA_BENCH_OBJECTS)
+ $(call msg,GEN-OBJ,libarena,$@)
+ $(Q)$(BPFTOOL) gen object $@ $^
+
%_asan.bpf.o: %.bpf.c
$(call msg,CLNG-BPF,libarena,$@)
$(Q)$(CLANG) $(BPF_CFLAGS) $(ASAN_FLAGS) -DBPF_ARENA_ASAN $(BPF_TARGET_ENDIAN) -c $< -o $@
diff --git a/tools/testing/selftests/bpf/libarena/benchs/bench_malloc.bpf.c b/tools/testing/selftests/bpf/libarena/benchs/bench_malloc.bpf.c
new file mode 100644
index 0000000000000..e06397dc9a40d
--- /dev/null
+++ b/tools/testing/selftests/bpf/libarena/benchs/bench_malloc.bpf.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: LGPL-2.1 OR BSD-2-Clause
+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+#include <libarena/common.h>
+
+#include <libarena/asan.h>
+#include <libarena/buddy.h>
+
+u32 bench_alloc_size;
+u32 bench_nallocs;
+long bench_hits;
+long bench_duration_ns;
+
+SEC("syscall")
+int bench_malloc(void)
+{
+ void __arena *mem;
+ u64 start_ns;
+ u32 i;
+
+ start_ns = bpf_ktime_get_ns();
+ for (i = zero; i < bench_nallocs && can_loop; i++) {
+ mem = arena_malloc(bench_alloc_size);
+ if (!mem)
+ return -ENOMEM;
+ }
+
+ __sync_add_and_fetch(&bench_duration_ns,
+ bpf_ktime_get_ns() - start_ns);
+ __sync_add_and_fetch(&bench_hits, i);
+ return 0;
+}
+
+SEC("syscall")
+int bench_calloc(void)
+{
+ void __arena *mem;
+ u64 start_ns;
+ u32 i;
+
+ start_ns = bpf_ktime_get_ns();
+ for (i = zero; i < bench_nallocs && can_loop; i++) {
+ mem = arena_calloc(1, bench_alloc_size);
+ if (!mem)
+ return -ENOMEM;
+ }
+
+ __sync_add_and_fetch(&bench_duration_ns,
+ bpf_ktime_get_ns() - start_ns);
+ __sync_add_and_fetch(&bench_hits, i);
+ return 0;
+}
diff --git a/tools/testing/selftests/bpf/libarena/include/bpf_arena_spin_lock.h b/tools/testing/selftests/bpf/libarena/include/bpf_arena_spin_lock.h
index ae6b72d15bb67..872fa20f29a59 100644
--- a/tools/testing/selftests/bpf/libarena/include/bpf_arena_spin_lock.h
+++ b/tools/testing/selftests/bpf/libarena/include/bpf_arena_spin_lock.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
#ifndef BPF_ARENA_SPIN_LOCK_H
#define BPF_ARENA_SPIN_LOCK_H
diff --git a/tools/testing/selftests/bpf/libarena/include/bpf_atomic.h b/tools/testing/selftests/bpf/libarena/include/bpf_atomic.h
index 43c306e17f198..65582b2010ad4 100644
--- a/tools/testing/selftests/bpf/libarena/include/bpf_atomic.h
+++ b/tools/testing/selftests/bpf/libarena/include/bpf_atomic.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
#ifndef BPF_ATOMIC_H
#define BPF_ATOMIC_H
diff --git a/tools/testing/selftests/bpf/libarena/include/bpf_may_goto.h b/tools/testing/selftests/bpf/libarena/include/bpf_may_goto.h
index 9ba90689d6ba2..b32a420d4e1ad 100644
--- a/tools/testing/selftests/bpf/libarena/include/bpf_may_goto.h
+++ b/tools/testing/selftests/bpf/libarena/include/bpf_may_goto.h
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: LGPL-2.1 OR BSD-2-Clause
#pragma once
/*
diff --git a/tools/testing/selftests/bpf/libarena/include/libarena/bitmap.h b/tools/testing/selftests/bpf/libarena/include/libarena/bitmap.h
index e2431ea6fdd68..163e2b83d9431 100644
--- a/tools/testing/selftests/bpf/libarena/include/libarena/bitmap.h
+++ b/tools/testing/selftests/bpf/libarena/include/libarena/bitmap.h
@@ -1,5 +1,8 @@
+// SPDX-License-Identifier: LGPL-2.1 OR BSD-2-Clause
#pragma once
+#include <bpf_atomic.h>
+
#define BITS_PER_BYTE 8
#define BYTES_TO_BITS(nb) ((nb) * BITS_PER_BYTE)
@@ -15,11 +18,8 @@ struct arena_bitmap {
struct arena_bitmap __arena *bmp_alloc(size_t bits);
void bmp_free(struct arena_bitmap __arena *bmp);
-void __bmp_set_bit(u32 bit, struct arena_bitmap __arena *bmp);
-void __bmp_clear_bit(u32 bit, struct arena_bitmap __arena *bmp);
void bmp_set_bit(u32 bit, struct arena_bitmap __arena *bmp);
void bmp_clear_bit(u32 bit, struct arena_bitmap __arena *bmp);
-bool bmp_test_bit(u32 bit, struct arena_bitmap __arena *bmp);
bool bmp_test_and_clear_bit(u32 bit, struct arena_bitmap __arena *bmp);
bool bmp_test_and_set_bit(u32 bit, struct arena_bitmap __arena *bmp);
@@ -32,3 +32,27 @@ void bmp_copy(size_t bits, struct arena_bitmap __arena *dst, struct arena_bitmap
bool bmp_intersects(size_t bits, struct arena_bitmap __arena *arg1, struct arena_bitmap __arena *arg2);
bool bmp_subset(size_t bits, struct arena_bitmap __arena *big, struct arena_bitmap __arena *small);
void bmp_print(size_t bits, struct arena_bitmap __arena *bmp);
+
+static __always_inline
+void __bmp_set_bit(u32 bit, struct arena_bitmap __arena *bmp)
+{
+ volatile u64 __arena *word = &bmp->bits[BIT_WORD(bit)];
+
+ *word |= BIT_MASK(bit);
+}
+
+static __always_inline
+void __bmp_clear_bit(u32 bit, struct arena_bitmap __arena *bmp)
+{
+ volatile u64 __arena *word = &bmp->bits[BIT_WORD(bit)];
+
+ *word &= ~BIT_MASK(bit);
+}
+
+static __always_inline
+bool bmp_test_bit(u32 bit, struct arena_bitmap __arena *bmp)
+{
+ u64 word = READ_ONCE(bmp->bits[BIT_WORD(bit)]);
+
+ return word & BIT_MASK(bit);
+}
diff --git a/tools/testing/selftests/bpf/libarena/include/libarena/common.h b/tools/testing/selftests/bpf/libarena/include/libarena/common.h
index 931ace9a49e2b..a6220355a6975 100644
--- a/tools/testing/selftests/bpf/libarena/include/libarena/common.h
+++ b/tools/testing/selftests/bpf/libarena/include/libarena/common.h
@@ -49,6 +49,7 @@ extern volatile u64 asan_violated;
int arena_fls(__u64 word);
void __arena *arena_malloc(size_t size);
+void __arena *arena_calloc(size_t ncount, size_t size);
void arena_free(void __arena *ptr);
/*
@@ -61,6 +62,76 @@ void arena_free(void __arena *ptr);
*/
#define arena_subprog_init() do { asm volatile ("" :: "r"(&arena)); } while (0)
+/*
+ * BPF does not currently support the memset intrinsics. for large
+ * sequential copies, or assignments of large data structures,
+ * the frontend will generate an intrinsic that causes the BPF
+ * backend to exit due to a missing implementation. Provide
+ * implementations for the intrinsic.
+ */
+static inline int arena_memset(s8 __arena *dst, s8 val, size_t size)
+{
+ size_t headalign;
+ size_t tailalign;
+ u8 uval = (u8)val;
+ size_t val64;
+ size_t i;
+
+ /*
+ * Calculate how many bytes to the next word-aligned one.
+ * We get this by truncating the 2s complement of the
+ * pointer to the last 3 bits. Intuitively, since
+ *
+ * The N LSBs of dst and -dst add to 1 << N, which
+ * is why dst + (-dst) = 0x0ULL through overflow. So the
+ * last N = 3 bits of the negative are the number of
+ * bytes to align dst on the last 3 bits.
+ *
+ */
+ headalign = -(u64)dst & (sizeof(u64) - 1);
+ if (!headalign || size < headalign)
+ goto ptraligned;
+
+ for (i = zero; i < headalign && can_loop; i++)
+ dst[i] = uval;
+
+ dst += headalign;
+ size -= headalign;
+
+ptraligned:
+
+ /*
+ * Make a word with all bytes equal to the byte we are setting.
+ * Since 1 byte -> 2 hex digits.
+ *
+ * Shifting the value by a 0 bytes is equal to multiplication by 0x01
+ * Shifting by 1 bytes is equal to multiplication by 0x01 << 8,
+ * ...
+ * Shifting by 7 bytes is equal to multiplication by 0x01 << 56.
+ *
+ * End operation to replicate the byte into all the bytes of a word
+ * is (since a | b = a + b when a & b == 0):
+ *
+ * val + val * (1UL << 8) + val * (1UL << 16) + .. + val * (1UL << 56)
+ * = val * (1UL << 56 + 1UL << 48 + ... + 1UL << 0)
+ * = val * (0x01UL << 56 | 0x01UL << 48 + ... + 1UL << 0)
+ * = val * 0x0101 0101 0101 0101
+ */
+ val64 = (u8)val * 0x0101010101010101ULL;
+
+ /* Pointer is now aligned, use word-aligned assignments. */
+ for (i = zero; i < size / sizeof(u64) && can_loop; i++)
+ ((u64 __arena *)dst)[i] = val64;
+
+ /* Go back to byte-aligned for the tail. */
+ tailalign = size % sizeof(u64);
+ dst += size - tailalign;
+ for (i = zero; i < tailalign && can_loop; i++)
+ dst[i] = uval;
+
+ return 0;
+}
+
#else /* ! __BPF__ */
#include <stdint.h>
diff --git a/tools/testing/selftests/bpf/libarena/selftests/test_bitmap.bpf.c b/tools/testing/selftests/bpf/libarena/selftests/test_bitmap.bpf.c
index 76319a529f02b..e66b3a26ca3d6 100644
--- a/tools/testing/selftests/bpf/libarena/selftests/test_bitmap.bpf.c
+++ b/tools/testing/selftests/bpf/libarena/selftests/test_bitmap.bpf.c
@@ -1,3 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1 OR BSD-2-Clause
+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+
#include <libarena/common.h>
#include <libarena/asan.h>
diff --git a/tools/testing/selftests/bpf/libarena/src/asan.bpf.c b/tools/testing/selftests/bpf/libarena/src/asan.bpf.c
index 5135d5c72a46d..de656b69d13af 100644
--- a/tools/testing/selftests/bpf/libarena/src/asan.bpf.c
+++ b/tools/testing/selftests/bpf/libarena/src/asan.bpf.c
@@ -103,23 +103,6 @@ volatile bool asan_inited = false;
*/
volatile bool asan_report_once = false;
-/*
- * BPF does not currently support the memset/memcpy/memcmp intrinsics.
- * For large sequential copies, or assignments of large data structures,
- * the frontend will generate an intrinsic that causes the BPF backend
- * to exit due to a missing implementation. Provide a simple implementation
- * just for memset to use it for poisoning/unpoisoning the map.
- */
-__weak int asan_memset(s8 __arena *dst, s8 val, size_t size)
-{
- size_t i;
-
- for (i = zero; i < size && can_loop; i++)
- dst[i] = val;
-
- return 0;
-}
-
/* Validate a 1-byte access, always within a single byte. */
static __always_inline bool memory_is_poisoned_1(s8 __arena *addr)
{
@@ -422,7 +405,7 @@ __hidden __noasan int asan_poison(void __arena *addr, s8 val, size_t size)
shadow = mem_to_shadow(addr);
len = size >> ASAN_SHADOW_SHIFT;
- asan_memset(shadow, val, len);
+ arena_memset(shadow, val, len);
return 0;
}
@@ -463,7 +446,7 @@ __hidden __noasan int asan_unpoison(void __arena *addr, size_t size)
shadow = mem_to_shadow(addr);
len = size >> ASAN_SHADOW_SHIFT;
- asan_memset(shadow, 0, len);
+ arena_memset(shadow, 0, len);
/*
* If we are allocating a non-granule aligned region, we need to adjust
diff --git a/tools/testing/selftests/bpf/libarena/src/bitmap.bpf.c b/tools/testing/selftests/bpf/libarena/src/bitmap.bpf.c
index 5ff8e688ddc76..0390f20ce366d 100644
--- a/tools/testing/selftests/bpf/libarena/src/bitmap.bpf.c
+++ b/tools/testing/selftests/bpf/libarena/src/bitmap.bpf.c
@@ -34,24 +34,6 @@ void bmp_free(struct arena_bitmap __arena *bmp)
arena_free(bmp);
}
-__weak
-void __bmp_set_bit(u32 bit, struct arena_bitmap __arena *bmp)
-{
- bmp->bits[BIT_WORD(bit)] |= BIT_MASK(bit);
-}
-
-__weak
-void __bmp_clear_bit(u32 bit, struct arena_bitmap __arena *bmp)
-{
- bmp->bits[BIT_WORD(bit)] &= ~BIT_MASK(bit);
-}
-
-__weak
-bool bmp_test_bit(u32 bit, struct arena_bitmap __arena *bmp)
-{
- return bmp->bits[BIT_WORD(bit)] & BIT_MASK(bit);
-}
-
__weak
bool bmp_test_and_clear_bit(u32 bit, struct arena_bitmap __arena *bmp)
{
diff --git a/tools/testing/selftests/bpf/libarena/src/buddy.bpf.c b/tools/testing/selftests/bpf/libarena/src/buddy.bpf.c
index c674ee5cfcc1d..2490ab1396de0 100644
--- a/tools/testing/selftests/bpf/libarena/src/buddy.bpf.c
+++ b/tools/testing/selftests/bpf/libarena/src/buddy.bpf.c
@@ -5,6 +5,8 @@
#include <libarena/asan.h>
#include <libarena/buddy.h>
+#include <bpf_arena_spin_lock.h>
+
/*
* Buddy allocator arena-based implementation.
*
@@ -45,15 +47,8 @@ enum {
BUDDY_CHUNK_PAGES = BUDDY_CHUNK_BYTES / __PAGE_SIZE
};
-static inline int buddy_lock(struct buddy __arena *buddy)
-{
- return arena_spin_lock(&buddy->lock);
-}
-
-static inline void buddy_unlock(struct buddy __arena *buddy)
-{
- arena_spin_unlock(&buddy->lock);
-}
+#define buddy_lock(buddy, flags) (arena_spin_lock_irqsave(&(buddy)->lock, (flags)))
+#define buddy_unlock(buddy, flags) (arena_spin_unlock_irqrestore(&(buddy)->lock, (flags)))
/*
* Reserve part of the arena address space for the allocator. We use
@@ -385,6 +380,7 @@ static struct buddy_chunk __arena *buddy_chunk_get(struct buddy __arena *buddy)
{
u64 order, ord, min_order, max_order;
struct buddy_chunk __arena *chunk;
+ unsigned long flags;
size_t left;
int power2;
u64 vaddr;
@@ -416,7 +412,7 @@ static struct buddy_chunk __arena *buddy_chunk_get(struct buddy __arena *buddy)
return NULL;
}
- if (buddy_lock(buddy)) {
+ if (buddy_lock(buddy, flags)) {
/*
* We cannot reclaim the vaddr space, but that is ok - this
* operation should always succeed. The error path is to catch
@@ -520,7 +516,7 @@ static struct buddy_chunk __arena *buddy_chunk_get(struct buddy __arena *buddy)
arena_stderr(
"chunk has size of 0x%lx bytes (left %lx bytes)\n",
sizeof(*chunk), left);
- buddy_unlock(buddy);
+ buddy_unlock(buddy, flags);
return NULL;
}
@@ -531,7 +527,7 @@ static struct buddy_chunk __arena *buddy_chunk_get(struct buddy __arena *buddy)
order = (power2 >= BUDDY_MIN_ALLOC_SHIFT) ? power2 - BUDDY_MIN_ALLOC_SHIFT : 0;
if (idx_set_allocated(chunk, idx, true)) {
- buddy_unlock(buddy);
+ buddy_unlock(buddy, flags);
return NULL;
}
@@ -547,7 +543,7 @@ static struct buddy_chunk __arena *buddy_chunk_get(struct buddy __arena *buddy)
*/
min_order = left ? order + 1 : order;
if (add_leftovers_to_freelist(chunk, idx, min_order, max_order)) {
- buddy_unlock(buddy);
+ buddy_unlock(buddy, flags);
return NULL;
}
@@ -556,7 +552,7 @@ static struct buddy_chunk __arena *buddy_chunk_get(struct buddy __arena *buddy)
max_order = order;
}
- buddy_unlock(buddy);
+ buddy_unlock(buddy, flags);
return chunk;
}
@@ -564,6 +560,7 @@ static struct buddy_chunk __arena *buddy_chunk_get(struct buddy __arena *buddy)
__weak int buddy_init(struct buddy __arena *buddy)
{
struct buddy_chunk __arena *chunk;
+ unsigned long flags;
int ret;
if (!asan_ready())
@@ -579,7 +576,7 @@ __weak int buddy_init(struct buddy __arena *buddy)
chunk = buddy_chunk_get(buddy);
- if (buddy_lock(buddy)) {
+ if (buddy_lock(buddy, flags)) {
bpf_arena_free_pages(&arena, chunk, BUDDY_CHUNK_PAGES);
return -EINVAL;
}
@@ -591,7 +588,7 @@ __weak int buddy_init(struct buddy __arena *buddy)
/* Put the chunk at the beginning of the list. */
buddy->first_chunk = chunk;
- buddy_unlock(buddy);
+ buddy_unlock(buddy, flags);
return chunk ? 0 : -ENOMEM;
}
@@ -730,9 +727,10 @@ static u64 buddy_alloc_from_existing_chunks(struct buddy __arena *buddy, int ord
*/
static u64 buddy_alloc_from_new_chunk(struct buddy __arena *buddy, struct buddy_chunk __arena *chunk, int order)
{
+ unsigned long flags;
u64 address;
- if (buddy_lock(buddy))
+ if (buddy_lock(buddy, flags))
return (u64)NULL;
@@ -745,7 +743,7 @@ static u64 buddy_alloc_from_new_chunk(struct buddy __arena *buddy, struct buddy_
address = buddy_chunk_alloc(buddy->first_chunk, order);
- buddy_unlock(buddy);
+ buddy_unlock(buddy, flags);
return (u64)address;
}
@@ -754,6 +752,7 @@ void __arena *buddy_alloc(struct buddy __arena *buddy, size_t size)
{
void __arena *address = NULL;
struct buddy_chunk __arena *chunk;
+ unsigned long flags;
int order;
if (!buddy)
@@ -765,11 +764,11 @@ void __arena *buddy_alloc(struct buddy __arena *buddy, size_t size)
return NULL;
}
- if (buddy_lock(buddy))
+ if (buddy_lock(buddy, flags))
return NULL;
address = (u8 __arena *)buddy_alloc_from_existing_chunks(buddy, order);
- buddy_unlock(buddy);
+ buddy_unlock(buddy, flags);
if (address)
goto done;
@@ -880,6 +879,7 @@ static __always_inline int buddy_free_unlocked(struct buddy __arena *buddy, u64
__weak int buddy_free(struct buddy __arena *buddy, void __arena *addr)
{
+ unsigned long flags;
int ret;
if (!buddy)
@@ -889,13 +889,13 @@ __weak int buddy_free(struct buddy __arena *buddy, void __arena *addr)
if (!addr)
return 0;
- ret = buddy_lock(buddy);
+ ret = buddy_lock(buddy, flags);
if (ret)
return ret;
ret = buddy_free_unlocked(buddy, (u64)addr);
- buddy_unlock(buddy);
+ buddy_unlock(buddy, flags);
return ret;
}
diff --git a/tools/testing/selftests/bpf/libarena/src/common.bpf.c b/tools/testing/selftests/bpf/libarena/src/common.bpf.c
index 569f0f64d518b..785d4872cb494 100644
--- a/tools/testing/selftests/bpf/libarena/src/common.bpf.c
+++ b/tools/testing/selftests/bpf/libarena/src/common.bpf.c
@@ -1,5 +1,7 @@
// SPDX-License-Identifier: LGPL-2.1 OR BSD-2-Clause
/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+#include <limits.h>
+
#include <libarena/common.h>
#include <libarena/asan.h>
#include <libarena/buddy.h>
@@ -48,10 +50,33 @@ __weak void __arena *arena_malloc(size_t size)
return buddy_alloc(&buddy, size);
}
+__weak void __arena *arena_calloc(size_t ncount, size_t size)
+{
+ void __arena *mem;
+ size_t total;
+
+ /*
+ * Ideally we'd be using __builtin_mul_overflow here,
+ * but the BPF compiler backend doesn't implement __multi3.
+ * There are ways to optimize the division away from the
+ * overflow check, but any costs are dwarfed by the
+ * buddy_alloc() call. Keep it simple for now.
+ */
+ if (unlikely(ncount && size >= ULLONG_MAX / ncount))
+ return NULL;
+
+ total = ncount * size;
+
+ mem = buddy_alloc(&buddy, total);
+ if (likely(mem))
+ arena_memset(mem, 0, total);
+
+ return mem;
+}
+
__weak void arena_free(void __arena *ptr)
{
buddy_free(&buddy, ptr);
}
-
char _license[] SEC("license") = "GPL";
|
| 3/2 | 2026/08/17 19:43 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 14230 output: 71 thoughts: 829 Thoughts: **BPF Selftests Patch Analysis** Okay, so I need to figure out if this patch series necessitates fuzzing. The files listed are all nestled within `tools/testing/selftests/bpf/`. My immediate reaction is: *not kernel core code*. This looks like BPF self-tests and benchmarks. I'm pretty certain this is user-space tooling designed for testing, not kernel functionality. The prompt states clear criteria for when a patch *doesn't* warrant fuzzing. Things like documentation updates, logging changes, decorative modifications, unreachable code paths, and specific driver teardown callbacks are all exempt. This seems like it would be related. Let me just double-check, because it's always good to be thorough. Okay, a quick glance at the file list confirms my initial assessment. All paths begin with `tools/testing/selftests/bpf/`. This patch *only* touches BPF self-tests and related benchmark code. It's not modifying any core kernel logic, drivers, or architectures. The context is very clearly user-space tools and BPF programs for testing. No chance of any core kernel side effects. Therefore, applying the provided criteria, I'm confident in concluding that this patch doesn't warrant fuzzing by a tool like syzkaller, which targets the kernel itself. WorthFuzzing=false. Let's get that logged. |
| 4/2 | 2026/08/17 19:44 | tool | set-results |
0mArgs:{
"EnableConfigs": [],
"FocusSymbols": [],
"Reasoning": "The patch only modifies BPF selftests and benchmarks under tools/testing/selftests/bpf/. It does not make any functional changes to the core kernel, drivers, or architectures.",
"WorthFuzzing": false
}Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch only modifies BPF selftests and benchmarks under tools/testing/selftests/bpf/. It does not make any functional changes to the core kernel, drivers, or architectures.",
"WorthFuzzing": false
} |
| 5/1 | 2026/08/17 19:44 | action | If |
0mArgs:{
"WorthFuzzing": false
}Results: null |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|