1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2024 Benjamin Tissoires
3 */
4
5 #include "bpf_experimental.h"
6 #include <bpf/bpf_helpers.h>
7 #include "bpf_misc.h"
8 #include "../bpf_testmod/bpf_testmod_kfunc.h"
9
10 char _license[] SEC("license") = "GPL";
11
12 struct hmap_elem {
13 int counter;
14 struct bpf_timer timer; /* unused */
15 struct bpf_spin_lock lock; /* unused */
16 struct bpf_wq work;
17 };
18
19 struct {
20 __uint(type, BPF_MAP_TYPE_HASH);
21 __uint(max_entries, 1000);
22 __type(key, int);
23 __type(value, struct hmap_elem);
24 } hmap SEC(".maps");
25
26 struct {
27 __uint(type, BPF_MAP_TYPE_HASH);
28 __uint(map_flags, BPF_F_NO_PREALLOC);
29 __uint(max_entries, 1000);
30 __type(key, int);
31 __type(value, struct hmap_elem);
32 } hmap_malloc SEC(".maps");
33
34 struct elem {
35 int ok_offset;
36 struct bpf_wq w;
37 };
38
39 struct {
40 __uint(type, BPF_MAP_TYPE_ARRAY);
41 __uint(max_entries, 2);
42 __type(key, int);
43 __type(value, struct elem);
44 } array SEC(".maps");
45
46 struct {
47 __uint(type, BPF_MAP_TYPE_LRU_HASH);
48 __uint(max_entries, 4);
49 __type(key, int);
50 __type(value, struct elem);
51 } lru SEC(".maps");
52
53 __u32 ok;
54 __u32 ok_sleepable;
55
test_elem_callback(void * map,int * key,int (callback_fn)(void * map,int * key,void * value))56 static int test_elem_callback(void *map, int *key,
57 int (callback_fn)(void *map, int *key, void *value))
58 {
59 struct elem init = {}, *val;
60 struct bpf_wq *wq;
61
62 if ((ok & (1 << *key) ||
63 (ok_sleepable & (1 << *key))))
64 return -22;
65
66 if (map == &lru &&
67 bpf_map_update_elem(map, key, &init, 0))
68 return -1;
69
70 val = bpf_map_lookup_elem(map, key);
71 if (!val)
72 return -2;
73
74 val->ok_offset = *key;
75
76 wq = &val->w;
77 if (bpf_wq_init(wq, map, 0) != 0)
78 return -3;
79
80 if (bpf_wq_set_callback(wq, callback_fn, 0))
81 return -4;
82
83 if (bpf_wq_start(wq, 0))
84 return -5;
85
86 return 0;
87 }
88
test_hmap_elem_callback(void * map,int * key,int (callback_fn)(void * map,int * key,void * value))89 static int test_hmap_elem_callback(void *map, int *key,
90 int (callback_fn)(void *map, int *key, void *value))
91 {
92 struct hmap_elem init = {}, *val;
93 struct bpf_wq *wq;
94
95 if ((ok & (1 << *key) ||
96 (ok_sleepable & (1 << *key))))
97 return -22;
98
99 if (bpf_map_update_elem(map, key, &init, 0))
100 return -1;
101
102 val = bpf_map_lookup_elem(map, key);
103 if (!val)
104 return -2;
105
106 wq = &val->work;
107 if (bpf_wq_init(wq, map, 0) != 0)
108 return -3;
109
110 if (bpf_wq_set_callback(wq, callback_fn, 0))
111 return -4;
112
113 if (bpf_wq_start(wq, 0))
114 return -5;
115
116 return 0;
117 }
118
119 /* callback for non sleepable workqueue */
wq_callback(void * map,int * key,void * value)120 static int wq_callback(void *map, int *key, void *value)
121 {
122 bpf_kfunc_common_test();
123 ok |= (1 << *key);
124 return 0;
125 }
126
127 /* callback for sleepable workqueue */
wq_cb_sleepable(void * map,int * key,void * value)128 static int wq_cb_sleepable(void *map, int *key, void *value)
129 {
130 struct elem *data = (struct elem *)value;
131 int offset = data->ok_offset;
132
133 if (*key != offset)
134 return 0;
135
136 bpf_kfunc_call_test_sleepable();
137 ok_sleepable |= (1 << offset);
138 return 0;
139 }
140
141 SEC("tc")
142 /* test that workqueues can be used from an array */
143 __retval(0)
test_call_array_sleepable(void * ctx)144 long test_call_array_sleepable(void *ctx)
145 {
146 int key = 0;
147
148 return test_elem_callback(&array, &key, wq_cb_sleepable);
149 }
150
151 SEC("syscall")
152 /* Same test than above but from a sleepable context. */
153 __retval(0)
test_syscall_array_sleepable(void * ctx)154 long test_syscall_array_sleepable(void *ctx)
155 {
156 int key = 1;
157
158 return test_elem_callback(&array, &key, wq_cb_sleepable);
159 }
160
161 SEC("tc")
162 /* test that workqueues can be used from a hashmap */
163 __retval(0)
test_call_hash_sleepable(void * ctx)164 long test_call_hash_sleepable(void *ctx)
165 {
166 int key = 2;
167
168 return test_hmap_elem_callback(&hmap, &key, wq_callback);
169 }
170
171 SEC("tc")
172 /* test that workqueues can be used from a hashmap with NO_PREALLOC. */
173 __retval(0)
test_call_hash_malloc_sleepable(void * ctx)174 long test_call_hash_malloc_sleepable(void *ctx)
175 {
176 int key = 3;
177
178 return test_hmap_elem_callback(&hmap_malloc, &key, wq_callback);
179 }
180
181 SEC("tc")
182 /* test that workqueues can be used from a LRU map */
183 __retval(0)
test_call_lru_sleepable(void * ctx)184 long test_call_lru_sleepable(void *ctx)
185 {
186 int key = 4;
187
188 return test_elem_callback(&lru, &key, wq_callback);
189 }
190