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