1 /* SPDX-License-Identifier: GPL-2.0-only
2  * Copyright (c) 2024 Benjamin Tissoires
3  */
4 
5 #ifndef __HID_BPF_ASYNC_H__
6 #define __HID_BPF_ASYNC_H__
7 
8 #ifndef HID_BPF_ASYNC_MAX_CTX
9 #error "HID_BPF_ASYNC_MAX_CTX should be set to the maximum number of concurrent async functions"
10 #endif /* HID_BPF_ASYNC_MAX_CTX */
11 
12 #define CLOCK_MONOTONIC		1
13 
14 typedef int (*hid_bpf_async_callback_t)(void *map, int *key, void *value);
15 
16 enum hid_bpf_async_state {
17 	HID_BPF_ASYNC_STATE_UNSET = 0,
18 	HID_BPF_ASYNC_STATE_INITIALIZING,
19 	HID_BPF_ASYNC_STATE_INITIALIZED,
20 	HID_BPF_ASYNC_STATE_STARTING,
21 	HID_BPF_ASYNC_STATE_RUNNING,
22 };
23 
24 struct hid_bpf_async_map_elem {
25 	struct bpf_spin_lock lock;
26 	enum hid_bpf_async_state state;
27 	struct bpf_timer t;
28 	struct bpf_wq wq;
29 	u32 hid;
30 };
31 
32 struct {
33 	__uint(type, BPF_MAP_TYPE_ARRAY);
34 	__uint(max_entries, HID_BPF_ASYNC_MAX_CTX);
35 	__type(key, u32);
36 	__type(value, struct hid_bpf_async_map_elem);
37 } hid_bpf_async_ctx_map SEC(".maps");
38 
39 /**
40  * HID_BPF_ASYNC_CB: macro to define an async callback used in a bpf_wq
41  *
42  * The caller is responsible for allocating a key in the async map
43  * with hid_bpf_async_get_ctx().
44  */
45 #define HID_BPF_ASYNC_CB(cb)					\
46 cb(void *map, int *key, void *value);				\
47 static __always_inline int					\
48 ____##cb(struct hid_bpf_ctx *ctx);				\
49 typeof(cb(0, 0, 0)) cb(void *map, int *key, void *value)	\
50 {								\
51 	struct hid_bpf_async_map_elem *e;				\
52 	struct hid_bpf_ctx *ctx;				\
53 								\
54 	e = (struct hid_bpf_async_map_elem *)value;			\
55 	ctx = hid_bpf_allocate_context(e->hid);			\
56 	if (!ctx)						\
57 		return 0; /* EPERM check */			\
58 								\
59 	e->state = HID_BPF_ASYNC_STATE_RUNNING;			\
60 								\
61 	____##cb(ctx);						\
62 								\
63 	e->state = HID_BPF_ASYNC_STATE_INITIALIZED;		\
64 	hid_bpf_release_context(ctx);				\
65 	return 0;						\
66 }								\
67 static __always_inline int					\
68 ____##cb
69 
70 /**
71  * ASYNC: macro to automatically handle async callbacks contexts
72  *
73  * Needs to be used in conjunction with HID_BPF_ASYNC_INIT and HID_BPF_ASYNC_DELAYED_CALL
74  */
75 #define HID_BPF_ASYNC_FUN(fun)						\
76 fun(struct hid_bpf_ctx *ctx);					\
77 int ____key__##fun;						\
78 static int ____async_init_##fun(void)				\
79 {								\
80 	____key__##fun = hid_bpf_async_get_ctx();			\
81 	if (____key__##fun < 0)					\
82 		return ____key__##fun;				\
83 	return 0;						\
84 }								\
85 static int HID_BPF_ASYNC_CB(____##fun##_cb)(struct hid_bpf_ctx *hctx)	\
86 {								\
87 	return fun(hctx);					\
88 }								\
89 typeof(fun(0)) fun
90 
91 #define HID_BPF_ASYNC_INIT(fun)	____async_init_##fun()
92 #define HID_BPF_ASYNC_DELAYED_CALL(fun, ctx, delay)		\
93 	hid_bpf_async_delayed_call(ctx, delay, ____key__##fun, ____##fun##_cb)
94 
95 /*
96  * internal cb for starting the delayed work callback in a workqueue.
97  */
__start_wq_timer_cb(void * map,int * key,void * value)98 static int __start_wq_timer_cb(void *map, int *key, void *value)
99 {
100 	struct hid_bpf_async_map_elem *e = (struct hid_bpf_async_map_elem *)value;
101 
102 	bpf_wq_start(&e->wq, 0);
103 
104 	return 0;
105 }
106 
hid_bpf_async_find_empty_key(void)107 static int hid_bpf_async_find_empty_key(void)
108 {
109 	int i;
110 
111 	bpf_for(i, 0, HID_BPF_ASYNC_MAX_CTX) {
112 		struct hid_bpf_async_map_elem *elem;
113 		int key = i;
114 
115 		elem = bpf_map_lookup_elem(&hid_bpf_async_ctx_map, &key);
116 		if (!elem)
117 			return -ENOMEM; /* should never happen */
118 
119 		bpf_spin_lock(&elem->lock);
120 
121 		if (elem->state == HID_BPF_ASYNC_STATE_UNSET) {
122 			elem->state = HID_BPF_ASYNC_STATE_INITIALIZING;
123 			bpf_spin_unlock(&elem->lock);
124 			return i;
125 		}
126 
127 		bpf_spin_unlock(&elem->lock);
128 	}
129 
130 	return -EINVAL;
131 }
132 
hid_bpf_async_get_ctx(void)133 static int hid_bpf_async_get_ctx(void)
134 {
135 	int key = hid_bpf_async_find_empty_key();
136 	struct hid_bpf_async_map_elem *elem;
137 	int err;
138 
139 	if (key < 0)
140 		return key;
141 
142 	elem = bpf_map_lookup_elem(&hid_bpf_async_ctx_map, &key);
143 	if (!elem)
144 		return -EINVAL;
145 
146 	err = bpf_timer_init(&elem->t, &hid_bpf_async_ctx_map, CLOCK_MONOTONIC);
147 	if (err)
148 		return err;
149 
150 	err = bpf_timer_set_callback(&elem->t, __start_wq_timer_cb);
151 	if (err)
152 		return err;
153 
154 	err = bpf_wq_init(&elem->wq, &hid_bpf_async_ctx_map, 0);
155 	if (err)
156 		return err;
157 
158 	elem->state = HID_BPF_ASYNC_STATE_INITIALIZED;
159 
160 	return key;
161 }
162 
ms_to_ns(u64 milliseconds)163 static inline u64 ms_to_ns(u64 milliseconds)
164 {
165 	return (u64)milliseconds * 1000UL * 1000UL;
166 }
167 
hid_bpf_async_delayed_call(struct hid_bpf_ctx * hctx,u64 milliseconds,int key,hid_bpf_async_callback_t wq_cb)168 static int hid_bpf_async_delayed_call(struct hid_bpf_ctx *hctx, u64 milliseconds, int key,
169 			      hid_bpf_async_callback_t wq_cb)
170 {
171 	struct hid_bpf_async_map_elem *elem;
172 	int err;
173 
174 	elem = bpf_map_lookup_elem(&hid_bpf_async_ctx_map, &key);
175 	if (!elem)
176 		return -EINVAL;
177 
178 	bpf_spin_lock(&elem->lock);
179 	/* The wq must be:
180 	 * - HID_BPF_ASYNC_STATE_INITIALIZED -> it's been initialized and ready to be called
181 	 * - HID_BPF_ASYNC_STATE_RUNNING -> possible re-entry from the wq itself
182 	 */
183 	if (elem->state != HID_BPF_ASYNC_STATE_INITIALIZED &&
184 	    elem->state != HID_BPF_ASYNC_STATE_RUNNING) {
185 		bpf_spin_unlock(&elem->lock);
186 		return -EINVAL;
187 	}
188 	elem->state = HID_BPF_ASYNC_STATE_STARTING;
189 	bpf_spin_unlock(&elem->lock);
190 
191 	elem->hid = hctx->hid->id;
192 
193 	err = bpf_wq_set_callback(&elem->wq, wq_cb, 0);
194 	if (err)
195 		return err;
196 
197 	if (milliseconds) {
198 		/* needed for every call because a cancel might unset this */
199 		err = bpf_timer_set_callback(&elem->t, __start_wq_timer_cb);
200 		if (err)
201 			return err;
202 
203 		err = bpf_timer_start(&elem->t, ms_to_ns(milliseconds), 0);
204 		if (err)
205 			return err;
206 
207 		return 0;
208 	}
209 
210 	return bpf_wq_start(&elem->wq, 0);
211 }
212 
hid_bpf_async_call(struct hid_bpf_ctx * ctx,int key,hid_bpf_async_callback_t wq_cb)213 static inline int hid_bpf_async_call(struct hid_bpf_ctx *ctx, int key,
214 				     hid_bpf_async_callback_t wq_cb)
215 {
216 	return hid_bpf_async_delayed_call(ctx, 0, key, wq_cb);
217 }
218 
219 #endif /* __HID_BPF_ASYNC_H__ */
220