BPF List
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Emil Tsalapatis" <emil@etsalapatis.com>, bpf@vger.kernel.org
Subject: Re: [PATCH bpf 2/2] selftests/bpf: Add a selftest for program allocation/freeing
Date: Mon, 02 Nov 2026 07:47:42 +0000	[thread overview]
Message-ID: <sashiko-outbox-193047@kernel.org> (raw)
In-Reply-To: <20261102073439.6822-3-emil@etsalapatis.com>

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Medium] selftests/bpf: standalone stress binary bypasses test_progs and kselftest
- [Medium] selftests/bpf: hardcoded 64-CPU check and contiguous CPU IDs in pin_cpu()
- [Medium] selftests/bpf: 64 KiB stack in run_threads() below PTHREAD_STACK_MIN
- [Low] selftests/bpf: commit message omits deadlock details and test design
--

commit f26b450586e3e82488cb0fdfca492b1cd1920f9b
Author: Emil Tsalapatis <emil@etsalapatis.com>

selftests/bpf: Add a selftest for program allocation/freeing

This commit adds a standalone stress test under tools/testing/selftests/bpf/
that repeatedly loads 65,536 socket filter programs across 64 CPUs, closes
them from CPU 0 under SCHED_FIFO, and spawns 1,024 waiter threads.

>     selftests/bpf: Add a selftest for program allocation/freeing
>     
>     Add a selftest that stresses BPF program allocation/free
>     paths.
>     
>     Signed-off-by: Emil Tsalapatis <emil@etsalapatis.com>

[Severity: Low]
This isn't a bug, but the commit message only states that a selftest is
added without explaining the underlying kernel bug mechanism or why the
workload in run_burst() is structured this way.

Could the commit message be expanded to describe the deadlock scenario being
tested (such as deferred __bpf_prog_put_noref() work items saturating CPU 0's
system_percpu_wq max_active limit while blocked on pack_mutex and starving
the vmalloc purge_vmap_node work item), the rationale for the multi-phase
workload parameters in run_burst(), and why the binary is placed in
TEST_GEN_PROGS_EXTENDED?

> diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
> index 93c707116fad0..e59e07c4bd121 100644
> --- a/tools/testing/selftests/bpf/Makefile
> +++ b/tools/testing/selftests/bpf/Makefile
> @@ -137,6 +137,7 @@ TEST_KMOD_TARGETS = $(addprefix $(OUTPUT)/,$(TEST_KMODS))
>  
>  # Compile but not part of 'make run_tests'
>  TEST_GEN_PROGS_EXTENDED = \
> +	bpf_prog_free_stress \
>  	bench \
>  	flow_dissector_load \
>  	test_cpp \

[Severity: Medium]
By adding bpf_prog_free_stress only to TEST_GEN_PROGS_EXTENDED without a
corresponding runner in TEST_PROGS or prog_tests/, neither make run_tests
nor BPF CI (test_progs) will ever execute this test.

In addition, tools/testing/selftests/bpf/bpf_prog_free_stress.c defines a
standalone main() that reports status via printf() and err()/errx(), and
reimplements program loading in load_prog() via raw syscall(__NR_bpf,
BPF_PROG_LOAD, ...) instead of using libbpf's bpf_prog_load() and either
test_progs ASSERT_*() macros (like existing stress tests in prog_tests/ such
as task_work_stress.c, fexit_stress.c, and bpf_mod_race.c) or kselftest.h
ksft_* reporting helpers.

