xref: /linux/tools/testing/selftests/bpf/progs/timer_start_deadlock.c (revision c17ee635fd3a482b2ad2bf5e269755c2eae5f25e)
167ee5ad2SAlexei Starovoitov // SPDX-License-Identifier: GPL-2.0
267ee5ad2SAlexei Starovoitov /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
367ee5ad2SAlexei Starovoitov #include <vmlinux.h>
467ee5ad2SAlexei Starovoitov #include <bpf/bpf_helpers.h>
567ee5ad2SAlexei Starovoitov #include <bpf/bpf_tracing.h>
667ee5ad2SAlexei Starovoitov 
767ee5ad2SAlexei Starovoitov #define CLOCK_MONOTONIC 1
867ee5ad2SAlexei Starovoitov 
967ee5ad2SAlexei Starovoitov char _license[] SEC("license") = "GPL";
1067ee5ad2SAlexei Starovoitov 
1167ee5ad2SAlexei Starovoitov struct elem {
1267ee5ad2SAlexei Starovoitov 	struct bpf_timer timer;
1367ee5ad2SAlexei Starovoitov };
1467ee5ad2SAlexei Starovoitov 
1567ee5ad2SAlexei Starovoitov struct {
1667ee5ad2SAlexei Starovoitov 	__uint(type, BPF_MAP_TYPE_ARRAY);
1767ee5ad2SAlexei Starovoitov 	__uint(max_entries, 1);
1867ee5ad2SAlexei Starovoitov 	__type(key, int);
1967ee5ad2SAlexei Starovoitov 	__type(value, struct elem);
2067ee5ad2SAlexei Starovoitov } timer_map SEC(".maps");
2167ee5ad2SAlexei Starovoitov 
2267ee5ad2SAlexei Starovoitov volatile int in_timer_start;
2367ee5ad2SAlexei Starovoitov volatile int tp_called;
2467ee5ad2SAlexei Starovoitov 
2567ee5ad2SAlexei Starovoitov static int timer_cb(void *map, int *key, struct elem *value)
2667ee5ad2SAlexei Starovoitov {
2767ee5ad2SAlexei Starovoitov 	return 0;
2867ee5ad2SAlexei Starovoitov }
2967ee5ad2SAlexei Starovoitov 
3067ee5ad2SAlexei Starovoitov SEC("tp_btf/hrtimer_cancel")
3167ee5ad2SAlexei Starovoitov int BPF_PROG(tp_hrtimer_cancel, struct hrtimer *hrtimer)
3267ee5ad2SAlexei Starovoitov {
3367ee5ad2SAlexei Starovoitov 	struct bpf_timer *timer;
3467ee5ad2SAlexei Starovoitov 	int key = 0;
3567ee5ad2SAlexei Starovoitov 
3667ee5ad2SAlexei Starovoitov 	if (!in_timer_start)
3767ee5ad2SAlexei Starovoitov 		return 0;
3867ee5ad2SAlexei Starovoitov 
3967ee5ad2SAlexei Starovoitov 	tp_called = 1;
4067ee5ad2SAlexei Starovoitov 	timer = bpf_map_lookup_elem(&timer_map, &key);
4167ee5ad2SAlexei Starovoitov 
4267ee5ad2SAlexei Starovoitov 	/*
4367ee5ad2SAlexei Starovoitov 	 * Call bpf_timer_start() from the tracepoint within hrtimer logic
44*6e65cf81SAlexei Starovoitov 	 * on the same timer to make sure it doesn't deadlock.
4567ee5ad2SAlexei Starovoitov 	 */
4667ee5ad2SAlexei Starovoitov 	bpf_timer_start(timer, 1000000000, 0);
4767ee5ad2SAlexei Starovoitov 	return 0;
4867ee5ad2SAlexei Starovoitov }
4967ee5ad2SAlexei Starovoitov 
5067ee5ad2SAlexei Starovoitov SEC("syscall")
5167ee5ad2SAlexei Starovoitov int start_timer(void *ctx)
5267ee5ad2SAlexei Starovoitov {
5367ee5ad2SAlexei Starovoitov 	struct bpf_timer *timer;
5467ee5ad2SAlexei Starovoitov 	int key = 0;
5567ee5ad2SAlexei Starovoitov 
5667ee5ad2SAlexei Starovoitov 	timer = bpf_map_lookup_elem(&timer_map, &key);
5767ee5ad2SAlexei Starovoitov 	/* claude may complain here that there is no NULL check. Ignoring it. */
5867ee5ad2SAlexei Starovoitov 	bpf_timer_init(timer, &timer_map, CLOCK_MONOTONIC);
5967ee5ad2SAlexei Starovoitov 	bpf_timer_set_callback(timer, timer_cb);
6067ee5ad2SAlexei Starovoitov 
6167ee5ad2SAlexei Starovoitov 	/*
6267ee5ad2SAlexei Starovoitov 	 * call hrtimer_start() twice, so that 2nd call does
6367ee5ad2SAlexei Starovoitov 	 * remove_hrtimer() and trace_hrtimer_cancel() tracepoint.
6467ee5ad2SAlexei Starovoitov 	 */
6567ee5ad2SAlexei Starovoitov 	in_timer_start = 1;
6667ee5ad2SAlexei Starovoitov 	bpf_timer_start(timer, 1000000000, 0);
6767ee5ad2SAlexei Starovoitov 	bpf_timer_start(timer, 1000000000, 0);
6867ee5ad2SAlexei Starovoitov 	in_timer_start = 0;
6967ee5ad2SAlexei Starovoitov 	return 0;
7067ee5ad2SAlexei Starovoitov }
71