]>
Commit | Line | Data |
---|---|---|
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> |
6f6d33f3 | 25 | #include <bpf/libbpf.h> |
e00c7b21 | 26 | #include "bpf_util.h" |
5aa5bd14 DB |
27 | |
28 | static int map_flags; | |
29 | ||
30 | static void test_hashmap(int task, void *data) | |
31 | { | |
8fe45924 | 32 | long long key, next_key, first_key, value; |
5aa5bd14 DB |
33 | int fd; |
34 | ||
f4874d01 | 35 | fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), |
5aa5bd14 DB |
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); |
8c290e60 AS |
84 | key = 3; |
85 | assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == -1 && | |
86 | errno == E2BIG); | |
5aa5bd14 DB |
87 | |
88 | /* Check that key = 0 doesn't exist. */ | |
89 | key = 0; | |
e58383b8 | 90 | assert(bpf_map_delete_elem(fd, &key) == -1 && errno == ENOENT); |
5aa5bd14 DB |
91 | |
92 | /* Iterate over two elements. */ | |
8fe45924 TQ |
93 | assert(bpf_map_get_next_key(fd, NULL, &first_key) == 0 && |
94 | (first_key == 1 || first_key == 2)); | |
5f155c25 | 95 | assert(bpf_map_get_next_key(fd, &key, &next_key) == 0 && |
8fe45924 | 96 | (next_key == first_key)); |
5f155c25 | 97 | assert(bpf_map_get_next_key(fd, &next_key, &next_key) == 0 && |
8fe45924 TQ |
98 | (next_key == 1 || next_key == 2) && |
99 | (next_key != first_key)); | |
5f155c25 | 100 | assert(bpf_map_get_next_key(fd, &next_key, &next_key) == -1 && |
5aa5bd14 DB |
101 | errno == ENOENT); |
102 | ||
103 | /* Delete both elements. */ | |
104 | key = 1; | |
e58383b8 | 105 | assert(bpf_map_delete_elem(fd, &key) == 0); |
5aa5bd14 | 106 | key = 2; |
e58383b8 MS |
107 | assert(bpf_map_delete_elem(fd, &key) == 0); |
108 | assert(bpf_map_delete_elem(fd, &key) == -1 && errno == ENOENT); | |
5aa5bd14 DB |
109 | |
110 | key = 0; | |
111 | /* Check that map is empty. */ | |
8fe45924 TQ |
112 | assert(bpf_map_get_next_key(fd, NULL, &next_key) == -1 && |
113 | errno == ENOENT); | |
5f155c25 | 114 | assert(bpf_map_get_next_key(fd, &key, &next_key) == -1 && |
5aa5bd14 DB |
115 | errno == ENOENT); |
116 | ||
117 | close(fd); | |
118 | } | |
119 | ||
8c290e60 AS |
120 | static void test_hashmap_sizes(int task, void *data) |
121 | { | |
122 | int fd, i, j; | |
123 | ||
124 | for (i = 1; i <= 512; i <<= 1) | |
125 | for (j = 1; j <= 1 << 18; j <<= 1) { | |
126 | fd = bpf_create_map(BPF_MAP_TYPE_HASH, i, j, | |
127 | 2, map_flags); | |
128 | if (fd < 0) { | |
129 | printf("Failed to create hashmap key=%d value=%d '%s'\n", | |
130 | i, j, strerror(errno)); | |
131 | exit(1); | |
132 | } | |
133 | close(fd); | |
134 | usleep(10); /* give kernel time to destroy */ | |
135 | } | |
136 | } | |
137 | ||
5aa5bd14 DB |
138 | static void test_hashmap_percpu(int task, void *data) |
139 | { | |
e00c7b21 | 140 | unsigned int nr_cpus = bpf_num_possible_cpus(); |
f3515b5d | 141 | BPF_DECLARE_PERCPU(long, value); |
8fe45924 | 142 | long long key, next_key, first_key; |
5aa5bd14 DB |
143 | int expected_key_mask = 0; |
144 | int fd, i; | |
145 | ||
f4874d01 | 146 | fd = bpf_create_map(BPF_MAP_TYPE_PERCPU_HASH, sizeof(key), |
f3515b5d | 147 | sizeof(bpf_percpu(value, 0)), 2, map_flags); |
5aa5bd14 DB |
148 | if (fd < 0) { |
149 | printf("Failed to create hashmap '%s'!\n", strerror(errno)); | |
150 | exit(1); | |
151 | } | |
152 | ||
153 | for (i = 0; i < nr_cpus; i++) | |
f3515b5d | 154 | bpf_percpu(value, i) = i + 100; |
5aa5bd14 DB |
155 | |
156 | key = 1; | |
157 | /* Insert key=1 element. */ | |
158 | assert(!(expected_key_mask & key)); | |
10ecc728 | 159 | assert(bpf_map_update_elem(fd, &key, value, BPF_ANY) == 0); |
5aa5bd14 DB |
160 | expected_key_mask |= key; |
161 | ||
162 | /* BPF_NOEXIST means add new element if it doesn't exist. */ | |
10ecc728 | 163 | assert(bpf_map_update_elem(fd, &key, value, BPF_NOEXIST) == -1 && |
5aa5bd14 DB |
164 | /* key=1 already exists. */ |
165 | errno == EEXIST); | |
166 | ||
167 | /* -1 is an invalid flag. */ | |
10ecc728 MS |
168 | assert(bpf_map_update_elem(fd, &key, value, -1) == -1 && |
169 | errno == EINVAL); | |
5aa5bd14 DB |
170 | |
171 | /* Check that key=1 can be found. Value could be 0 if the lookup | |
172 | * was run from a different CPU. | |
173 | */ | |
f3515b5d DB |
174 | bpf_percpu(value, 0) = 1; |
175 | assert(bpf_map_lookup_elem(fd, &key, value) == 0 && | |
176 | bpf_percpu(value, 0) == 100); | |
5aa5bd14 DB |
177 | |
178 | key = 2; | |
179 | /* Check that key=2 is not found. */ | |
e5ff7c40 | 180 | assert(bpf_map_lookup_elem(fd, &key, value) == -1 && errno == ENOENT); |
5aa5bd14 DB |
181 | |
182 | /* BPF_EXIST means update existing element. */ | |
10ecc728 | 183 | assert(bpf_map_update_elem(fd, &key, value, BPF_EXIST) == -1 && |
5aa5bd14 DB |
184 | /* key=2 is not there. */ |
185 | errno == ENOENT); | |
186 | ||
187 | /* Insert key=2 element. */ | |
188 | assert(!(expected_key_mask & key)); | |
10ecc728 | 189 | assert(bpf_map_update_elem(fd, &key, value, BPF_NOEXIST) == 0); |
5aa5bd14 DB |
190 | expected_key_mask |= key; |
191 | ||
192 | /* key=1 and key=2 were inserted, check that key=0 cannot be | |
193 | * inserted due to max_entries limit. | |
194 | */ | |
195 | key = 0; | |
10ecc728 | 196 | assert(bpf_map_update_elem(fd, &key, value, BPF_NOEXIST) == -1 && |
5aa5bd14 DB |
197 | errno == E2BIG); |
198 | ||
199 | /* Check that key = 0 doesn't exist. */ | |
e58383b8 | 200 | assert(bpf_map_delete_elem(fd, &key) == -1 && errno == ENOENT); |
5aa5bd14 DB |
201 | |
202 | /* Iterate over two elements. */ | |
8fe45924 TQ |
203 | assert(bpf_map_get_next_key(fd, NULL, &first_key) == 0 && |
204 | ((expected_key_mask & first_key) == first_key)); | |
5f155c25 | 205 | while (!bpf_map_get_next_key(fd, &key, &next_key)) { |
8fe45924 TQ |
206 | if (first_key) { |
207 | assert(next_key == first_key); | |
208 | first_key = 0; | |
209 | } | |
5aa5bd14 DB |
210 | assert((expected_key_mask & next_key) == next_key); |
211 | expected_key_mask &= ~next_key; | |
212 | ||
e5ff7c40 | 213 | assert(bpf_map_lookup_elem(fd, &next_key, value) == 0); |
5aa5bd14 DB |
214 | |
215 | for (i = 0; i < nr_cpus; i++) | |
f3515b5d | 216 | assert(bpf_percpu(value, i) == i + 100); |
5aa5bd14 DB |
217 | |
218 | key = next_key; | |
219 | } | |
220 | assert(errno == ENOENT); | |
221 | ||
222 | /* Update with BPF_EXIST. */ | |
223 | key = 1; | |
10ecc728 | 224 | assert(bpf_map_update_elem(fd, &key, value, BPF_EXIST) == 0); |
5aa5bd14 DB |
225 | |
226 | /* Delete both elements. */ | |
227 | key = 1; | |
e58383b8 | 228 | assert(bpf_map_delete_elem(fd, &key) == 0); |
5aa5bd14 | 229 | key = 2; |
e58383b8 MS |
230 | assert(bpf_map_delete_elem(fd, &key) == 0); |
231 | assert(bpf_map_delete_elem(fd, &key) == -1 && errno == ENOENT); | |
5aa5bd14 DB |
232 | |
233 | key = 0; | |
234 | /* Check that map is empty. */ | |
8fe45924 TQ |
235 | assert(bpf_map_get_next_key(fd, NULL, &next_key) == -1 && |
236 | errno == ENOENT); | |
5f155c25 | 237 | assert(bpf_map_get_next_key(fd, &key, &next_key) == -1 && |
5aa5bd14 DB |
238 | errno == ENOENT); |
239 | ||
240 | close(fd); | |
241 | } | |
242 | ||
5ecf51fd DB |
243 | static void test_hashmap_walk(int task, void *data) |
244 | { | |
245 | int fd, i, max_entries = 100000; | |
246 | long long key, value, next_key; | |
247 | bool next_key_valid = true; | |
248 | ||
249 | fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), | |
250 | max_entries, map_flags); | |
251 | if (fd < 0) { | |
252 | printf("Failed to create hashmap '%s'!\n", strerror(errno)); | |
253 | exit(1); | |
254 | } | |
255 | ||
256 | for (i = 0; i < max_entries; i++) { | |
257 | key = i; value = key; | |
258 | assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == 0); | |
259 | } | |
260 | ||
261 | for (i = 0; bpf_map_get_next_key(fd, !i ? NULL : &key, | |
262 | &next_key) == 0; i++) { | |
263 | key = next_key; | |
264 | assert(bpf_map_lookup_elem(fd, &key, &value) == 0); | |
265 | } | |
266 | ||
267 | assert(i == max_entries); | |
268 | ||
269 | assert(bpf_map_get_next_key(fd, NULL, &key) == 0); | |
270 | for (i = 0; next_key_valid; i++) { | |
271 | next_key_valid = bpf_map_get_next_key(fd, &key, &next_key) == 0; | |
272 | assert(bpf_map_lookup_elem(fd, &key, &value) == 0); | |
273 | value++; | |
274 | assert(bpf_map_update_elem(fd, &key, &value, BPF_EXIST) == 0); | |
275 | key = next_key; | |
276 | } | |
277 | ||
278 | assert(i == max_entries); | |
279 | ||
280 | for (i = 0; bpf_map_get_next_key(fd, !i ? NULL : &key, | |
281 | &next_key) == 0; i++) { | |
282 | key = next_key; | |
283 | assert(bpf_map_lookup_elem(fd, &key, &value) == 0); | |
284 | assert(value - 1 == key); | |
285 | } | |
286 | ||
287 | assert(i == max_entries); | |
288 | close(fd); | |
289 | } | |
290 | ||
5aa5bd14 DB |
291 | static void test_arraymap(int task, void *data) |
292 | { | |
293 | int key, next_key, fd; | |
294 | long long value; | |
295 | ||
f4874d01 | 296 | fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(key), sizeof(value), |
5aa5bd14 DB |
297 | 2, 0); |
298 | if (fd < 0) { | |
299 | printf("Failed to create arraymap '%s'!\n", strerror(errno)); | |
300 | exit(1); | |
301 | } | |
302 | ||
303 | key = 1; | |
304 | value = 1234; | |
305 | /* Insert key=1 element. */ | |
10ecc728 | 306 | assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) == 0); |
5aa5bd14 DB |
307 | |
308 | value = 0; | |
10ecc728 | 309 | assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == -1 && |
5aa5bd14 DB |
310 | errno == EEXIST); |
311 | ||
312 | /* Check that key=1 can be found. */ | |
e5ff7c40 | 313 | assert(bpf_map_lookup_elem(fd, &key, &value) == 0 && value == 1234); |
5aa5bd14 DB |
314 | |
315 | key = 0; | |
316 | /* Check that key=0 is also found and zero initialized. */ | |
e5ff7c40 | 317 | assert(bpf_map_lookup_elem(fd, &key, &value) == 0 && value == 0); |
5aa5bd14 DB |
318 | |
319 | /* key=0 and key=1 were inserted, check that key=2 cannot be inserted | |
320 | * due to max_entries limit. | |
321 | */ | |
322 | key = 2; | |
10ecc728 | 323 | assert(bpf_map_update_elem(fd, &key, &value, BPF_EXIST) == -1 && |
5aa5bd14 DB |
324 | errno == E2BIG); |
325 | ||
326 | /* Check that key = 2 doesn't exist. */ | |
e5ff7c40 | 327 | assert(bpf_map_lookup_elem(fd, &key, &value) == -1 && errno == ENOENT); |
5aa5bd14 DB |
328 | |
329 | /* Iterate over two elements. */ | |
8fe45924 TQ |
330 | assert(bpf_map_get_next_key(fd, NULL, &next_key) == 0 && |
331 | next_key == 0); | |
5f155c25 | 332 | assert(bpf_map_get_next_key(fd, &key, &next_key) == 0 && |
5aa5bd14 | 333 | next_key == 0); |
5f155c25 | 334 | assert(bpf_map_get_next_key(fd, &next_key, &next_key) == 0 && |
5aa5bd14 | 335 | next_key == 1); |
5f155c25 | 336 | assert(bpf_map_get_next_key(fd, &next_key, &next_key) == -1 && |
5aa5bd14 DB |
337 | errno == ENOENT); |
338 | ||
339 | /* Delete shouldn't succeed. */ | |
340 | key = 1; | |
e58383b8 | 341 | assert(bpf_map_delete_elem(fd, &key) == -1 && errno == EINVAL); |
5aa5bd14 DB |
342 | |
343 | close(fd); | |
344 | } | |
345 | ||
346 | static void test_arraymap_percpu(int task, void *data) | |
347 | { | |
e00c7b21 | 348 | unsigned int nr_cpus = bpf_num_possible_cpus(); |
f3515b5d | 349 | BPF_DECLARE_PERCPU(long, values); |
5aa5bd14 | 350 | int key, next_key, fd, i; |
5aa5bd14 | 351 | |
f4874d01 | 352 | fd = bpf_create_map(BPF_MAP_TYPE_PERCPU_ARRAY, sizeof(key), |
f3515b5d | 353 | sizeof(bpf_percpu(values, 0)), 2, 0); |
5aa5bd14 DB |
354 | if (fd < 0) { |
355 | printf("Failed to create arraymap '%s'!\n", strerror(errno)); | |
356 | exit(1); | |
357 | } | |
358 | ||
359 | for (i = 0; i < nr_cpus; i++) | |
f3515b5d | 360 | bpf_percpu(values, i) = i + 100; |
5aa5bd14 DB |
361 | |
362 | key = 1; | |
363 | /* Insert key=1 element. */ | |
10ecc728 | 364 | assert(bpf_map_update_elem(fd, &key, values, BPF_ANY) == 0); |
5aa5bd14 | 365 | |
f3515b5d | 366 | bpf_percpu(values, 0) = 0; |
10ecc728 | 367 | assert(bpf_map_update_elem(fd, &key, values, BPF_NOEXIST) == -1 && |
5aa5bd14 DB |
368 | errno == EEXIST); |
369 | ||
370 | /* Check that key=1 can be found. */ | |
f3515b5d DB |
371 | assert(bpf_map_lookup_elem(fd, &key, values) == 0 && |
372 | bpf_percpu(values, 0) == 100); | |
5aa5bd14 DB |
373 | |
374 | key = 0; | |
375 | /* Check that key=0 is also found and zero initialized. */ | |
e5ff7c40 | 376 | assert(bpf_map_lookup_elem(fd, &key, values) == 0 && |
f3515b5d DB |
377 | bpf_percpu(values, 0) == 0 && |
378 | bpf_percpu(values, nr_cpus - 1) == 0); | |
5aa5bd14 DB |
379 | |
380 | /* Check that key=2 cannot be inserted due to max_entries limit. */ | |
381 | key = 2; | |
10ecc728 | 382 | assert(bpf_map_update_elem(fd, &key, values, BPF_EXIST) == -1 && |
5aa5bd14 DB |
383 | errno == E2BIG); |
384 | ||
385 | /* Check that key = 2 doesn't exist. */ | |
e5ff7c40 | 386 | assert(bpf_map_lookup_elem(fd, &key, values) == -1 && errno == ENOENT); |
5aa5bd14 DB |
387 | |
388 | /* Iterate over two elements. */ | |
8fe45924 TQ |
389 | assert(bpf_map_get_next_key(fd, NULL, &next_key) == 0 && |
390 | next_key == 0); | |
5f155c25 | 391 | assert(bpf_map_get_next_key(fd, &key, &next_key) == 0 && |
5aa5bd14 | 392 | next_key == 0); |
5f155c25 | 393 | assert(bpf_map_get_next_key(fd, &next_key, &next_key) == 0 && |
5aa5bd14 | 394 | next_key == 1); |
5f155c25 | 395 | assert(bpf_map_get_next_key(fd, &next_key, &next_key) == -1 && |
5aa5bd14 DB |
396 | errno == ENOENT); |
397 | ||
398 | /* Delete shouldn't succeed. */ | |
399 | key = 1; | |
e58383b8 | 400 | assert(bpf_map_delete_elem(fd, &key) == -1 && errno == EINVAL); |
5aa5bd14 DB |
401 | |
402 | close(fd); | |
403 | } | |
404 | ||
405 | static void test_arraymap_percpu_many_keys(void) | |
406 | { | |
e00c7b21 | 407 | unsigned int nr_cpus = bpf_num_possible_cpus(); |
f3515b5d | 408 | BPF_DECLARE_PERCPU(long, values); |
8c290e60 AS |
409 | /* nr_keys is not too large otherwise the test stresses percpu |
410 | * allocator more than anything else | |
411 | */ | |
412 | unsigned int nr_keys = 2000; | |
5aa5bd14 DB |
413 | int key, fd, i; |
414 | ||
f4874d01 | 415 | fd = bpf_create_map(BPF_MAP_TYPE_PERCPU_ARRAY, sizeof(key), |
f3515b5d | 416 | sizeof(bpf_percpu(values, 0)), nr_keys, 0); |
5aa5bd14 DB |
417 | if (fd < 0) { |
418 | printf("Failed to create per-cpu arraymap '%s'!\n", | |
419 | strerror(errno)); | |
420 | exit(1); | |
421 | } | |
422 | ||
423 | for (i = 0; i < nr_cpus; i++) | |
f3515b5d | 424 | bpf_percpu(values, i) = i + 10; |
5aa5bd14 DB |
425 | |
426 | for (key = 0; key < nr_keys; key++) | |
10ecc728 | 427 | assert(bpf_map_update_elem(fd, &key, values, BPF_ANY) == 0); |
5aa5bd14 DB |
428 | |
429 | for (key = 0; key < nr_keys; key++) { | |
430 | for (i = 0; i < nr_cpus; i++) | |
f3515b5d | 431 | bpf_percpu(values, i) = 0; |
5aa5bd14 | 432 | |
e5ff7c40 | 433 | assert(bpf_map_lookup_elem(fd, &key, values) == 0); |
5aa5bd14 DB |
434 | |
435 | for (i = 0; i < nr_cpus; i++) | |
f3515b5d | 436 | assert(bpf_percpu(values, i) == i + 10); |
5aa5bd14 DB |
437 | } |
438 | ||
439 | close(fd); | |
440 | } | |
441 | ||
546ac1ff JF |
442 | static void test_devmap(int task, void *data) |
443 | { | |
81f6bf81 | 444 | int fd; |
546ac1ff JF |
445 | __u32 key, value; |
446 | ||
447 | fd = bpf_create_map(BPF_MAP_TYPE_DEVMAP, sizeof(key), sizeof(value), | |
448 | 2, 0); | |
449 | if (fd < 0) { | |
450 | printf("Failed to create arraymap '%s'!\n", strerror(errno)); | |
451 | exit(1); | |
452 | } | |
453 | ||
454 | close(fd); | |
455 | } | |
456 | ||
6f6d33f3 JF |
457 | #include <sys/socket.h> |
458 | #include <sys/ioctl.h> | |
459 | #include <arpa/inet.h> | |
460 | #include <sys/select.h> | |
461 | #include <linux/err.h> | |
462 | #define SOCKMAP_PARSE_PROG "./sockmap_parse_prog.o" | |
463 | #define SOCKMAP_VERDICT_PROG "./sockmap_verdict_prog.o" | |
3f0d6a16 | 464 | static void test_sockmap(int tasks, void *data) |
6f6d33f3 | 465 | { |
81374aaa JF |
466 | int one = 1, map_fd_rx, map_fd_tx, map_fd_break, s, sc, rc; |
467 | struct bpf_map *bpf_map_rx, *bpf_map_tx, *bpf_map_break; | |
6f6d33f3 | 468 | int ports[] = {50200, 50201, 50202, 50204}; |
435bf0d3 | 469 | int err, i, fd, udp, sfd[6] = {0xdeadbeef}; |
6fd28865 | 470 | u8 buf[20] = {0x0, 0x5, 0x3, 0x2, 0x1, 0x0}; |
6f6d33f3 | 471 | int parse_prog, verdict_prog; |
6f6d33f3 JF |
472 | struct sockaddr_in addr; |
473 | struct bpf_object *obj; | |
474 | struct timeval to; | |
475 | __u32 key, value; | |
3f0d6a16 | 476 | pid_t pid[tasks]; |
6f6d33f3 JF |
477 | fd_set w; |
478 | ||
479 | /* Create some sockets to use with sockmap */ | |
480 | for (i = 0; i < 2; i++) { | |
481 | sfd[i] = socket(AF_INET, SOCK_STREAM, 0); | |
482 | if (sfd[i] < 0) | |
483 | goto out; | |
484 | err = setsockopt(sfd[i], SOL_SOCKET, SO_REUSEADDR, | |
485 | (char *)&one, sizeof(one)); | |
486 | if (err) { | |
487 | printf("failed to setsockopt\n"); | |
488 | goto out; | |
489 | } | |
490 | err = ioctl(sfd[i], FIONBIO, (char *)&one); | |
491 | if (err < 0) { | |
492 | printf("failed to ioctl\n"); | |
493 | goto out; | |
494 | } | |
495 | memset(&addr, 0, sizeof(struct sockaddr_in)); | |
496 | addr.sin_family = AF_INET; | |
497 | addr.sin_addr.s_addr = inet_addr("127.0.0.1"); | |
498 | addr.sin_port = htons(ports[i]); | |
499 | err = bind(sfd[i], (struct sockaddr *)&addr, sizeof(addr)); | |
500 | if (err < 0) { | |
501 | printf("failed to bind: err %i: %i:%i\n", | |
502 | err, i, sfd[i]); | |
503 | goto out; | |
504 | } | |
505 | err = listen(sfd[i], 32); | |
506 | if (err < 0) { | |
90774a93 | 507 | printf("failed to listen\n"); |
6f6d33f3 JF |
508 | goto out; |
509 | } | |
510 | } | |
511 | ||
512 | for (i = 2; i < 4; i++) { | |
513 | sfd[i] = socket(AF_INET, SOCK_STREAM, 0); | |
514 | if (sfd[i] < 0) | |
515 | goto out; | |
516 | err = setsockopt(sfd[i], SOL_SOCKET, SO_REUSEADDR, | |
517 | (char *)&one, sizeof(one)); | |
518 | if (err) { | |
519 | printf("set sock opt\n"); | |
520 | goto out; | |
521 | } | |
522 | memset(&addr, 0, sizeof(struct sockaddr_in)); | |
523 | addr.sin_family = AF_INET; | |
524 | addr.sin_addr.s_addr = inet_addr("127.0.0.1"); | |
525 | addr.sin_port = htons(ports[i - 2]); | |
526 | err = connect(sfd[i], (struct sockaddr *)&addr, sizeof(addr)); | |
527 | if (err) { | |
90774a93 | 528 | printf("failed to connect\n"); |
6f6d33f3 JF |
529 | goto out; |
530 | } | |
531 | } | |
532 | ||
533 | ||
534 | for (i = 4; i < 6; i++) { | |
535 | sfd[i] = accept(sfd[i - 4], NULL, NULL); | |
536 | if (sfd[i] < 0) { | |
537 | printf("accept failed\n"); | |
538 | goto out; | |
539 | } | |
540 | } | |
541 | ||
542 | /* Test sockmap with connected sockets */ | |
543 | fd = bpf_create_map(BPF_MAP_TYPE_SOCKMAP, | |
544 | sizeof(key), sizeof(value), | |
545 | 6, 0); | |
546 | if (fd < 0) { | |
547 | printf("Failed to create sockmap %i\n", fd); | |
548 | goto out_sockmap; | |
435bf0d3 JF |
549 | } |
550 | ||
551 | /* Test update with unsupported UDP socket */ | |
552 | udp = socket(AF_INET, SOCK_DGRAM, 0); | |
553 | i = 0; | |
554 | err = bpf_map_update_elem(fd, &i, &udp, BPF_ANY); | |
555 | if (!err) { | |
556 | printf("Failed socket SOCK_DGRAM allowed '%i:%i'\n", | |
557 | i, udp); | |
558 | goto out_sockmap; | |
6f6d33f3 JF |
559 | } |
560 | ||
464bc0fd | 561 | /* Test update without programs */ |
6f6d33f3 JF |
562 | for (i = 0; i < 6; i++) { |
563 | err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_ANY); | |
464bc0fd JF |
564 | if (err) { |
565 | printf("Failed noprog update sockmap '%i:%i'\n", | |
6f6d33f3 JF |
566 | i, sfd[i]); |
567 | goto out_sockmap; | |
568 | } | |
569 | } | |
570 | ||
5a67da2a | 571 | /* Test attaching/detaching bad fds */ |
464bc0fd | 572 | err = bpf_prog_attach(-1, fd, BPF_SK_SKB_STREAM_PARSER, 0); |
6f6d33f3 | 573 | if (!err) { |
464bc0fd JF |
574 | printf("Failed invalid parser prog attach\n"); |
575 | goto out_sockmap; | |
576 | } | |
577 | ||
578 | err = bpf_prog_attach(-1, fd, BPF_SK_SKB_STREAM_VERDICT, 0); | |
579 | if (!err) { | |
580 | printf("Failed invalid verdict prog attach\n"); | |
6f6d33f3 JF |
581 | goto out_sockmap; |
582 | } | |
583 | ||
5a67da2a JF |
584 | err = bpf_prog_attach(-1, fd, __MAX_BPF_ATTACH_TYPE, 0); |
585 | if (!err) { | |
586 | printf("Failed unknown prog attach\n"); | |
587 | goto out_sockmap; | |
588 | } | |
589 | ||
590 | err = bpf_prog_detach(fd, BPF_SK_SKB_STREAM_PARSER); | |
591 | if (err) { | |
592 | printf("Failed empty parser prog detach\n"); | |
593 | goto out_sockmap; | |
594 | } | |
595 | ||
596 | err = bpf_prog_detach(fd, BPF_SK_SKB_STREAM_VERDICT); | |
597 | if (err) { | |
598 | printf("Failed empty verdict prog detach\n"); | |
599 | goto out_sockmap; | |
600 | } | |
601 | ||
602 | err = bpf_prog_detach(fd, __MAX_BPF_ATTACH_TYPE); | |
603 | if (!err) { | |
604 | printf("Detach invalid prog successful\n"); | |
605 | goto out_sockmap; | |
606 | } | |
607 | ||
6f6d33f3 JF |
608 | /* Load SK_SKB program and Attach */ |
609 | err = bpf_prog_load(SOCKMAP_PARSE_PROG, | |
610 | BPF_PROG_TYPE_SK_SKB, &obj, &parse_prog); | |
611 | if (err) { | |
612 | printf("Failed to load SK_SKB parse prog\n"); | |
613 | goto out_sockmap; | |
614 | } | |
615 | ||
616 | err = bpf_prog_load(SOCKMAP_VERDICT_PROG, | |
617 | BPF_PROG_TYPE_SK_SKB, &obj, &verdict_prog); | |
618 | if (err) { | |
619 | printf("Failed to load SK_SKB verdict prog\n"); | |
620 | goto out_sockmap; | |
621 | } | |
622 | ||
6fd28865 JF |
623 | bpf_map_rx = bpf_object__find_map_by_name(obj, "sock_map_rx"); |
624 | if (IS_ERR(bpf_map_rx)) { | |
625 | printf("Failed to load map rx from verdict prog\n"); | |
6f6d33f3 JF |
626 | goto out_sockmap; |
627 | } | |
628 | ||
6fd28865 JF |
629 | map_fd_rx = bpf_map__fd(bpf_map_rx); |
630 | if (map_fd_rx < 0) { | |
6f6d33f3 JF |
631 | printf("Failed to get map fd\n"); |
632 | goto out_sockmap; | |
633 | } | |
634 | ||
6fd28865 JF |
635 | bpf_map_tx = bpf_object__find_map_by_name(obj, "sock_map_tx"); |
636 | if (IS_ERR(bpf_map_tx)) { | |
637 | printf("Failed to load map tx from verdict prog\n"); | |
638 | goto out_sockmap; | |
639 | } | |
640 | ||
641 | map_fd_tx = bpf_map__fd(bpf_map_tx); | |
642 | if (map_fd_tx < 0) { | |
643 | printf("Failed to get map tx fd\n"); | |
644 | goto out_sockmap; | |
645 | } | |
646 | ||
81374aaa JF |
647 | bpf_map_break = bpf_object__find_map_by_name(obj, "sock_map_break"); |
648 | if (IS_ERR(bpf_map_break)) { | |
649 | printf("Failed to load map tx from verdict prog\n"); | |
650 | goto out_sockmap; | |
651 | } | |
652 | ||
653 | map_fd_break = bpf_map__fd(bpf_map_break); | |
654 | if (map_fd_break < 0) { | |
655 | printf("Failed to get map tx fd\n"); | |
656 | goto out_sockmap; | |
657 | } | |
658 | ||
659 | err = bpf_prog_attach(parse_prog, map_fd_break, | |
660 | BPF_SK_SKB_STREAM_PARSER, 0); | |
661 | if (!err) { | |
662 | printf("Allowed attaching SK_SKB program to invalid map\n"); | |
663 | goto out_sockmap; | |
664 | } | |
665 | ||
6fd28865 | 666 | err = bpf_prog_attach(parse_prog, map_fd_rx, |
464bc0fd JF |
667 | BPF_SK_SKB_STREAM_PARSER, 0); |
668 | if (err) { | |
81374aaa | 669 | printf("Failed stream parser bpf prog attach\n"); |
464bc0fd JF |
670 | goto out_sockmap; |
671 | } | |
672 | ||
6fd28865 | 673 | err = bpf_prog_attach(verdict_prog, map_fd_rx, |
464bc0fd | 674 | BPF_SK_SKB_STREAM_VERDICT, 0); |
6f6d33f3 | 675 | if (err) { |
81374aaa | 676 | printf("Failed stream verdict bpf prog attach\n"); |
6f6d33f3 JF |
677 | goto out_sockmap; |
678 | } | |
679 | ||
5a67da2a JF |
680 | err = bpf_prog_attach(verdict_prog, map_fd_rx, |
681 | __MAX_BPF_ATTACH_TYPE, 0); | |
682 | if (!err) { | |
683 | printf("Attached unknown bpf prog\n"); | |
684 | goto out_sockmap; | |
685 | } | |
686 | ||
464bc0fd | 687 | /* Test map update elem afterwards fd lives in fd and map_fd */ |
6f6d33f3 | 688 | for (i = 0; i < 6; i++) { |
6fd28865 JF |
689 | err = bpf_map_update_elem(map_fd_rx, &i, &sfd[i], BPF_ANY); |
690 | if (err) { | |
691 | printf("Failed map_fd_rx update sockmap %i '%i:%i'\n", | |
692 | err, i, sfd[i]); | |
693 | goto out_sockmap; | |
694 | } | |
695 | err = bpf_map_update_elem(map_fd_tx, &i, &sfd[i], BPF_ANY); | |
6f6d33f3 | 696 | if (err) { |
6fd28865 | 697 | printf("Failed map_fd_tx update sockmap %i '%i:%i'\n", |
6f6d33f3 JF |
698 | err, i, sfd[i]); |
699 | goto out_sockmap; | |
700 | } | |
701 | } | |
702 | ||
703 | /* Test map delete elem and remove send/recv sockets */ | |
704 | for (i = 2; i < 4; i++) { | |
6fd28865 JF |
705 | err = bpf_map_delete_elem(map_fd_rx, &i); |
706 | if (err) { | |
707 | printf("Failed delete sockmap rx %i '%i:%i'\n", | |
708 | err, i, sfd[i]); | |
709 | goto out_sockmap; | |
710 | } | |
711 | err = bpf_map_delete_elem(map_fd_tx, &i); | |
6f6d33f3 | 712 | if (err) { |
6fd28865 | 713 | printf("Failed delete sockmap tx %i '%i:%i'\n", |
6f6d33f3 JF |
714 | err, i, sfd[i]); |
715 | goto out_sockmap; | |
716 | } | |
717 | } | |
718 | ||
719 | /* Test map send/recv */ | |
6fd28865 JF |
720 | for (i = 0; i < 2; i++) { |
721 | buf[0] = i; | |
722 | buf[1] = 0x5; | |
723 | sc = send(sfd[2], buf, 20, 0); | |
724 | if (sc < 0) { | |
725 | printf("Failed sockmap send\n"); | |
726 | goto out_sockmap; | |
727 | } | |
6f6d33f3 | 728 | |
6fd28865 JF |
729 | FD_ZERO(&w); |
730 | FD_SET(sfd[3], &w); | |
731 | to.tv_sec = 1; | |
732 | to.tv_usec = 0; | |
733 | s = select(sfd[3] + 1, &w, NULL, NULL, &to); | |
734 | if (s == -1) { | |
735 | perror("Failed sockmap select()"); | |
736 | goto out_sockmap; | |
737 | } else if (!s) { | |
738 | printf("Failed sockmap unexpected timeout\n"); | |
739 | goto out_sockmap; | |
740 | } | |
6f6d33f3 | 741 | |
6fd28865 JF |
742 | if (!FD_ISSET(sfd[3], &w)) { |
743 | printf("Failed sockmap select/recv\n"); | |
744 | goto out_sockmap; | |
745 | } | |
746 | ||
747 | rc = recv(sfd[3], buf, sizeof(buf), 0); | |
748 | if (rc < 0) { | |
749 | printf("Failed sockmap recv\n"); | |
750 | goto out_sockmap; | |
751 | } | |
6f6d33f3 JF |
752 | } |
753 | ||
6fd28865 JF |
754 | /* Negative null entry lookup from datapath should be dropped */ |
755 | buf[0] = 1; | |
756 | buf[1] = 12; | |
757 | sc = send(sfd[2], buf, 20, 0); | |
758 | if (sc < 0) { | |
759 | printf("Failed sockmap send\n"); | |
6f6d33f3 JF |
760 | goto out_sockmap; |
761 | } | |
762 | ||
464bc0fd JF |
763 | /* Push fd into same slot */ |
764 | i = 2; | |
6f6d33f3 JF |
765 | err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_NOEXIST); |
766 | if (!err) { | |
464bc0fd | 767 | printf("Failed allowed sockmap dup slot BPF_NOEXIST\n"); |
6f6d33f3 JF |
768 | goto out_sockmap; |
769 | } | |
770 | ||
771 | err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_ANY); | |
772 | if (err) { | |
464bc0fd | 773 | printf("Failed sockmap update new slot BPF_ANY\n"); |
6f6d33f3 JF |
774 | goto out_sockmap; |
775 | } | |
776 | ||
777 | err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_EXIST); | |
778 | if (err) { | |
464bc0fd | 779 | printf("Failed sockmap update new slot BPF_EXIST\n"); |
6f6d33f3 JF |
780 | goto out_sockmap; |
781 | } | |
782 | ||
464bc0fd JF |
783 | /* Delete the elems without programs */ |
784 | for (i = 0; i < 6; i++) { | |
785 | err = bpf_map_delete_elem(fd, &i); | |
786 | if (err) { | |
787 | printf("Failed delete sockmap %i '%i:%i'\n", | |
788 | err, i, sfd[i]); | |
789 | } | |
6f6d33f3 JF |
790 | } |
791 | ||
464bc0fd JF |
792 | /* Test having multiple maps open and set with programs on same fds */ |
793 | err = bpf_prog_attach(parse_prog, fd, | |
794 | BPF_SK_SKB_STREAM_PARSER, 0); | |
6f6d33f3 | 795 | if (err) { |
464bc0fd | 796 | printf("Failed fd bpf parse prog attach\n"); |
6f6d33f3 JF |
797 | goto out_sockmap; |
798 | } | |
464bc0fd JF |
799 | err = bpf_prog_attach(verdict_prog, fd, |
800 | BPF_SK_SKB_STREAM_VERDICT, 0); | |
6f6d33f3 | 801 | if (err) { |
464bc0fd | 802 | printf("Failed fd bpf verdict prog attach\n"); |
6f6d33f3 JF |
803 | goto out_sockmap; |
804 | } | |
805 | ||
464bc0fd JF |
806 | for (i = 4; i < 6; i++) { |
807 | err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_ANY); | |
808 | if (!err) { | |
809 | printf("Failed allowed duplicate programs in update ANY sockmap %i '%i:%i'\n", | |
810 | err, i, sfd[i]); | |
811 | goto out_sockmap; | |
812 | } | |
813 | err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_NOEXIST); | |
814 | if (!err) { | |
815 | printf("Failed allowed duplicate program in update NOEXIST sockmap %i '%i:%i'\n", | |
816 | err, i, sfd[i]); | |
817 | goto out_sockmap; | |
818 | } | |
819 | err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_EXIST); | |
820 | if (!err) { | |
821 | printf("Failed allowed duplicate program in update EXIST sockmap %i '%i:%i'\n", | |
822 | err, i, sfd[i]); | |
823 | goto out_sockmap; | |
824 | } | |
6f6d33f3 JF |
825 | } |
826 | ||
3f0d6a16 JF |
827 | /* Test tasks number of forked operations */ |
828 | for (i = 0; i < tasks; i++) { | |
829 | pid[i] = fork(); | |
830 | if (pid[i] == 0) { | |
831 | for (i = 0; i < 6; i++) { | |
832 | bpf_map_delete_elem(map_fd_tx, &i); | |
833 | bpf_map_delete_elem(map_fd_rx, &i); | |
834 | bpf_map_update_elem(map_fd_tx, &i, | |
835 | &sfd[i], BPF_ANY); | |
836 | bpf_map_update_elem(map_fd_rx, &i, | |
837 | &sfd[i], BPF_ANY); | |
838 | } | |
839 | exit(0); | |
840 | } else if (pid[i] == -1) { | |
841 | printf("Couldn't spawn #%d process!\n", i); | |
842 | exit(1); | |
843 | } | |
844 | } | |
845 | ||
846 | for (i = 0; i < tasks; i++) { | |
847 | int status; | |
848 | ||
849 | assert(waitpid(pid[i], &status, 0) == pid[i]); | |
850 | assert(status == 0); | |
851 | } | |
852 | ||
5a67da2a JF |
853 | err = bpf_prog_detach(map_fd_rx, __MAX_BPF_ATTACH_TYPE); |
854 | if (!err) { | |
855 | printf("Detached an invalid prog type.\n"); | |
856 | goto out_sockmap; | |
857 | } | |
858 | ||
859 | err = bpf_prog_detach(map_fd_rx, BPF_SK_SKB_STREAM_PARSER); | |
860 | if (err) { | |
861 | printf("Failed parser prog detach\n"); | |
862 | goto out_sockmap; | |
863 | } | |
864 | ||
865 | err = bpf_prog_detach(map_fd_rx, BPF_SK_SKB_STREAM_VERDICT); | |
866 | if (err) { | |
867 | printf("Failed parser prog detach\n"); | |
868 | goto out_sockmap; | |
869 | } | |
870 | ||
6f6d33f3 JF |
871 | /* Test map close sockets */ |
872 | for (i = 0; i < 6; i++) | |
873 | close(sfd[i]); | |
874 | close(fd); | |
6fd28865 | 875 | close(map_fd_rx); |
6f6d33f3 JF |
876 | bpf_object__close(obj); |
877 | return; | |
878 | out: | |
879 | for (i = 0; i < 6; i++) | |
880 | close(sfd[i]); | |
881 | printf("Failed to create sockmap '%i:%s'!\n", i, strerror(errno)); | |
882 | exit(1); | |
883 | out_sockmap: | |
884 | for (i = 0; i < 6; i++) | |
885 | close(sfd[i]); | |
886 | close(fd); | |
887 | exit(1); | |
888 | } | |
889 | ||
5aa5bd14 DB |
890 | #define MAP_SIZE (32 * 1024) |
891 | ||
892 | static void test_map_large(void) | |
893 | { | |
894 | struct bigkey { | |
895 | int a; | |
896 | char b[116]; | |
897 | long long c; | |
898 | } key; | |
899 | int fd, i, value; | |
900 | ||
f4874d01 | 901 | fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), |
5aa5bd14 DB |
902 | MAP_SIZE, map_flags); |
903 | if (fd < 0) { | |
904 | printf("Failed to create large map '%s'!\n", strerror(errno)); | |
905 | exit(1); | |
906 | } | |
907 | ||
908 | for (i = 0; i < MAP_SIZE; i++) { | |
909 | key = (struct bigkey) { .c = i }; | |
910 | value = i; | |
911 | ||
10ecc728 | 912 | assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == 0); |
5aa5bd14 DB |
913 | } |
914 | ||
915 | key.c = -1; | |
10ecc728 | 916 | assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == -1 && |
5aa5bd14 DB |
917 | errno == E2BIG); |
918 | ||
919 | /* Iterate through all elements. */ | |
8fe45924 TQ |
920 | assert(bpf_map_get_next_key(fd, NULL, &key) == 0); |
921 | key.c = -1; | |
5aa5bd14 | 922 | for (i = 0; i < MAP_SIZE; i++) |
5f155c25 MS |
923 | assert(bpf_map_get_next_key(fd, &key, &key) == 0); |
924 | assert(bpf_map_get_next_key(fd, &key, &key) == -1 && errno == ENOENT); | |
5aa5bd14 DB |
925 | |
926 | key.c = 0; | |
e5ff7c40 | 927 | assert(bpf_map_lookup_elem(fd, &key, &value) == 0 && value == 0); |
5aa5bd14 | 928 | key.a = 1; |
e5ff7c40 | 929 | assert(bpf_map_lookup_elem(fd, &key, &value) == -1 && errno == ENOENT); |
5aa5bd14 DB |
930 | |
931 | close(fd); | |
932 | } | |
933 | ||
934 | static void run_parallel(int tasks, void (*fn)(int task, void *data), | |
935 | void *data) | |
936 | { | |
937 | pid_t pid[tasks]; | |
938 | int i; | |
939 | ||
940 | for (i = 0; i < tasks; i++) { | |
941 | pid[i] = fork(); | |
942 | if (pid[i] == 0) { | |
943 | fn(i, data); | |
944 | exit(0); | |
945 | } else if (pid[i] == -1) { | |
946 | printf("Couldn't spawn #%d process!\n", i); | |
947 | exit(1); | |
948 | } | |
949 | } | |
950 | ||
951 | for (i = 0; i < tasks; i++) { | |
952 | int status; | |
953 | ||
954 | assert(waitpid(pid[i], &status, 0) == pid[i]); | |
955 | assert(status == 0); | |
956 | } | |
957 | } | |
958 | ||
959 | static void test_map_stress(void) | |
960 | { | |
961 | run_parallel(100, test_hashmap, NULL); | |
962 | run_parallel(100, test_hashmap_percpu, NULL); | |
8c290e60 | 963 | run_parallel(100, test_hashmap_sizes, NULL); |
5ecf51fd | 964 | run_parallel(100, test_hashmap_walk, NULL); |
5aa5bd14 DB |
965 | |
966 | run_parallel(100, test_arraymap, NULL); | |
967 | run_parallel(100, test_arraymap_percpu, NULL); | |
968 | } | |
969 | ||
970 | #define TASKS 1024 | |
971 | ||
972 | #define DO_UPDATE 1 | |
973 | #define DO_DELETE 0 | |
974 | ||
975 | static void do_work(int fn, void *data) | |
976 | { | |
977 | int do_update = ((int *)data)[1]; | |
978 | int fd = ((int *)data)[0]; | |
979 | int i, key, value; | |
980 | ||
981 | for (i = fn; i < MAP_SIZE; i += TASKS) { | |
982 | key = value = i; | |
983 | ||
984 | if (do_update) { | |
10ecc728 MS |
985 | assert(bpf_map_update_elem(fd, &key, &value, |
986 | BPF_NOEXIST) == 0); | |
987 | assert(bpf_map_update_elem(fd, &key, &value, | |
988 | BPF_EXIST) == 0); | |
5aa5bd14 | 989 | } else { |
e58383b8 | 990 | assert(bpf_map_delete_elem(fd, &key) == 0); |
5aa5bd14 DB |
991 | } |
992 | } | |
993 | } | |
994 | ||
995 | static void test_map_parallel(void) | |
996 | { | |
997 | int i, fd, key = 0, value = 0; | |
998 | int data[2]; | |
999 | ||
f4874d01 | 1000 | fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), |
5aa5bd14 DB |
1001 | MAP_SIZE, map_flags); |
1002 | if (fd < 0) { | |
1003 | printf("Failed to create map for parallel test '%s'!\n", | |
1004 | strerror(errno)); | |
1005 | exit(1); | |
1006 | } | |
1007 | ||
1008 | /* Use the same fd in children to add elements to this map: | |
1009 | * child_0 adds key=0, key=1024, key=2048, ... | |
1010 | * child_1 adds key=1, key=1025, key=2049, ... | |
1011 | * child_1023 adds key=1023, ... | |
1012 | */ | |
1013 | data[0] = fd; | |
1014 | data[1] = DO_UPDATE; | |
1015 | run_parallel(TASKS, do_work, data); | |
1016 | ||
1017 | /* Check that key=0 is already there. */ | |
10ecc728 | 1018 | assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == -1 && |
5aa5bd14 DB |
1019 | errno == EEXIST); |
1020 | ||
1021 | /* Check that all elements were inserted. */ | |
8fe45924 | 1022 | assert(bpf_map_get_next_key(fd, NULL, &key) == 0); |
5aa5bd14 DB |
1023 | key = -1; |
1024 | for (i = 0; i < MAP_SIZE; i++) | |
5f155c25 MS |
1025 | assert(bpf_map_get_next_key(fd, &key, &key) == 0); |
1026 | assert(bpf_map_get_next_key(fd, &key, &key) == -1 && errno == ENOENT); | |
5aa5bd14 DB |
1027 | |
1028 | /* Another check for all elements */ | |
1029 | for (i = 0; i < MAP_SIZE; i++) { | |
1030 | key = MAP_SIZE - i - 1; | |
1031 | ||
e5ff7c40 | 1032 | assert(bpf_map_lookup_elem(fd, &key, &value) == 0 && |
5aa5bd14 DB |
1033 | value == key); |
1034 | } | |
1035 | ||
1036 | /* Now let's delete all elemenets in parallel. */ | |
1037 | data[1] = DO_DELETE; | |
1038 | run_parallel(TASKS, do_work, data); | |
1039 | ||
1040 | /* Nothing should be left. */ | |
1041 | key = -1; | |
8fe45924 | 1042 | assert(bpf_map_get_next_key(fd, NULL, &key) == -1 && errno == ENOENT); |
5f155c25 | 1043 | assert(bpf_map_get_next_key(fd, &key, &key) == -1 && errno == ENOENT); |
5aa5bd14 DB |
1044 | } |
1045 | ||
1046 | static void run_all_tests(void) | |
1047 | { | |
1048 | test_hashmap(0, NULL); | |
1049 | test_hashmap_percpu(0, NULL); | |
5ecf51fd | 1050 | test_hashmap_walk(0, NULL); |
5aa5bd14 DB |
1051 | |
1052 | test_arraymap(0, NULL); | |
1053 | test_arraymap_percpu(0, NULL); | |
1054 | ||
1055 | test_arraymap_percpu_many_keys(); | |
1056 | ||
81f6bf81 | 1057 | test_devmap(0, NULL); |
6f6d33f3 | 1058 | test_sockmap(0, NULL); |
81f6bf81 | 1059 | |
5aa5bd14 DB |
1060 | test_map_large(); |
1061 | test_map_parallel(); | |
1062 | test_map_stress(); | |
1063 | } | |
1064 | ||
1065 | int main(void) | |
1066 | { | |
1067 | struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY }; | |
1068 | ||
1069 | setrlimit(RLIMIT_MEMLOCK, &rinf); | |
1070 | ||
1071 | map_flags = 0; | |
1072 | run_all_tests(); | |
1073 | ||
1074 | map_flags = BPF_F_NO_PREALLOC; | |
1075 | run_all_tests(); | |
1076 | ||
1077 | printf("test_maps: OK\n"); | |
1078 | return 0; | |
1079 | } |