1
2
3
4
5
6
7
8
9
10
11#include <stdio.h>
12#include <unistd.h>
13#include <linux/bpf.h>
14#include <errno.h>
15#include <string.h>
16#include <assert.h>
17#include <sys/wait.h>
18#include <stdlib.h>
19#include "libbpf.h"
20
21static int map_flags;
22
23
24static void test_hashmap_sanity(int i, void *data)
25{
26 long long key, next_key, value;
27 int map_fd;
28
29 map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
30 2, map_flags);
31 if (map_fd < 0) {
32 printf("failed to create hashmap '%s'\n", strerror(errno));
33 exit(1);
34 }
35
36 key = 1;
37 value = 1234;
38
39 assert(bpf_update_elem(map_fd, &key, &value, BPF_ANY) == 0);
40
41 value = 0;
42
43 assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 &&
44
45 errno == EEXIST);
46
47 assert(bpf_update_elem(map_fd, &key, &value, -1) == -1 && errno == EINVAL);
48
49
50 assert(bpf_lookup_elem(map_fd, &key, &value) == 0 && value == 1234);
51
52 key = 2;
53
54 assert(bpf_lookup_elem(map_fd, &key, &value) == -1 && errno == ENOENT);
55
56
57 assert(bpf_update_elem(map_fd, &key, &value, BPF_EXIST) == -1 &&
58
59 errno == ENOENT);
60
61
62 assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == 0);
63
64
65
66
67 key = 0;
68 assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 &&
69 errno == E2BIG);
70
71
72 assert(bpf_delete_elem(map_fd, &key) == -1 && errno == ENOENT);
73
74
75 assert(bpf_get_next_key(map_fd, &key, &next_key) == 0 &&
76 (next_key == 1 || next_key == 2));
77 assert(bpf_get_next_key(map_fd, &next_key, &next_key) == 0 &&
78 (next_key == 1 || next_key == 2));
79 assert(bpf_get_next_key(map_fd, &next_key, &next_key) == -1 &&
80 errno == ENOENT);
81
82
83 key = 1;
84 assert(bpf_delete_elem(map_fd, &key) == 0);
85 key = 2;
86 assert(bpf_delete_elem(map_fd, &key) == 0);
87 assert(bpf_delete_elem(map_fd, &key) == -1 && errno == ENOENT);
88
89 key = 0;
90
91 assert(bpf_get_next_key(map_fd, &key, &next_key) == -1 &&
92 errno == ENOENT);
93 close(map_fd);
94}
95
96
97static void test_percpu_hashmap_sanity(int task, void *data)
98{
99 long long key, next_key;
100 int expected_key_mask = 0;
101 unsigned int nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
102 long long value[nr_cpus];
103 int map_fd, i;
104
105 map_fd = bpf_create_map(BPF_MAP_TYPE_PERCPU_HASH, sizeof(key),
106 sizeof(value[0]), 2, map_flags);
107 if (map_fd < 0) {
108 printf("failed to create hashmap '%s'\n", strerror(errno));
109 exit(1);
110 }
111
112 for (i = 0; i < nr_cpus; i++)
113 value[i] = i + 100;
114 key = 1;
115
116 assert(!(expected_key_mask & key));
117 assert(bpf_update_elem(map_fd, &key, value, BPF_ANY) == 0);
118 expected_key_mask |= key;
119
120
121 assert(bpf_update_elem(map_fd, &key, value, BPF_NOEXIST) == -1 &&
122
123 errno == EEXIST);
124
125
126 assert(bpf_update_elem(map_fd, &key, value, -1) == -1 &&
127 errno == EINVAL);
128
129
130
131
132 value[0] = 1;
133 assert(bpf_lookup_elem(map_fd, &key, value) == 0 && value[0] == 100);
134
135 key = 2;
136
137 assert(bpf_lookup_elem(map_fd, &key, value) == -1 && errno == ENOENT);
138
139
140 assert(bpf_update_elem(map_fd, &key, value, BPF_EXIST) == -1 &&
141
142 errno == ENOENT);
143
144
145 assert(!(expected_key_mask & key));
146 assert(bpf_update_elem(map_fd, &key, value, BPF_NOEXIST) == 0);
147 expected_key_mask |= key;
148
149
150
151
152 key = 0;
153 assert(bpf_update_elem(map_fd, &key, value, BPF_NOEXIST) == -1 &&
154 errno == E2BIG);
155
156
157 assert(bpf_delete_elem(map_fd, &key) == -1 && errno == ENOENT);
158
159
160 while (!bpf_get_next_key(map_fd, &key, &next_key)) {
161 assert((expected_key_mask & next_key) == next_key);
162 expected_key_mask &= ~next_key;
163
164 assert(bpf_lookup_elem(map_fd, &next_key, value) == 0);
165 for (i = 0; i < nr_cpus; i++)
166 assert(value[i] == i + 100);
167
168 key = next_key;
169 }
170 assert(errno == ENOENT);
171
172
173 key = 1;
174 assert(bpf_update_elem(map_fd, &key, value, BPF_EXIST) == 0);
175
176
177 key = 1;
178 assert(bpf_delete_elem(map_fd, &key) == 0);
179 key = 2;
180 assert(bpf_delete_elem(map_fd, &key) == 0);
181 assert(bpf_delete_elem(map_fd, &key) == -1 && errno == ENOENT);
182
183 key = 0;
184
185 assert(bpf_get_next_key(map_fd, &key, &next_key) == -1 &&
186 errno == ENOENT);
187 close(map_fd);
188}
189
190static void test_arraymap_sanity(int i, void *data)
191{
192 int key, next_key, map_fd;
193 long long value;
194
195 map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(key), sizeof(value),
196 2, 0);
197 if (map_fd < 0) {
198 printf("failed to create arraymap '%s'\n", strerror(errno));
199 exit(1);
200 }
201
202 key = 1;
203 value = 1234;
204
205 assert(bpf_update_elem(map_fd, &key, &value, BPF_ANY) == 0);
206
207 value = 0;
208 assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 &&
209 errno == EEXIST);
210
211
212 assert(bpf_lookup_elem(map_fd, &key, &value) == 0 && value == 1234);
213
214 key = 0;
215
216 assert(bpf_lookup_elem(map_fd, &key, &value) == 0 && value == 0);
217
218
219
220
221
222 key = 2;
223 assert(bpf_update_elem(map_fd, &key, &value, BPF_EXIST) == -1 &&
224 errno == E2BIG);
225
226
227 assert(bpf_lookup_elem(map_fd, &key, &value) == -1 && errno == ENOENT);
228
229
230 assert(bpf_get_next_key(map_fd, &key, &next_key) == 0 &&
231 next_key == 0);
232 assert(bpf_get_next_key(map_fd, &next_key, &next_key) == 0 &&
233 next_key == 1);
234 assert(bpf_get_next_key(map_fd, &next_key, &next_key) == -1 &&
235 errno == ENOENT);
236
237
238 key = 1;
239 assert(bpf_delete_elem(map_fd, &key) == -1 && errno == EINVAL);
240
241 close(map_fd);
242}
243
244static void test_percpu_arraymap_many_keys(void)
245{
246 unsigned nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
247 unsigned nr_keys = 20000;
248 long values[nr_cpus];
249 int key, map_fd, i;
250
251 map_fd = bpf_create_map(BPF_MAP_TYPE_PERCPU_ARRAY, sizeof(key),
252 sizeof(values[0]), nr_keys, 0);
253 if (map_fd < 0) {
254 printf("failed to create per-cpu arraymap '%s'\n",
255 strerror(errno));
256 exit(1);
257 }
258
259 for (i = 0; i < nr_cpus; i++)
260 values[i] = i + 10;
261
262 for (key = 0; key < nr_keys; key++)
263 assert(bpf_update_elem(map_fd, &key, values, BPF_ANY) == 0);
264
265 for (key = 0; key < nr_keys; key++) {
266 for (i = 0; i < nr_cpus; i++)
267 values[i] = 0;
268 assert(bpf_lookup_elem(map_fd, &key, values) == 0);
269 for (i = 0; i < nr_cpus; i++)
270 assert(values[i] == i + 10);
271 }
272
273 close(map_fd);
274}
275
276static void test_percpu_arraymap_sanity(int i, void *data)
277{
278 unsigned nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
279 long values[nr_cpus];
280 int key, next_key, map_fd;
281
282 map_fd = bpf_create_map(BPF_MAP_TYPE_PERCPU_ARRAY, sizeof(key),
283 sizeof(values[0]), 2, 0);
284 if (map_fd < 0) {
285 printf("failed to create arraymap '%s'\n", strerror(errno));
286 exit(1);
287 }
288
289 for (i = 0; i < nr_cpus; i++)
290 values[i] = i + 100;
291
292 key = 1;
293
294 assert(bpf_update_elem(map_fd, &key, values, BPF_ANY) == 0);
295
296 values[0] = 0;
297 assert(bpf_update_elem(map_fd, &key, values, BPF_NOEXIST) == -1 &&
298 errno == EEXIST);
299
300
301 assert(bpf_lookup_elem(map_fd, &key, values) == 0 && values[0] == 100);
302
303 key = 0;
304
305 assert(bpf_lookup_elem(map_fd, &key, values) == 0 &&
306 values[0] == 0 && values[nr_cpus - 1] == 0);
307
308
309
310 key = 2;
311 assert(bpf_update_elem(map_fd, &key, values, BPF_EXIST) == -1 &&
312 errno == E2BIG);
313
314
315 assert(bpf_lookup_elem(map_fd, &key, values) == -1 && errno == ENOENT);
316
317
318 assert(bpf_get_next_key(map_fd, &key, &next_key) == 0 &&
319 next_key == 0);
320 assert(bpf_get_next_key(map_fd, &next_key, &next_key) == 0 &&
321 next_key == 1);
322 assert(bpf_get_next_key(map_fd, &next_key, &next_key) == -1 &&
323 errno == ENOENT);
324
325
326 key = 1;
327 assert(bpf_delete_elem(map_fd, &key) == -1 && errno == EINVAL);
328
329 close(map_fd);
330}
331
332#define MAP_SIZE (32 * 1024)
333static void test_map_large(void)
334{
335 struct bigkey {
336 int a;
337 char b[116];
338 long long c;
339 } key;
340 int map_fd, i, value;
341
342
343 map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
344 MAP_SIZE, map_flags);
345 if (map_fd < 0) {
346 printf("failed to create large map '%s'\n", strerror(errno));
347 exit(1);
348 }
349
350 for (i = 0; i < MAP_SIZE; i++) {
351 key = (struct bigkey) {.c = i};
352 value = i;
353 assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == 0);
354 }
355 key.c = -1;
356 assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 &&
357 errno == E2BIG);
358
359
360 for (i = 0; i < MAP_SIZE; i++)
361 assert(bpf_get_next_key(map_fd, &key, &key) == 0);
362 assert(bpf_get_next_key(map_fd, &key, &key) == -1 && errno == ENOENT);
363
364 key.c = 0;
365 assert(bpf_lookup_elem(map_fd, &key, &value) == 0 && value == 0);
366 key.a = 1;
367 assert(bpf_lookup_elem(map_fd, &key, &value) == -1 && errno == ENOENT);
368
369 close(map_fd);
370}
371
372
373static void run_parallel(int tasks, void (*fn)(int i, void *data), void *data)
374{
375 pid_t pid[tasks];
376 int i;
377
378 for (i = 0; i < tasks; i++) {
379 pid[i] = fork();
380 if (pid[i] == 0) {
381 fn(i, data);
382 exit(0);
383 } else if (pid[i] == -1) {
384 printf("couldn't spawn #%d process\n", i);
385 exit(1);
386 }
387 }
388 for (i = 0; i < tasks; i++) {
389 int status;
390
391 assert(waitpid(pid[i], &status, 0) == pid[i]);
392 assert(status == 0);
393 }
394}
395
396static void test_map_stress(void)
397{
398 run_parallel(100, test_hashmap_sanity, NULL);
399 run_parallel(100, test_percpu_hashmap_sanity, NULL);
400 run_parallel(100, test_arraymap_sanity, NULL);
401 run_parallel(100, test_percpu_arraymap_sanity, NULL);
402}
403
404#define TASKS 1024
405#define DO_UPDATE 1
406#define DO_DELETE 0
407static void do_work(int fn, void *data)
408{
409 int map_fd = ((int *)data)[0];
410 int do_update = ((int *)data)[1];
411 int i;
412 int key, value;
413
414 for (i = fn; i < MAP_SIZE; i += TASKS) {
415 key = value = i;
416 if (do_update)
417 assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == 0);
418 else
419 assert(bpf_delete_elem(map_fd, &key) == 0);
420 }
421}
422
423static void test_map_parallel(void)
424{
425 int i, map_fd, key = 0, value = 0;
426 int data[2];
427
428 map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
429 MAP_SIZE, map_flags);
430 if (map_fd < 0) {
431 printf("failed to create map for parallel test '%s'\n",
432 strerror(errno));
433 exit(1);
434 }
435
436 data[0] = map_fd;
437 data[1] = DO_UPDATE;
438
439
440
441
442
443 run_parallel(TASKS, do_work, data);
444
445
446 assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 &&
447 errno == EEXIST);
448
449
450 key = -1;
451 for (i = 0; i < MAP_SIZE; i++)
452 assert(bpf_get_next_key(map_fd, &key, &key) == 0);
453 assert(bpf_get_next_key(map_fd, &key, &key) == -1 && errno == ENOENT);
454
455
456 for (i = 0; i < MAP_SIZE; i++) {
457 key = MAP_SIZE - i - 1;
458 assert(bpf_lookup_elem(map_fd, &key, &value) == 0 &&
459 value == key);
460 }
461
462
463 data[1] = DO_DELETE;
464 run_parallel(TASKS, do_work, data);
465
466
467 key = -1;
468 assert(bpf_get_next_key(map_fd, &key, &key) == -1 && errno == ENOENT);
469}
470
471static void run_all_tests(void)
472{
473 test_hashmap_sanity(0, NULL);
474 test_percpu_hashmap_sanity(0, NULL);
475 test_arraymap_sanity(0, NULL);
476 test_percpu_arraymap_sanity(0, NULL);
477 test_percpu_arraymap_many_keys();
478
479 test_map_large();
480 test_map_parallel();
481 test_map_stress();
482}
483
484int main(void)
485{
486 map_flags = 0;
487 run_all_tests();
488 map_flags = BPF_F_NO_PREALLOC;
489 run_all_tests();
490 printf("test_maps: OK\n");
491 return 0;
492}
493