blob: 5f5fe53321489ec761b2f7dba7c294e000d8e581 [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
239#define MAP_SIZE (32 * 1024)
240static void test_map_large(void)
241{
242 struct bigkey {
243 int a;
244 char b[116];
245 long long c;
246 } key;
247 int map_fd, i, value;
248
249 /* allocate 4Mbyte of memory */
250 map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
251 MAP_SIZE);
252 if (map_fd < 0) {
253 printf("failed to create large map '%s'\n", strerror(errno));
254 exit(1);
255 }
256
257 for (i = 0; i < MAP_SIZE; i++) {
258 key = (struct bigkey) {.c = i};
259 value = i;
260 assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == 0);
261 }
262 key.c = -1;
263 assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 &&
264 errno == E2BIG);
265
266 /* iterate through all elements */
267 for (i = 0; i < MAP_SIZE; i++)
268 assert(bpf_get_next_key(map_fd, &key, &key) == 0);
269 assert(bpf_get_next_key(map_fd, &key, &key) == -1 && errno == ENOENT);
270
271 key.c = 0;
272 assert(bpf_lookup_elem(map_fd, &key, &value) == 0 && value == 0);
273 key.a = 1;
274 assert(bpf_lookup_elem(map_fd, &key, &value) == -1 && errno == ENOENT);
275
276 close(map_fd);
277}
278
279/* fork N children and wait for them to complete */
280static void run_parallel(int tasks, void (*fn)(int i, void *data), void *data)
281{
282 pid_t pid[tasks];
283 int i;
284
285 for (i = 0; i < tasks; i++) {
286 pid[i] = fork();
287 if (pid[i] == 0) {
288 fn(i, data);
289 exit(0);
290 } else if (pid[i] == -1) {
291 printf("couldn't spawn #%d process\n", i);
292 exit(1);
293 }
294 }
295 for (i = 0; i < tasks; i++) {
296 int status;
297
298 assert(waitpid(pid[i], &status, 0) == pid[i]);
299 assert(status == 0);
300 }
301}
302
303static void test_map_stress(void)
304{
305 run_parallel(100, test_hashmap_sanity, NULL);
Martin KaFai Laue1559672016-02-01 22:39:56 -0800306 run_parallel(100, test_percpu_hashmap_sanity, NULL);
Alexei Starovoitovffb65f22014-11-13 17:36:48 -0800307 run_parallel(100, test_arraymap_sanity, NULL);
308}
309
310#define TASKS 1024
311#define DO_UPDATE 1
312#define DO_DELETE 0
313static void do_work(int fn, void *data)
314{
315 int map_fd = ((int *)data)[0];
316 int do_update = ((int *)data)[1];
317 int i;
318 int key, value;
319
320 for (i = fn; i < MAP_SIZE; i += TASKS) {
321 key = value = i;
322 if (do_update)
323 assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == 0);
324 else
325 assert(bpf_delete_elem(map_fd, &key) == 0);
326 }
327}
328
329static void test_map_parallel(void)
330{
331 int i, map_fd, key = 0, value = 0;
332 int data[2];
333
334 map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
335 MAP_SIZE);
336 if (map_fd < 0) {
337 printf("failed to create map for parallel test '%s'\n",
338 strerror(errno));
339 exit(1);
340 }
341
342 data[0] = map_fd;
343 data[1] = DO_UPDATE;
344 /* use the same map_fd in children to add elements to this map
345 * child_0 adds key=0, key=1024, key=2048, ...
346 * child_1 adds key=1, key=1025, key=2049, ...
347 * child_1023 adds key=1023, ...
348 */
349 run_parallel(TASKS, do_work, data);
350
351 /* check that key=0 is already there */
352 assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 &&
353 errno == EEXIST);
354
355 /* check that all elements were inserted */
356 key = -1;
357 for (i = 0; i < MAP_SIZE; i++)
358 assert(bpf_get_next_key(map_fd, &key, &key) == 0);
359 assert(bpf_get_next_key(map_fd, &key, &key) == -1 && errno == ENOENT);
360
361 /* another check for all elements */
362 for (i = 0; i < MAP_SIZE; i++) {
363 key = MAP_SIZE - i - 1;
364 assert(bpf_lookup_elem(map_fd, &key, &value) == 0 &&
365 value == key);
366 }
367
368 /* now let's delete all elemenets in parallel */
369 data[1] = DO_DELETE;
370 run_parallel(TASKS, do_work, data);
371
372 /* nothing should be left */
373 key = -1;
374 assert(bpf_get_next_key(map_fd, &key, &key) == -1 && errno == ENOENT);
375}
376
377int main(void)
378{
379 test_hashmap_sanity(0, NULL);
Martin KaFai Laue1559672016-02-01 22:39:56 -0800380 test_percpu_hashmap_sanity(0, NULL);
Alexei Starovoitovffb65f22014-11-13 17:36:48 -0800381 test_arraymap_sanity(0, NULL);
382 test_map_large();
383 test_map_parallel();
384 test_map_stress();
385 printf("test_maps: OK\n");
386 return 0;
387}