No BPF selftest creates task-local storage from a tracepoint which runs while an hrtimer base raw lock is held. On PREEMPT_RT, this can exercise allocator rtmutexes inside a raw-lock section.
Attach a program to tp_btf/hrtimer_start for the test process. Create and delete task storage on every event and trigger 1,000 events with timerfd_settime(). Detach the program before reading its counters, then require at least one successful allocation and balanced attempt and outcome counters. Allocation failure remains valid for the best-effort no-lock API. Assisted-by: LLM Signed-off-by: Karl Mehltretter <[email protected]> --- .../bpf/prog_tests/task_storage_hrtimer.c | 50 +++++++++++++++++++ .../bpf/progs/task_storage_hrtimer.c | 48 ++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/task_storage_hrtimer.c create mode 100644 tools/testing/selftests/bpf/progs/task_storage_hrtimer.c diff --git a/tools/testing/selftests/bpf/prog_tests/task_storage_hrtimer.c b/tools/testing/selftests/bpf/prog_tests/task_storage_hrtimer.c new file mode 100644 index 000000000000..216e66263bf6 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/task_storage_hrtimer.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <sys/timerfd.h> +#include <unistd.h> + +#include <test_progs.h> +#include "task_storage_hrtimer.skel.h" + +#define TRIGGER_COUNT 1000 + +void test_task_storage_hrtimer(void) +{ + struct itimerspec timer = { + .it_value.tv_nsec = 1000000, + }; + struct task_storage_hrtimer *skel; + int err, fd = -1, i; + + skel = task_storage_hrtimer__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel_open_and_load")) + return; + + skel->bss->target_tgid = getpid(); + err = task_storage_hrtimer__attach(skel); + if (!ASSERT_OK(err, "skel_attach")) + goto cleanup; + + fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); + if (!ASSERT_GE(fd, 0, "timerfd_create")) + goto cleanup; + + for (i = 0; i < TRIGGER_COUNT; i++) { + err = timerfd_settime(fd, 0, &timer, NULL); + if (!ASSERT_OK(err, "timerfd_settime")) + goto cleanup; + } + + task_storage_hrtimer__detach(skel); + + ASSERT_GE(skel->bss->seen, TRIGGER_COUNT, "seen"); + ASSERT_EQ(skel->bss->attempted, skel->bss->seen, "attempted"); + ASSERT_GT(skel->bss->succeeded, 0, "succeeded"); + ASSERT_EQ(skel->bss->delete_errors, 0, "delete_errors"); + ASSERT_EQ(skel->bss->attempted, + skel->bss->succeeded + skel->bss->failed, "attempts"); + +cleanup: + if (fd >= 0) + close(fd); + task_storage_hrtimer__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/progs/task_storage_hrtimer.c b/tools/testing/selftests/bpf/progs/task_storage_hrtimer.c new file mode 100644 index 000000000000..a53c966f15b5 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/task_storage_hrtimer.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "vmlinux.h" +#include <bpf/bpf_helpers.h> +#include <bpf/bpf_tracing.h> + +char _license[] SEC("license") = "GPL"; + +struct { + __uint(type, BPF_MAP_TYPE_TASK_STORAGE); + __uint(map_flags, BPF_F_NO_PREALLOC); + __type(key, int); + __type(value, long); +} task_storage SEC(".maps"); + +u32 target_tgid; +u64 seen; +u64 attempted; +u64 succeeded; +u64 failed; +u64 delete_errors; + +SEC("tp_btf/hrtimer_start") +int BPF_PROG(on_hrtimer_start, struct hrtimer *timer, + enum hrtimer_mode mode, bool was_armed) +{ + struct task_struct *task; + long *storage; + + if (bpf_get_current_pid_tgid() >> 32 != target_tgid) + return 0; + + seen++; + attempted++; + task = bpf_get_current_task_btf(); + storage = bpf_task_storage_get(&task_storage, task, NULL, + BPF_LOCAL_STORAGE_GET_F_CREATE); + if (!storage) { + failed++; + return 0; + } + + if (bpf_task_storage_delete(&task_storage, task)) + delete_errors++; + else + succeeded++; + + return 0; +} -- 2.53.0

