timer.c (7969B)
1// SPDX-License-Identifier: GPL-2.0 2/* Copyright (c) 2021 Facebook */ 3#include <linux/bpf.h> 4#include <time.h> 5#include <errno.h> 6#include <bpf/bpf_helpers.h> 7#include "bpf_tcp_helpers.h" 8 9char _license[] SEC("license") = "GPL"; 10struct hmap_elem { 11 int counter; 12 struct bpf_timer timer; 13 struct bpf_spin_lock lock; /* unused */ 14}; 15 16struct { 17 __uint(type, BPF_MAP_TYPE_HASH); 18 __uint(max_entries, 1000); 19 __type(key, int); 20 __type(value, struct hmap_elem); 21} hmap SEC(".maps"); 22 23struct { 24 __uint(type, BPF_MAP_TYPE_HASH); 25 __uint(map_flags, BPF_F_NO_PREALLOC); 26 __uint(max_entries, 1000); 27 __type(key, int); 28 __type(value, struct hmap_elem); 29} hmap_malloc SEC(".maps"); 30 31struct elem { 32 struct bpf_timer t; 33}; 34 35struct { 36 __uint(type, BPF_MAP_TYPE_ARRAY); 37 __uint(max_entries, 2); 38 __type(key, int); 39 __type(value, struct elem); 40} array SEC(".maps"); 41 42struct { 43 __uint(type, BPF_MAP_TYPE_LRU_HASH); 44 __uint(max_entries, 4); 45 __type(key, int); 46 __type(value, struct elem); 47} lru SEC(".maps"); 48 49__u64 bss_data; 50__u64 err; 51__u64 ok; 52__u64 callback_check = 52; 53__u64 callback2_check = 52; 54 55#define ARRAY 1 56#define HTAB 2 57#define HTAB_MALLOC 3 58#define LRU 4 59 60/* callback for array and lru timers */ 61static int timer_cb1(void *map, int *key, struct bpf_timer *timer) 62{ 63 /* increment bss variable twice. 64 * Once via array timer callback and once via lru timer callback 65 */ 66 bss_data += 5; 67 68 /* *key == 0 - the callback was called for array timer. 69 * *key == 4 - the callback was called from lru timer. 70 */ 71 if (*key == ARRAY) { 72 struct bpf_timer *lru_timer; 73 int lru_key = LRU; 74 75 /* rearm array timer to be called again in ~35 seconds */ 76 if (bpf_timer_start(timer, 1ull << 35, 0) != 0) 77 err |= 1; 78 79 lru_timer = bpf_map_lookup_elem(&lru, &lru_key); 80 if (!lru_timer) 81 return 0; 82 bpf_timer_set_callback(lru_timer, timer_cb1); 83 if (bpf_timer_start(lru_timer, 0, 0) != 0) 84 err |= 2; 85 } else if (*key == LRU) { 86 int lru_key, i; 87 88 for (i = LRU + 1; 89 i <= 100 /* for current LRU eviction algorithm this number 90 * should be larger than ~ lru->max_entries * 2 91 */; 92 i++) { 93 struct elem init = {}; 94 95 /* lru_key cannot be used as loop induction variable 96 * otherwise the loop will be unbounded. 97 */ 98 lru_key = i; 99 100 /* add more elements into lru map to push out current 101 * element and force deletion of this timer 102 */ 103 bpf_map_update_elem(map, &lru_key, &init, 0); 104 /* look it up to bump it into active list */ 105 bpf_map_lookup_elem(map, &lru_key); 106 107 /* keep adding until *key changes underneath, 108 * which means that key/timer memory was reused 109 */ 110 if (*key != LRU) 111 break; 112 } 113 114 /* check that the timer was removed */ 115 if (bpf_timer_cancel(timer) != -EINVAL) 116 err |= 4; 117 ok |= 1; 118 } 119 return 0; 120} 121 122SEC("fentry/bpf_fentry_test1") 123int BPF_PROG(test1, int a) 124{ 125 struct bpf_timer *arr_timer, *lru_timer; 126 struct elem init = {}; 127 int lru_key = LRU; 128 int array_key = ARRAY; 129 130 arr_timer = bpf_map_lookup_elem(&array, &array_key); 131 if (!arr_timer) 132 return 0; 133 bpf_timer_init(arr_timer, &array, CLOCK_MONOTONIC); 134 135 bpf_map_update_elem(&lru, &lru_key, &init, 0); 136 lru_timer = bpf_map_lookup_elem(&lru, &lru_key); 137 if (!lru_timer) 138 return 0; 139 bpf_timer_init(lru_timer, &lru, CLOCK_MONOTONIC); 140 141 bpf_timer_set_callback(arr_timer, timer_cb1); 142 bpf_timer_start(arr_timer, 0 /* call timer_cb1 asap */, 0); 143 144 /* init more timers to check that array destruction 145 * doesn't leak timer memory. 146 */ 147 array_key = 0; 148 arr_timer = bpf_map_lookup_elem(&array, &array_key); 149 if (!arr_timer) 150 return 0; 151 bpf_timer_init(arr_timer, &array, CLOCK_MONOTONIC); 152 return 0; 153} 154 155/* callback for prealloc and non-prealloca hashtab timers */ 156static int timer_cb2(void *map, int *key, struct hmap_elem *val) 157{ 158 if (*key == HTAB) 159 callback_check--; 160 else 161 callback2_check--; 162 if (val->counter > 0 && --val->counter) { 163 /* re-arm the timer again to execute after 1 usec */ 164 bpf_timer_start(&val->timer, 1000, 0); 165 } else if (*key == HTAB) { 166 struct bpf_timer *arr_timer; 167 int array_key = ARRAY; 168 169 /* cancel arr_timer otherwise bpf_fentry_test1 prog 170 * will stay alive forever. 171 */ 172 arr_timer = bpf_map_lookup_elem(&array, &array_key); 173 if (!arr_timer) 174 return 0; 175 if (bpf_timer_cancel(arr_timer) != 1) 176 /* bpf_timer_cancel should return 1 to indicate 177 * that arr_timer was active at this time 178 */ 179 err |= 8; 180 181 /* try to cancel ourself. It shouldn't deadlock. */ 182 if (bpf_timer_cancel(&val->timer) != -EDEADLK) 183 err |= 16; 184 185 /* delete this key and this timer anyway. 186 * It shouldn't deadlock either. 187 */ 188 bpf_map_delete_elem(map, key); 189 190 /* in preallocated hashmap both 'key' and 'val' could have been 191 * reused to store another map element (like in LRU above), 192 * but in controlled test environment the below test works. 193 * It's not a use-after-free. The memory is owned by the map. 194 */ 195 if (bpf_timer_start(&val->timer, 1000, 0) != -EINVAL) 196 err |= 32; 197 ok |= 2; 198 } else { 199 if (*key != HTAB_MALLOC) 200 err |= 64; 201 202 /* try to cancel ourself. It shouldn't deadlock. */ 203 if (bpf_timer_cancel(&val->timer) != -EDEADLK) 204 err |= 128; 205 206 /* delete this key and this timer anyway. 207 * It shouldn't deadlock either. 208 */ 209 bpf_map_delete_elem(map, key); 210 211 /* in non-preallocated hashmap both 'key' and 'val' are RCU 212 * protected and still valid though this element was deleted 213 * from the map. Arm this timer for ~35 seconds. When callback 214 * finishes the call_rcu will invoke: 215 * htab_elem_free_rcu 216 * check_and_free_timer 217 * bpf_timer_cancel_and_free 218 * to cancel this 35 second sleep and delete the timer for real. 219 */ 220 if (bpf_timer_start(&val->timer, 1ull << 35, 0) != 0) 221 err |= 256; 222 ok |= 4; 223 } 224 return 0; 225} 226 227int bpf_timer_test(void) 228{ 229 struct hmap_elem *val; 230 int key = HTAB, key_malloc = HTAB_MALLOC; 231 232 val = bpf_map_lookup_elem(&hmap, &key); 233 if (val) { 234 if (bpf_timer_init(&val->timer, &hmap, CLOCK_BOOTTIME) != 0) 235 err |= 512; 236 bpf_timer_set_callback(&val->timer, timer_cb2); 237 bpf_timer_start(&val->timer, 1000, 0); 238 } 239 val = bpf_map_lookup_elem(&hmap_malloc, &key_malloc); 240 if (val) { 241 if (bpf_timer_init(&val->timer, &hmap_malloc, CLOCK_BOOTTIME) != 0) 242 err |= 1024; 243 bpf_timer_set_callback(&val->timer, timer_cb2); 244 bpf_timer_start(&val->timer, 1000, 0); 245 } 246 return 0; 247} 248 249SEC("fentry/bpf_fentry_test2") 250int BPF_PROG(test2, int a, int b) 251{ 252 struct hmap_elem init = {}, *val; 253 int key = HTAB, key_malloc = HTAB_MALLOC; 254 255 init.counter = 10; /* number of times to trigger timer_cb2 */ 256 bpf_map_update_elem(&hmap, &key, &init, 0); 257 val = bpf_map_lookup_elem(&hmap, &key); 258 if (val) 259 bpf_timer_init(&val->timer, &hmap, CLOCK_BOOTTIME); 260 /* update the same key to free the timer */ 261 bpf_map_update_elem(&hmap, &key, &init, 0); 262 263 bpf_map_update_elem(&hmap_malloc, &key_malloc, &init, 0); 264 val = bpf_map_lookup_elem(&hmap_malloc, &key_malloc); 265 if (val) 266 bpf_timer_init(&val->timer, &hmap_malloc, CLOCK_BOOTTIME); 267 /* update the same key to free the timer */ 268 bpf_map_update_elem(&hmap_malloc, &key_malloc, &init, 0); 269 270 /* init more timers to check that htab operations 271 * don't leak timer memory. 272 */ 273 key = 0; 274 bpf_map_update_elem(&hmap, &key, &init, 0); 275 val = bpf_map_lookup_elem(&hmap, &key); 276 if (val) 277 bpf_timer_init(&val->timer, &hmap, CLOCK_BOOTTIME); 278 bpf_map_delete_elem(&hmap, &key); 279 bpf_map_update_elem(&hmap, &key, &init, 0); 280 val = bpf_map_lookup_elem(&hmap, &key); 281 if (val) 282 bpf_timer_init(&val->timer, &hmap, CLOCK_BOOTTIME); 283 284 /* and with non-prealloc htab */ 285 key_malloc = 0; 286 bpf_map_update_elem(&hmap_malloc, &key_malloc, &init, 0); 287 val = bpf_map_lookup_elem(&hmap_malloc, &key_malloc); 288 if (val) 289 bpf_timer_init(&val->timer, &hmap_malloc, CLOCK_BOOTTIME); 290 bpf_map_delete_elem(&hmap_malloc, &key_malloc); 291 bpf_map_update_elem(&hmap_malloc, &key_malloc, &init, 0); 292 val = bpf_map_lookup_elem(&hmap_malloc, &key_malloc); 293 if (val) 294 bpf_timer_init(&val->timer, &hmap_malloc, CLOCK_BOOTTIME); 295 296 return bpf_timer_test(); 297}