]> git.karo-electronics.de Git - karo-tx-linux.git/blob - samples/bpf/test_maps.c
Merge remote-tracking branch 'drm/drm-next'
[karo-tx-linux.git] / samples / bpf / test_maps.c
1 /*
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 */
21 static 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 &&
72                (next_key == 1 || next_key == 2));
73         assert(bpf_get_next_key(map_fd, &next_key, &next_key) == 0 &&
74                (next_key == 1 || next_key == 2));
75         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
92 /* sanity tests for percpu map API */
93 static 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
186 static 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
239 static 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
271 static 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
327 #define MAP_SIZE (32 * 1024)
328 static 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 */
368 static 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
391 static void test_map_stress(void)
392 {
393         run_parallel(100, test_hashmap_sanity, NULL);
394         run_parallel(100, test_percpu_hashmap_sanity, NULL);
395         run_parallel(100, test_arraymap_sanity, NULL);
396         run_parallel(100, test_percpu_arraymap_sanity, NULL);
397 }
398
399 #define TASKS 1024
400 #define DO_UPDATE 1
401 #define DO_DELETE 0
402 static 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
418 static 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
466 int main(void)
467 {
468         test_hashmap_sanity(0, NULL);
469         test_percpu_hashmap_sanity(0, NULL);
470         test_arraymap_sanity(0, NULL);
471         test_percpu_arraymap_sanity(0, NULL);
472         test_percpu_arraymap_many_keys();
473
474         test_map_large();
475         test_map_parallel();
476         test_map_stress();
477         printf("test_maps: OK\n");
478         return 0;
479 }