Should this test be integrated as a test_progs subtest under prog_tests/ (or
use libbpf's bpf_prog_load() and kselftest.h reporting if kept standalone)?

> diff --git a/tools/testing/selftests/bpf/bpf_prog_free_stress.c b/tools/testing/selftests/bpf/bpf_prog_free_stress.c
> new file mode 100644
> index 0000000000000..8c761845c7032
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/bpf_prog_free_stress.c
> @@ -0,0 +1,184 @@

[ ... ]

> +#define LOAD_CPUS 64
> +#define PROGRAMS 65536
> +#define WAITERS 1024
> +#define DEFAULT_DURATION_SEC 5
> +
> +static int fds[PROGRAMS];
> +static const struct bpf_insn insns[] = {
> +	BPF_MOV64_IMM(BPF_REG_0, 0),
> +	BPF_EXIT_INSN(),
> +};
> +
> +static void pin_cpu(int cpu)
> +{
> +	cpu_set_t mask;
> +
> +	CPU_ZERO(&mask);
> +	CPU_SET(cpu, &mask);
> +	if (sched_setaffinity(0, sizeof(mask), &mask))
> +		err(EXIT_FAILURE, "sched_setaffinity CPU%d", cpu);
> +}

[ ... ]

> +static void *load_worker(void *arg)
> +{
> +	int cpu = (uintptr_t)arg;
> +	int first = cpu * (PROGRAMS / LOAD_CPUS);
> +	int i;
> +
> +	pin_cpu(cpu);
> +	for (i = first; i < first + PROGRAMS / LOAD_CPUS; i++)
> +		fds[i] = load_prog();
> +	return NULL;
> +}

[ ... ]

> +static void run_threads(int count, void *(*worker)(void *))
> +{
> +	pthread_t threads[WAITERS];
> +	pthread_attr_t attr;
> +	int i;
> +
> +	check_pthread(pthread_attr_init(&attr), "pthread_attr_init");
> +	check_pthread(pthread_attr_setstacksize(&attr, 64 * 1024),
> +		      "pthread_attr_setstacksize");

[Severity: Medium]
In run_threads(), does setting a hardcoded 64 KiB stack size via
pthread_attr_setstacksize(&attr, 64 * 1024) fail with EINVAL on architectures
where PTHREAD_STACK_MIN is larger than 64 KiB?

For example, on powerpc64le (where PTHREAD_STACK_MIN is 131072) or aarch64
with 64 KiB pages, pthread_attr_setstacksize() returns EINVAL when the
requested size is smaller than PTHREAD_STACK_MIN, causing check_pthread() to
abort the test with EXIT_FAILURE on the first burst.

Could this clamp the stack size to at least PTHREAD_STACK_MIN?

[ ... ]

> +int main(int argc, char **argv)
> +{
> +	struct rlimit limit = { PROGRAMS + 1024, PROGRAMS + 1024 };
> +	long duration = parse_duration(argc, argv);
> +	long start;
> +	int round = 0;
> +
> +	setvbuf(stdout, NULL, _IOLBF, 0);
> +	if (sysconf(_SC_NPROCESSORS_ONLN) < LOAD_CPUS)
> +		errx(EXIT_FAILURE, "need at least %d online CPUs", LOAD_CPUS);

[Severity: Medium]
What happens in main() and load_worker() when this test runs on systems with
fewer than 64 online CPUs, or on systems with at least 64 online CPUs where
CPU IDs 0 through 63 are not all online and permitted by the process's CPU
affinity mask?

First, main() exits with EXIT_FAILURE when sysconf(_SC_NPROCESSORS_ONLN) <
LOAD_CPUS (64), or when unprivileged calls to setrlimit(),
sched_setscheduler(), or BPF_PROG_LOAD fail. Per kselftest policy, missing
environment or hardware prerequisites should skip rather than fail. Moreover,
since __purge_vmap_area_lazy() in mm/vmalloc.c queues asynchronous purge work
onto system_percpu_wq whenever nr_purge_nodes >= 2, could the test scale to
the number of available CPUs in the affinity mask and only skip when fewer
than 2 CPUs are available?

Second, load_worker() passes the worker index 0..LOAD_CPUS-1 directly to
pin_cpu(cpu), while main() and load_waiter() hardcode pin_cpu(0). Because
sysconf(_SC_NPROCESSORS_ONLN) only checks the total count of online CPUs,
sched_setaffinity() in pin_cpu() will fail with EINVAL if any CPU in 0..63 is
offline or excluded by the current cpuset or taskset affinity mask. Could
main() query sched_getaffinity() at startup and map workers onto the set of
usable CPU IDs?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261102073439.6822-1-emil@etsalapatis.com?part=2

  reply	other threads:[~2026-11-02  7:47 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-02  7:34 [PATCH bpf 0/2] bpf: Avoid workqueue deadlock when freeing programs Emil Tsalapatis
2026-11-02  7:34 ` [PATCH bpf 1/2] bpf: Use dedicated workqueue for freeing BPF programs Emil Tsalapatis
2026-11-02 17:58   ` Alexei Starovoitov
2026-11-02  7:34 ` [PATCH bpf 2/2] selftests/bpf: Add a selftest for program allocation/freeing Emil Tsalapatis
2026-11-02  7:47   ` sashiko-bot [this message]
2026-11-02 17:55 ` [PATCH bpf 0/2] bpf: Avoid workqueue deadlock when freeing programs Andrii Nakryiko

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=sashiko-outbox-193047@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=emil@etsalapatis.com \
    --cc=sashiko-reviews@lists.linux.dev \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox