xref: /linux/tools/testing/selftests/bpf/progs/timer_start_deadlock.c (revision 67ee5ad27d5101be4e9e8980c0734a0423bfd0a7)
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 }
76