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