1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2021 Facebook */
6 #include <bpf/bpf_helpers.h>
7 #include "bpf_tcp_helpers.h"
9 char _license[] SEC("license") = "GPL";
12 struct bpf_timer timer;
13 struct bpf_spin_lock lock; /* unused */
17 __uint(type, BPF_MAP_TYPE_HASH);
18 __uint(max_entries, 1000);
20 __type(value, struct hmap_elem);
24 __uint(type, BPF_MAP_TYPE_HASH);
25 __uint(map_flags, BPF_F_NO_PREALLOC);
26 __uint(max_entries, 1000);
28 __type(value, struct hmap_elem);
29 } hmap_malloc SEC(".maps");
36 __uint(type, BPF_MAP_TYPE_ARRAY);
37 __uint(max_entries, 2);
39 __type(value, struct elem);
43 __uint(type, BPF_MAP_TYPE_LRU_HASH);
44 __uint(max_entries, 4);
46 __type(value, struct elem);
50 __uint(type, BPF_MAP_TYPE_ARRAY);
51 __uint(max_entries, 1);
53 __type(value, struct elem);
54 } abs_timer SEC(".maps");
60 __u64 callback_check = 52;
61 __u64 callback2_check = 52;
68 /* callback for array and lru timers */
69 static int timer_cb1(void *map, int *key, struct bpf_timer *timer)
71 /* increment bss variable twice.
72 * Once via array timer callback and once via lru timer callback
76 /* *key == 0 - the callback was called for array timer.
77 * *key == 4 - the callback was called from lru timer.
80 struct bpf_timer *lru_timer;
83 /* rearm array timer to be called again in ~35 seconds */
84 if (bpf_timer_start(timer, 1ull << 35, 0) != 0)
87 lru_timer = bpf_map_lookup_elem(&lru, &lru_key);
90 bpf_timer_set_callback(lru_timer, timer_cb1);
91 if (bpf_timer_start(lru_timer, 0, 0) != 0)
93 } else if (*key == LRU) {
97 i <= 100 /* for current LRU eviction algorithm this number
98 * should be larger than ~ lru->max_entries * 2
101 struct elem init = {};
103 /* lru_key cannot be used as loop induction variable
104 * otherwise the loop will be unbounded.
108 /* add more elements into lru map to push out current
109 * element and force deletion of this timer
111 bpf_map_update_elem(map, &lru_key, &init, 0);
112 /* look it up to bump it into active list */
113 bpf_map_lookup_elem(map, &lru_key);
115 /* keep adding until *key changes underneath,
116 * which means that key/timer memory was reused
122 /* check that the timer was removed */
123 if (bpf_timer_cancel(timer) != -EINVAL)
130 SEC("fentry/bpf_fentry_test1")
131 int BPF_PROG2(test1, int, a)
133 struct bpf_timer *arr_timer, *lru_timer;
134 struct elem init = {};
136 int array_key = ARRAY;
138 arr_timer = bpf_map_lookup_elem(&array, &array_key);
141 bpf_timer_init(arr_timer, &array, CLOCK_MONOTONIC);
143 bpf_map_update_elem(&lru, &lru_key, &init, 0);
144 lru_timer = bpf_map_lookup_elem(&lru, &lru_key);
147 bpf_timer_init(lru_timer, &lru, CLOCK_MONOTONIC);
149 bpf_timer_set_callback(arr_timer, timer_cb1);
150 bpf_timer_start(arr_timer, 0 /* call timer_cb1 asap */, 0);
152 /* init more timers to check that array destruction
153 * doesn't leak timer memory.
156 arr_timer = bpf_map_lookup_elem(&array, &array_key);
159 bpf_timer_init(arr_timer, &array, CLOCK_MONOTONIC);
163 /* callback for prealloc and non-prealloca hashtab timers */
164 static int timer_cb2(void *map, int *key, struct hmap_elem *val)
170 if (val->counter > 0 && --val->counter) {
171 /* re-arm the timer again to execute after 1 usec */
172 bpf_timer_start(&val->timer, 1000, 0);
173 } else if (*key == HTAB) {
174 struct bpf_timer *arr_timer;
175 int array_key = ARRAY;
177 /* cancel arr_timer otherwise bpf_fentry_test1 prog
178 * will stay alive forever.
180 arr_timer = bpf_map_lookup_elem(&array, &array_key);
183 if (bpf_timer_cancel(arr_timer) != 1)
184 /* bpf_timer_cancel should return 1 to indicate
185 * that arr_timer was active at this time
189 /* try to cancel ourself. It shouldn't deadlock. */
190 if (bpf_timer_cancel(&val->timer) != -EDEADLK)
193 /* delete this key and this timer anyway.
194 * It shouldn't deadlock either.
196 bpf_map_delete_elem(map, key);
198 /* in preallocated hashmap both 'key' and 'val' could have been
199 * reused to store another map element (like in LRU above),
200 * but in controlled test environment the below test works.
201 * It's not a use-after-free. The memory is owned by the map.
203 if (bpf_timer_start(&val->timer, 1000, 0) != -EINVAL)
207 if (*key != HTAB_MALLOC)
210 /* try to cancel ourself. It shouldn't deadlock. */
211 if (bpf_timer_cancel(&val->timer) != -EDEADLK)
214 /* delete this key and this timer anyway.
215 * It shouldn't deadlock either.
217 bpf_map_delete_elem(map, key);
224 int bpf_timer_test(void)
226 struct hmap_elem *val;
227 int key = HTAB, key_malloc = HTAB_MALLOC;
229 val = bpf_map_lookup_elem(&hmap, &key);
231 if (bpf_timer_init(&val->timer, &hmap, CLOCK_BOOTTIME) != 0)
233 bpf_timer_set_callback(&val->timer, timer_cb2);
234 bpf_timer_start(&val->timer, 1000, 0);
236 val = bpf_map_lookup_elem(&hmap_malloc, &key_malloc);
238 if (bpf_timer_init(&val->timer, &hmap_malloc, CLOCK_BOOTTIME) != 0)
240 bpf_timer_set_callback(&val->timer, timer_cb2);
241 bpf_timer_start(&val->timer, 1000, 0);
246 SEC("fentry/bpf_fentry_test2")
247 int BPF_PROG2(test2, int, a, int, b)
249 struct hmap_elem init = {}, *val;
250 int key = HTAB, key_malloc = HTAB_MALLOC;
252 init.counter = 10; /* number of times to trigger timer_cb2 */
253 bpf_map_update_elem(&hmap, &key, &init, 0);
254 val = bpf_map_lookup_elem(&hmap, &key);
256 bpf_timer_init(&val->timer, &hmap, CLOCK_BOOTTIME);
257 /* update the same key to free the timer */
258 bpf_map_update_elem(&hmap, &key, &init, 0);
260 bpf_map_update_elem(&hmap_malloc, &key_malloc, &init, 0);
261 val = bpf_map_lookup_elem(&hmap_malloc, &key_malloc);
263 bpf_timer_init(&val->timer, &hmap_malloc, CLOCK_BOOTTIME);
264 /* update the same key to free the timer */
265 bpf_map_update_elem(&hmap_malloc, &key_malloc, &init, 0);
267 /* init more timers to check that htab operations
268 * don't leak timer memory.
271 bpf_map_update_elem(&hmap, &key, &init, 0);
272 val = bpf_map_lookup_elem(&hmap, &key);
274 bpf_timer_init(&val->timer, &hmap, CLOCK_BOOTTIME);
275 bpf_map_delete_elem(&hmap, &key);
276 bpf_map_update_elem(&hmap, &key, &init, 0);
277 val = bpf_map_lookup_elem(&hmap, &key);
279 bpf_timer_init(&val->timer, &hmap, CLOCK_BOOTTIME);
281 /* and with non-prealloc htab */
283 bpf_map_update_elem(&hmap_malloc, &key_malloc, &init, 0);
284 val = bpf_map_lookup_elem(&hmap_malloc, &key_malloc);
286 bpf_timer_init(&val->timer, &hmap_malloc, CLOCK_BOOTTIME);
287 bpf_map_delete_elem(&hmap_malloc, &key_malloc);
288 bpf_map_update_elem(&hmap_malloc, &key_malloc, &init, 0);
289 val = bpf_map_lookup_elem(&hmap_malloc, &key_malloc);
291 bpf_timer_init(&val->timer, &hmap_malloc, CLOCK_BOOTTIME);
293 return bpf_timer_test();
296 /* callback for absolute timer */
297 static int timer_cb3(void *map, int *key, struct bpf_timer *timer)
302 bpf_timer_start(timer, bpf_ktime_get_boot_ns() + 1000,
305 /* Re-arm timer ~35 seconds in future */
306 bpf_timer_start(timer, bpf_ktime_get_boot_ns() + (1ull << 35),
313 SEC("fentry/bpf_fentry_test3")
314 int BPF_PROG2(test3, int, a)
317 struct bpf_timer *timer;
321 timer = bpf_map_lookup_elem(&abs_timer, &key);
323 if (bpf_timer_init(timer, &abs_timer, CLOCK_BOOTTIME) != 0)
325 bpf_timer_set_callback(timer, timer_cb3);
326 bpf_timer_start(timer, bpf_ktime_get_boot_ns() + 1000,