Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/bpf: Add test case for the freeing of bpf_timer

The main purpose of the test is to demonstrate the lock problem for the
free of bpf_timer under PREEMPT_RT. When freeing a bpf_timer which is
running on other CPU in bpf_timer_cancel_and_free(), hrtimer_cancel()
will try to acquire a spin-lock (namely softirq_expiry_lock), however
the freeing procedure has already held a raw-spin-lock.

The test first creates two threads: one to start timers and the other to
free timers. The start-timers thread will start the timer and then wake
up the free-timers thread to free these timers when the starts complete.
After freeing, the free-timer thread will wake up the start-timer thread
to complete the current iteration. A loop of 10 iterations is used.

Signed-off-by: Hou Tao <houtao1@huawei.com>
Link: https://lore.kernel.org/r/20250117101816.2101857-6-houtao@huaweicloud.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Hou Tao and committed by
Alexei Starovoitov
0a5d2efa 58f038e6

+236
+165
tools/testing/selftests/bpf/prog_tests/free_timer.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (C) 2025. Huawei Technologies Co., Ltd */ 3 + #define _GNU_SOURCE 4 + #include <unistd.h> 5 + #include <sys/syscall.h> 6 + #include <test_progs.h> 7 + 8 + #include "free_timer.skel.h" 9 + 10 + struct run_ctx { 11 + struct bpf_program *start_prog; 12 + struct bpf_program *overwrite_prog; 13 + pthread_barrier_t notify; 14 + int loop; 15 + bool start; 16 + bool stop; 17 + }; 18 + 19 + static void start_threads(struct run_ctx *ctx) 20 + { 21 + ctx->start = true; 22 + } 23 + 24 + static void stop_threads(struct run_ctx *ctx) 25 + { 26 + ctx->stop = true; 27 + /* Guarantee the order between ->stop and ->start */ 28 + __atomic_store_n(&ctx->start, true, __ATOMIC_RELEASE); 29 + } 30 + 31 + static int wait_for_start(struct run_ctx *ctx) 32 + { 33 + while (!__atomic_load_n(&ctx->start, __ATOMIC_ACQUIRE)) 34 + usleep(10); 35 + 36 + return ctx->stop; 37 + } 38 + 39 + static void *overwrite_timer_fn(void *arg) 40 + { 41 + struct run_ctx *ctx = arg; 42 + int loop, fd, err; 43 + cpu_set_t cpuset; 44 + long ret = 0; 45 + 46 + /* Pin on CPU 0 */ 47 + CPU_ZERO(&cpuset); 48 + CPU_SET(0, &cpuset); 49 + pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset); 50 + 51 + /* Is the thread being stopped ? */ 52 + err = wait_for_start(ctx); 53 + if (err) 54 + return NULL; 55 + 56 + fd = bpf_program__fd(ctx->overwrite_prog); 57 + loop = ctx->loop; 58 + while (loop-- > 0) { 59 + LIBBPF_OPTS(bpf_test_run_opts, opts); 60 + 61 + /* Wait for start thread to complete */ 62 + pthread_barrier_wait(&ctx->notify); 63 + 64 + /* Overwrite timers */ 65 + err = bpf_prog_test_run_opts(fd, &opts); 66 + if (err) 67 + ret |= 1; 68 + else if (opts.retval) 69 + ret |= 2; 70 + 71 + /* Notify start thread to start timers */ 72 + pthread_barrier_wait(&ctx->notify); 73 + } 74 + 75 + return (void *)ret; 76 + } 77 + 78 + static void *start_timer_fn(void *arg) 79 + { 80 + struct run_ctx *ctx = arg; 81 + int loop, fd, err; 82 + cpu_set_t cpuset; 83 + long ret = 0; 84 + 85 + /* Pin on CPU 1 */ 86 + CPU_ZERO(&cpuset); 87 + CPU_SET(1, &cpuset); 88 + pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset); 89 + 90 + /* Is the thread being stopped ? */ 91 + err = wait_for_start(ctx); 92 + if (err) 93 + return NULL; 94 + 95 + fd = bpf_program__fd(ctx->start_prog); 96 + loop = ctx->loop; 97 + while (loop-- > 0) { 98 + LIBBPF_OPTS(bpf_test_run_opts, opts); 99 + 100 + /* Run the prog to start timer */ 101 + err = bpf_prog_test_run_opts(fd, &opts); 102 + if (err) 103 + ret |= 4; 104 + else if (opts.retval) 105 + ret |= 8; 106 + 107 + /* Notify overwrite thread to do overwrite */ 108 + pthread_barrier_wait(&ctx->notify); 109 + 110 + /* Wait for overwrite thread to complete */ 111 + pthread_barrier_wait(&ctx->notify); 112 + } 113 + 114 + return (void *)ret; 115 + } 116 + 117 + void test_free_timer(void) 118 + { 119 + struct free_timer *skel; 120 + struct bpf_program *prog; 121 + struct run_ctx ctx; 122 + pthread_t tid[2]; 123 + void *ret; 124 + int err; 125 + 126 + skel = free_timer__open_and_load(); 127 + if (!ASSERT_OK_PTR(skel, "open_load")) 128 + return; 129 + 130 + memset(&ctx, 0, sizeof(ctx)); 131 + 132 + prog = bpf_object__find_program_by_name(skel->obj, "start_timer"); 133 + if (!ASSERT_OK_PTR(prog, "find start prog")) 134 + goto out; 135 + ctx.start_prog = prog; 136 + 137 + prog = bpf_object__find_program_by_name(skel->obj, "overwrite_timer"); 138 + if (!ASSERT_OK_PTR(prog, "find overwrite prog")) 139 + goto out; 140 + ctx.overwrite_prog = prog; 141 + 142 + pthread_barrier_init(&ctx.notify, NULL, 2); 143 + ctx.loop = 10; 144 + 145 + err = pthread_create(&tid[0], NULL, start_timer_fn, &ctx); 146 + if (!ASSERT_OK(err, "create start_timer")) 147 + goto out; 148 + 149 + err = pthread_create(&tid[1], NULL, overwrite_timer_fn, &ctx); 150 + if (!ASSERT_OK(err, "create overwrite_timer")) { 151 + stop_threads(&ctx); 152 + goto out; 153 + } 154 + 155 + start_threads(&ctx); 156 + 157 + ret = NULL; 158 + err = pthread_join(tid[0], &ret); 159 + ASSERT_EQ(err | (long)ret, 0, "start_timer"); 160 + ret = NULL; 161 + err = pthread_join(tid[1], &ret); 162 + ASSERT_EQ(err | (long)ret, 0, "overwrite_timer"); 163 + out: 164 + free_timer__destroy(skel); 165 + }
+71
tools/testing/selftests/bpf/progs/free_timer.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (C) 2025. Huawei Technologies Co., Ltd */ 3 + #include <linux/bpf.h> 4 + #include <time.h> 5 + #include <bpf/bpf_tracing.h> 6 + #include <bpf/bpf_helpers.h> 7 + 8 + #define MAX_ENTRIES 8 9 + 10 + struct map_value { 11 + struct bpf_timer timer; 12 + }; 13 + 14 + struct { 15 + __uint(type, BPF_MAP_TYPE_HASH); 16 + __type(key, int); 17 + __type(value, struct map_value); 18 + __uint(max_entries, MAX_ENTRIES); 19 + } map SEC(".maps"); 20 + 21 + static int timer_cb(void *map, void *key, struct map_value *value) 22 + { 23 + volatile int sum = 0; 24 + int i; 25 + 26 + bpf_for(i, 0, 1024 * 1024) sum += i; 27 + 28 + return 0; 29 + } 30 + 31 + static int start_cb(int key) 32 + { 33 + struct map_value *value; 34 + 35 + value = bpf_map_lookup_elem(&map, (void *)&key); 36 + if (!value) 37 + return 0; 38 + 39 + bpf_timer_init(&value->timer, &map, CLOCK_MONOTONIC); 40 + bpf_timer_set_callback(&value->timer, timer_cb); 41 + /* Hope 100us will be enough to wake-up and run the overwrite thread */ 42 + bpf_timer_start(&value->timer, 100000, BPF_F_TIMER_CPU_PIN); 43 + 44 + return 0; 45 + } 46 + 47 + static int overwrite_cb(int key) 48 + { 49 + struct map_value zero = {}; 50 + 51 + /* Free the timer which may run on other CPU */ 52 + bpf_map_update_elem(&map, (void *)&key, &zero, BPF_ANY); 53 + 54 + return 0; 55 + } 56 + 57 + SEC("syscall") 58 + int BPF_PROG(start_timer) 59 + { 60 + bpf_loop(MAX_ENTRIES, start_cb, NULL, 0); 61 + return 0; 62 + } 63 + 64 + SEC("syscall") 65 + int BPF_PROG(overwrite_timer) 66 + { 67 + bpf_loop(MAX_ENTRIES, overwrite_cb, NULL, 0); 68 + return 0; 69 + } 70 + 71 + char _license[] SEC("license") = "GPL";