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