Skip to content

Commit 67ee5ad

Browse files
Alexei Starovoitovanakryiko
authored andcommitted
selftests/bpf: Add a testcase for deadlock avoidance
Add a testcase that checks that deadlock avoidance is working as expected. Signed-off-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Link: https://lore.kernel.org/bpf/20260204055147.54960-3-alexei.starovoitov@gmail.com
1 parent 7d49635 commit 67ee5ad

2 files changed

Lines changed: 108 additions & 0 deletions

File tree

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
3+
#include <test_progs.h>
4+
#include "timer_start_deadlock.skel.h"
5+
6+
void test_timer_start_deadlock(void)
7+
{
8+
struct timer_start_deadlock *skel;
9+
int err, prog_fd;
10+
LIBBPF_OPTS(bpf_test_run_opts, opts);
11+
12+
skel = timer_start_deadlock__open_and_load();
13+
if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
14+
return;
15+
16+
err = timer_start_deadlock__attach(skel);
17+
if (!ASSERT_OK(err, "skel_attach"))
18+
goto cleanup;
19+
20+
prog_fd = bpf_program__fd(skel->progs.start_timer);
21+
22+
/*
23+
* Run the syscall program that attempts to deadlock.
24+
* If the kernel deadlocks, this call will never return.
25+
*/
26+
err = bpf_prog_test_run_opts(prog_fd, &opts);
27+
ASSERT_OK(err, "prog_test_run");
28+
ASSERT_EQ(opts.retval, 0, "prog_retval");
29+
30+
ASSERT_EQ(skel->bss->tp_called, 1, "tp_called");
31+
cleanup:
32+
timer_start_deadlock__destroy(skel);
33+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
3+
#include <vmlinux.h>
4+
#include <bpf/bpf_helpers.h>
5+
#include <bpf/bpf_tracing.h>
6+
7+
#define CLOCK_MONOTONIC 1
8+
9+
char _license[] SEC("license") = "GPL";
10+
11+
struct elem {
12+
struct bpf_timer timer;
13+
};
14+
15+
struct {
16+
__uint(type, BPF_MAP_TYPE_ARRAY);
17+
__uint(max_entries, 1);
18+
__type(key, int);
19+
__type(value, struct elem);
20+
} timer_map SEC(".maps");
21+
22+
volatile int in_timer_start;
23+
volatile int tp_called;
24+
25+
static int timer_cb(void *map, int *key, struct elem *value)
26+
{
27+
return 0;
28+
}
29+
30+
SEC("tp_btf/hrtimer_cancel")
31+
int BPF_PROG(tp_hrtimer_cancel, struct hrtimer *hrtimer)
32+
{
33+
struct bpf_timer *timer;
34+
static bool called = false;
35+
int key = 0;
36+
37+
if (!in_timer_start)
38+
return 0;
39+
40+
tp_called = 1;
41+
timer = bpf_map_lookup_elem(&timer_map, &key);
42+
43+
/*
44+
* Call bpf_timer_start() from the tracepoint within hrtimer logic
45+
* on the same timer to make sure it doesn't deadlock,
46+
* and do it once.
47+
*/
48+
if (!called) {
49+
called = true;
50+
bpf_timer_start(timer, 1000000000, 0);
51+
}
52+
return 0;
53+
}
54+
55+
SEC("syscall")
56+
int start_timer(void *ctx)
57+
{
58+
struct bpf_timer *timer;
59+
int key = 0;
60+
61+
timer = bpf_map_lookup_elem(&timer_map, &key);
62+
/* claude may complain here that there is no NULL check. Ignoring it. */
63+
bpf_timer_init(timer, &timer_map, CLOCK_MONOTONIC);
64+
bpf_timer_set_callback(timer, timer_cb);
65+
66+
/*
67+
* call hrtimer_start() twice, so that 2nd call does
68+
* remove_hrtimer() and trace_hrtimer_cancel() tracepoint.
69+
*/
70+
in_timer_start = 1;
71+
bpf_timer_start(timer, 1000000000, 0);
72+
bpf_timer_start(timer, 1000000000, 0);
73+
in_timer_start = 0;
74+
return 0;
75+
}

0 commit comments

Comments
 (0)