xref: /linux/tools/testing/selftests/bpf/progs/timer_start_deadlock.c (revision 23b0f90ba871f096474e1c27c3d14f455189d2d9)
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 	int key = 0;
35 
36 	if (!in_timer_start)
37 		return 0;
38 
39 	tp_called = 1;
40 	timer = bpf_map_lookup_elem(&timer_map, &key);
41 
42 	/*
43 	 * Call bpf_timer_start() from the tracepoint within hrtimer logic
44 	 * on the same timer to make sure it doesn't deadlock.
45 	 */
46 	bpf_timer_start(timer, 1000000000, 0);
47 	return 0;
48 }
49 
50 SEC("syscall")
51 int start_timer(void *ctx)
52 {
53 	struct bpf_timer *timer;
54 	int key = 0;
55 
56 	timer = bpf_map_lookup_elem(&timer_map, &key);
57 	/* claude may complain here that there is no NULL check. Ignoring it. */
58 	bpf_timer_init(timer, &timer_map, CLOCK_MONOTONIC);
59 	bpf_timer_set_callback(timer, timer_cb);
60 
61 	/*
62 	 * call hrtimer_start() twice, so that 2nd call does
63 	 * remove_hrtimer() and trace_hrtimer_cancel() tracepoint.
64 	 */
65 	in_timer_start = 1;
66 	bpf_timer_start(timer, 1000000000, 0);
67 	bpf_timer_start(timer, 1000000000, 0);
68 	in_timer_start = 0;
69 	return 0;
70 }
71