blob: ee384f02cb6e3a0af072f55f1e0148179c14034a [file] [log] [blame]
Daniel Borkmann5aa5bd12016-10-17 14:28:36 +02001/*
2 * Testsuite for eBPF maps
3 *
4 * Copyright (c) 2014 PLUMgrid, http://plumgrid.com
5 * Copyright (c) 2016 Facebook
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of version 2 of the GNU General Public
9 * License as published by the Free Software Foundation.
10 */
11
12#include <stdio.h>
13#include <unistd.h>
14#include <errno.h>
15#include <string.h>
16#include <assert.h>
17#include <stdlib.h>
18
19#include <sys/wait.h>
20#include <sys/resource.h>
21
22#include <linux/bpf.h>
23
24#include "bpf_sys.h"
25
26static int map_flags;
27
28static void test_hashmap(int task, void *data)
29{
30 long long key, next_key, value;
31 int fd;
32
33 fd = bpf_map_create(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
34 2, map_flags);
35 if (fd < 0) {
36 printf("Failed to create hashmap '%s'!\n", strerror(errno));
37 exit(1);
38 }
39
40 key = 1;
41 value = 1234;
42 /* Insert key=1 element. */
43 assert(bpf_map_update(fd, &key, &value, BPF_ANY) == 0);
44
45 value = 0;
46 /* BPF_NOEXIST means add new element if it doesn't exist. */
47 assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == -1 &&
48 /* key=1 already exists. */
49 errno == EEXIST);
50
51 /* -1 is an invalid flag. */
52 assert(bpf_map_update(fd, &key, &value, -1) == -1 && errno == EINVAL);
53
54 /* Check that key=1 can be found. */
55 assert(bpf_map_lookup(fd, &key, &value) == 0 && value == 1234);
56
57 key = 2;
58 /* Check that key=2 is not found. */
59 assert(bpf_map_lookup(fd, &key, &value) == -1 && errno == ENOENT);
60
61 /* BPF_EXIST means update existing element. */
62 assert(bpf_map_update(fd, &key, &value, BPF_EXIST) == -1 &&
63 /* key=2 is not there. */
64 errno == ENOENT);
65
66 /* Insert key=2 element. */
67 assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == 0);
68
69 /* key=1 and key=2 were inserted, check that key=0 cannot be
70 * inserted due to max_entries limit.
71 */
72 key = 0;
73 assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == -1 &&
74 errno == E2BIG);
75
76 /* Update existing element, though the map is full. */
77 key = 1;
78 assert(bpf_map_update(fd, &key, &value, BPF_EXIST) == 0);
79 key = 2;
80 assert(bpf_map_update(fd, &key, &value, BPF_ANY) == 0);
81 key = 1;
82 assert(bpf_map_update(fd, &key, &value, BPF_ANY) == 0);
83
84 /* Check that key = 0 doesn't exist. */
85 key = 0;
86 assert(bpf_map_delete(fd, &key) == -1 && errno == ENOENT);
87
88 /* Iterate over two elements. */
89 assert(bpf_map_next_key(fd, &key, &next_key) == 0 &&
90 (next_key == 1 || next_key == 2));
91 assert(bpf_map_next_key(fd, &next_key, &next_key) == 0 &&
92 (next_key == 1 || next_key == 2));
93 assert(bpf_map_next_key(fd, &next_key, &next_key) == -1 &&
94 errno == ENOENT);
95
96 /* Delete both elements. */
97 key = 1;
98 assert(bpf_map_delete(fd, &key) == 0);
99 key = 2;
100 assert(bpf_map_delete(fd, &key) == 0);
101 assert(bpf_map_delete(fd, &key) == -1 && errno == ENOENT);
102
103 key = 0;
104 /* Check that map is empty. */
105 assert(bpf_map_next_key(fd, &key, &next_key) == -1 &&
106 errno == ENOENT);
107
108 close(fd);
109}
110
111static void test_hashmap_percpu(int task, void *data)
112{
113 unsigned int nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
114 long long value[nr_cpus];
115 long long key, next_key;
116 int expected_key_mask = 0;
117 int fd, i;
118
119 fd = bpf_map_create(BPF_MAP_TYPE_PERCPU_HASH, sizeof(key),
120 sizeof(value[0]), 2, map_flags);
121 if (fd < 0) {
122 printf("Failed to create hashmap '%s'!\n", strerror(errno));
123 exit(1);
124 }
125
126 for (i = 0; i < nr_cpus; i++)
127 value[i] = i + 100;
128
129 key = 1;
130 /* Insert key=1 element. */
131 assert(!(expected_key_mask & key));
132 assert(bpf_map_update(fd, &key, value, BPF_ANY) == 0);
133 expected_key_mask |= key;
134
135 /* BPF_NOEXIST means add new element if it doesn't exist. */
136 assert(bpf_map_update(fd, &key, value, BPF_NOEXIST) == -1 &&
137 /* key=1 already exists. */
138 errno == EEXIST);
139
140 /* -1 is an invalid flag. */
141 assert(bpf_map_update(fd, &key, value, -1) == -1 && errno == EINVAL);
142
143 /* Check that key=1 can be found. Value could be 0 if the lookup
144 * was run from a different CPU.
145 */
146 value[0] = 1;
147 assert(bpf_map_lookup(fd, &key, value) == 0 && value[0] == 100);
148
149 key = 2;
150 /* Check that key=2 is not found. */
151 assert(bpf_map_lookup(fd, &key, value) == -1 && errno == ENOENT);
152
153 /* BPF_EXIST means update existing element. */
154 assert(bpf_map_update(fd, &key, value, BPF_EXIST) == -1 &&
155 /* key=2 is not there. */
156 errno == ENOENT);
157
158 /* Insert key=2 element. */
159 assert(!(expected_key_mask & key));
160 assert(bpf_map_update(fd, &key, value, BPF_NOEXIST) == 0);
161 expected_key_mask |= key;
162
163 /* key=1 and key=2 were inserted, check that key=0 cannot be
164 * inserted due to max_entries limit.
165 */
166 key = 0;
167 assert(bpf_map_update(fd, &key, value, BPF_NOEXIST) == -1 &&
168 errno == E2BIG);
169
170 /* Check that key = 0 doesn't exist. */
171 assert(bpf_map_delete(fd, &key) == -1 && errno == ENOENT);
172
173 /* Iterate over two elements. */
174 while (!bpf_map_next_key(fd, &key, &next_key)) {
175 assert((expected_key_mask & next_key) == next_key);
176 expected_key_mask &= ~next_key;
177
178 assert(bpf_map_lookup(fd, &next_key, value) == 0);
179
180 for (i = 0; i < nr_cpus; i++)
181 assert(value[i] == i + 100);
182
183 key = next_key;
184 }
185 assert(errno == ENOENT);
186
187 /* Update with BPF_EXIST. */
188 key = 1;
189 assert(bpf_map_update(fd, &key, value, BPF_EXIST) == 0);
190
191 /* Delete both elements. */
192 key = 1;
193 assert(bpf_map_delete(fd, &key) == 0);
194 key = 2;
195 assert(bpf_map_delete(fd, &key) == 0);
196 assert(bpf_map_delete(fd, &key) == -1 && errno == ENOENT);
197
198 key = 0;
199 /* Check that map is empty. */
200 assert(bpf_map_next_key(fd, &key, &next_key) == -1 &&
201 errno == ENOENT);
202
203 close(fd);
204}
205
206static void test_arraymap(int task, void *data)
207{
208 int key, next_key, fd;
209 long long value;
210
211 fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, sizeof(key), sizeof(value),
212 2, 0);
213 if (fd < 0) {
214 printf("Failed to create arraymap '%s'!\n", strerror(errno));
215 exit(1);
216 }
217
218 key = 1;
219 value = 1234;
220 /* Insert key=1 element. */
221 assert(bpf_map_update(fd, &key, &value, BPF_ANY) == 0);
222
223 value = 0;
224 assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == -1 &&
225 errno == EEXIST);
226
227 /* Check that key=1 can be found. */
228 assert(bpf_map_lookup(fd, &key, &value) == 0 && value == 1234);
229
230 key = 0;
231 /* Check that key=0 is also found and zero initialized. */
232 assert(bpf_map_lookup(fd, &key, &value) == 0 && value == 0);
233
234 /* key=0 and key=1 were inserted, check that key=2 cannot be inserted
235 * due to max_entries limit.
236 */
237 key = 2;
238 assert(bpf_map_update(fd, &key, &value, BPF_EXIST) == -1 &&
239 errno == E2BIG);
240
241 /* Check that key = 2 doesn't exist. */
242 assert(bpf_map_lookup(fd, &key, &value) == -1 && errno == ENOENT);
243
244 /* Iterate over two elements. */
245 assert(bpf_map_next_key(fd, &key, &next_key) == 0 &&
246 next_key == 0);
247 assert(bpf_map_next_key(fd, &next_key, &next_key) == 0 &&
248 next_key == 1);
249 assert(bpf_map_next_key(fd, &next_key, &next_key) == -1 &&
250 errno == ENOENT);
251
252 /* Delete shouldn't succeed. */
253 key = 1;
254 assert(bpf_map_delete(fd, &key) == -1 && errno == EINVAL);
255
256 close(fd);
257}
258
259static void test_arraymap_percpu(int task, void *data)
260{
261 unsigned int nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
262 int key, next_key, fd, i;
263 long values[nr_cpus];
264
265 fd = bpf_map_create(BPF_MAP_TYPE_PERCPU_ARRAY, sizeof(key),
266 sizeof(values[0]), 2, 0);
267 if (fd < 0) {
268 printf("Failed to create arraymap '%s'!\n", strerror(errno));
269 exit(1);
270 }
271
272 for (i = 0; i < nr_cpus; i++)
273 values[i] = i + 100;
274
275 key = 1;
276 /* Insert key=1 element. */
277 assert(bpf_map_update(fd, &key, values, BPF_ANY) == 0);
278
279 values[0] = 0;
280 assert(bpf_map_update(fd, &key, values, BPF_NOEXIST) == -1 &&
281 errno == EEXIST);
282
283 /* Check that key=1 can be found. */
284 assert(bpf_map_lookup(fd, &key, values) == 0 && values[0] == 100);
285
286 key = 0;
287 /* Check that key=0 is also found and zero initialized. */
288 assert(bpf_map_lookup(fd, &key, values) == 0 &&
289 values[0] == 0 && values[nr_cpus - 1] == 0);
290
291 /* Check that key=2 cannot be inserted due to max_entries limit. */
292 key = 2;
293 assert(bpf_map_update(fd, &key, values, BPF_EXIST) == -1 &&
294 errno == E2BIG);
295
296 /* Check that key = 2 doesn't exist. */
297 assert(bpf_map_lookup(fd, &key, values) == -1 && errno == ENOENT);
298
299 /* Iterate over two elements. */
300 assert(bpf_map_next_key(fd, &key, &next_key) == 0 &&
301 next_key == 0);
302 assert(bpf_map_next_key(fd, &next_key, &next_key) == 0 &&
303 next_key == 1);
304 assert(bpf_map_next_key(fd, &next_key, &next_key) == -1 &&
305 errno == ENOENT);
306
307 /* Delete shouldn't succeed. */
308 key = 1;
309 assert(bpf_map_delete(fd, &key) == -1 && errno == EINVAL);
310
311 close(fd);
312}
313
314static void test_arraymap_percpu_many_keys(void)
315{
316 unsigned int nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
317 unsigned int nr_keys = 20000;
318 long values[nr_cpus];
319 int key, fd, i;
320
321 fd = bpf_map_create(BPF_MAP_TYPE_PERCPU_ARRAY, sizeof(key),
322 sizeof(values[0]), nr_keys, 0);
323 if (fd < 0) {
324 printf("Failed to create per-cpu arraymap '%s'!\n",
325 strerror(errno));
326 exit(1);
327 }
328
329 for (i = 0; i < nr_cpus; i++)
330 values[i] = i + 10;
331
332 for (key = 0; key < nr_keys; key++)
333 assert(bpf_map_update(fd, &key, values, BPF_ANY) == 0);
334
335 for (key = 0; key < nr_keys; key++) {
336 for (i = 0; i < nr_cpus; i++)
337 values[i] = 0;
338
339 assert(bpf_map_lookup(fd, &key, values) == 0);
340
341 for (i = 0; i < nr_cpus; i++)
342 assert(values[i] == i + 10);
343 }
344
345 close(fd);
346}
347
348#define MAP_SIZE (32 * 1024)
349
350static void test_map_large(void)
351{
352 struct bigkey {
353 int a;
354 char b[116];
355 long long c;
356 } key;
357 int fd, i, value;
358
359 fd = bpf_map_create(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
360 MAP_SIZE, map_flags);
361 if (fd < 0) {
362 printf("Failed to create large map '%s'!\n", strerror(errno));
363 exit(1);
364 }
365
366 for (i = 0; i < MAP_SIZE; i++) {
367 key = (struct bigkey) { .c = i };
368 value = i;
369
370 assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == 0);
371 }
372
373 key.c = -1;
374 assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == -1 &&
375 errno == E2BIG);
376
377 /* Iterate through all elements. */
378 for (i = 0; i < MAP_SIZE; i++)
379 assert(bpf_map_next_key(fd, &key, &key) == 0);
380 assert(bpf_map_next_key(fd, &key, &key) == -1 && errno == ENOENT);
381
382 key.c = 0;
383 assert(bpf_map_lookup(fd, &key, &value) == 0 && value == 0);
384 key.a = 1;
385 assert(bpf_map_lookup(fd, &key, &value) == -1 && errno == ENOENT);
386
387 close(fd);
388}
389
390static void run_parallel(int tasks, void (*fn)(int task, void *data),
391 void *data)
392{
393 pid_t pid[tasks];
394 int i;
395
396 for (i = 0; i < tasks; i++) {
397 pid[i] = fork();
398 if (pid[i] == 0) {
399 fn(i, data);
400 exit(0);
401 } else if (pid[i] == -1) {
402 printf("Couldn't spawn #%d process!\n", i);
403 exit(1);
404 }
405 }
406
407 for (i = 0; i < tasks; i++) {
408 int status;
409
410 assert(waitpid(pid[i], &status, 0) == pid[i]);
411 assert(status == 0);
412 }
413}
414
415static void test_map_stress(void)
416{
417 run_parallel(100, test_hashmap, NULL);
418 run_parallel(100, test_hashmap_percpu, NULL);
419
420 run_parallel(100, test_arraymap, NULL);
421 run_parallel(100, test_arraymap_percpu, NULL);
422}
423
424#define TASKS 1024
425
426#define DO_UPDATE 1
427#define DO_DELETE 0
428
429static void do_work(int fn, void *data)
430{
431 int do_update = ((int *)data)[1];
432 int fd = ((int *)data)[0];
433 int i, key, value;
434
435 for (i = fn; i < MAP_SIZE; i += TASKS) {
436 key = value = i;
437
438 if (do_update) {
439 assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == 0);
440 assert(bpf_map_update(fd, &key, &value, BPF_EXIST) == 0);
441 } else {
442 assert(bpf_map_delete(fd, &key) == 0);
443 }
444 }
445}
446
447static void test_map_parallel(void)
448{
449 int i, fd, key = 0, value = 0;
450 int data[2];
451
452 fd = bpf_map_create(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
453 MAP_SIZE, map_flags);
454 if (fd < 0) {
455 printf("Failed to create map for parallel test '%s'!\n",
456 strerror(errno));
457 exit(1);
458 }
459
460 /* Use the same fd in children to add elements to this map:
461 * child_0 adds key=0, key=1024, key=2048, ...
462 * child_1 adds key=1, key=1025, key=2049, ...
463 * child_1023 adds key=1023, ...
464 */
465 data[0] = fd;
466 data[1] = DO_UPDATE;
467 run_parallel(TASKS, do_work, data);
468
469 /* Check that key=0 is already there. */
470 assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == -1 &&
471 errno == EEXIST);
472
473 /* Check that all elements were inserted. */
474 key = -1;
475 for (i = 0; i < MAP_SIZE; i++)
476 assert(bpf_map_next_key(fd, &key, &key) == 0);
477 assert(bpf_map_next_key(fd, &key, &key) == -1 && errno == ENOENT);
478
479 /* Another check for all elements */
480 for (i = 0; i < MAP_SIZE; i++) {
481 key = MAP_SIZE - i - 1;
482
483 assert(bpf_map_lookup(fd, &key, &value) == 0 &&
484 value == key);
485 }
486
487 /* Now let's delete all elemenets in parallel. */
488 data[1] = DO_DELETE;
489 run_parallel(TASKS, do_work, data);
490
491 /* Nothing should be left. */
492 key = -1;
493 assert(bpf_map_next_key(fd, &key, &key) == -1 && errno == ENOENT);
494}
495
496static void run_all_tests(void)
497{
498 test_hashmap(0, NULL);
499 test_hashmap_percpu(0, NULL);
500
501 test_arraymap(0, NULL);
502 test_arraymap_percpu(0, NULL);
503
504 test_arraymap_percpu_many_keys();
505
506 test_map_large();
507 test_map_parallel();
508 test_map_stress();
509}
510
511int main(void)
512{
513 struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY };
514
515 setrlimit(RLIMIT_MEMLOCK, &rinf);
516
517 map_flags = 0;
518 run_all_tests();
519
520 map_flags = BPF_F_NO_PREALLOC;
521 run_all_tests();
522
523 printf("test_maps: OK\n");
524 return 0;
525}