xref: /linux/tools/testing/selftests/bpf/progs/test_wakeup_source.c (revision 12e896b9794bbd88f56aeac2a5807ae8d4bb5ad8)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright 2026 Google LLC */
3 
4 #include "vmlinux.h"
5 #include <bpf/bpf_helpers.h>
6 #include <bpf/bpf_core_read.h>
7 #include "bpf_experimental.h"
8 #include "bpf_misc.h"
9 #include "wakeup_source.h"
10 
11 #define MAX_LOOP_ITER 1000
12 #define RB_SIZE (16384 * 4)
13 
14 struct {
15 	__uint(type, BPF_MAP_TYPE_RINGBUF);
16 	__uint(max_entries, RB_SIZE);
17 } rb SEC(".maps");
18 
19 struct bpf_ws_lock;
20 struct bpf_ws_lock *bpf_wakeup_sources_read_lock(void) __ksym;
21 void bpf_wakeup_sources_read_unlock(struct bpf_ws_lock *lock) __ksym;
22 void *bpf_wakeup_sources_get_head(void) __ksym;
23 
24 SEC("syscall")
25 __success __retval(0)
26 int iterate_wakeupsources(void *ctx)
27 {
28 	struct list_head *head = bpf_wakeup_sources_get_head();
29 	struct list_head *pos = head;
30 	struct bpf_ws_lock *lock;
31 	int i;
32 
33 	lock = bpf_wakeup_sources_read_lock();
34 	if (!lock)
35 		return 0;
36 
37 	bpf_for(i, 0, MAX_LOOP_ITER) {
38 		if (bpf_core_read(&pos, sizeof(pos), &pos->next) || !pos || pos == head)
39 			break;
40 
41 		struct wakeup_event_t *e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);
42 
43 		if (!e)
44 			break;
45 
46 		struct wakeup_source *ws = bpf_core_cast(
47 				(void *)pos - bpf_core_field_offset(struct wakeup_source, entry),
48 				struct wakeup_source);
49 		s64 active_time = 0;
50 		bool active = BPF_CORE_READ_BITFIELD(ws, active);
51 		bool autosleep_enable = BPF_CORE_READ_BITFIELD(ws, autosleep_enabled);
52 		s64 last_time = ws->last_time;
53 		s64 max_time = ws->max_time;
54 		s64 prevent_sleep_time = ws->prevent_sleep_time;
55 		s64 total_time = ws->total_time;
56 
57 		if (active) {
58 			s64 curr_time = bpf_ktime_get_ns();
59 			s64 prevent_time = ws->start_prevent_time;
60 
61 			if (curr_time > last_time)
62 				active_time = curr_time - last_time;
63 
64 			total_time += active_time;
65 			if (active_time > max_time)
66 				max_time = active_time;
67 			if (autosleep_enable && curr_time > prevent_time)
68 				prevent_sleep_time += curr_time - prevent_time;
69 		}
70 
71 		e->active_count = ws->active_count;
72 		e->active_time_ns = active_time;
73 		e->event_count = ws->event_count;
74 		e->expire_count = ws->expire_count;
75 		e->last_time_ns = last_time;
76 		e->max_time_ns = max_time;
77 		e->prevent_sleep_time_ns = prevent_sleep_time;
78 		e->total_time_ns = total_time;
79 		e->wakeup_count = ws->wakeup_count;
80 
81 		if (bpf_probe_read_kernel_str(
82 				e->name, WAKEUP_NAME_LEN, ws->name) < 0)
83 			e->name[0] = '\0';
84 
85 		bpf_ringbuf_submit(e, 0);
86 	}
87 
88 	bpf_wakeup_sources_read_unlock(lock);
89 	return 0;
90 }
91 
92 char _license[] SEC("license") = "GPL";
93