Skip to content

Commit 0a5d2ef

Browse files
Hou TaoAlexei Starovoitov
Hou Tao
authored and
Alexei Starovoitov
committed
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 <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent 58f038e commit 0a5d2ef

File tree

2 files changed

+236
-0
lines changed

2 files changed

+236
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
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+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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";

0 commit comments

Comments
 (0)