Loading...
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright (c) 2016 Facebook
4 */
5#define _GNU_SOURCE
6#include <stdio.h>
7#include <unistd.h>
8#include <errno.h>
9#include <string.h>
10#include <assert.h>
11#include <sched.h>
12#include <stdlib.h>
13#include <time.h>
14
15#include <sys/wait.h>
16
17#include <bpf/bpf.h>
18#include <bpf/libbpf.h>
19
20#include "bpf_util.h"
21#include "../../../include/linux/filter.h"
22
23#define LOCAL_FREE_TARGET (128)
24#define PERCPU_FREE_TARGET (4)
25
26static int nr_cpus;
27
28static int create_map(int map_type, int map_flags, unsigned int size)
29{
30 LIBBPF_OPTS(bpf_map_create_opts, opts, .map_flags = map_flags);
31 int map_fd;
32
33 map_fd = bpf_map_create(map_type, NULL, sizeof(unsigned long long),
34 sizeof(unsigned long long), size, &opts);
35
36 if (map_fd == -1)
37 perror("bpf_map_create");
38
39 return map_fd;
40}
41
42static int bpf_map_lookup_elem_with_ref_bit(int fd, unsigned long long key,
43 void *value)
44{
45 struct bpf_insn insns[] = {
46 BPF_LD_MAP_VALUE(BPF_REG_9, 0, 0),
47 BPF_LD_MAP_FD(BPF_REG_1, fd),
48 BPF_LD_IMM64(BPF_REG_3, key),
49 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
50 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
51 BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, 0),
52 BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
53 BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4),
54 BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, 0),
55 BPF_STX_MEM(BPF_DW, BPF_REG_9, BPF_REG_1, 0),
56 BPF_MOV64_IMM(BPF_REG_0, 42),
57 BPF_JMP_IMM(BPF_JA, 0, 0, 1),
58 BPF_MOV64_IMM(BPF_REG_0, 1),
59 BPF_EXIT_INSN(),
60 };
61 __u8 data[64] = {};
62 int mfd, pfd, ret, zero = 0;
63 LIBBPF_OPTS(bpf_test_run_opts, topts,
64 .data_in = data,
65 .data_size_in = sizeof(data),
66 .repeat = 1,
67 );
68
69 mfd = bpf_map_create(BPF_MAP_TYPE_ARRAY, NULL, sizeof(int), sizeof(__u64), 1, NULL);
70 if (mfd < 0)
71 return -1;
72
73 insns[0].imm = mfd;
74
75 pfd = bpf_prog_load(BPF_PROG_TYPE_SCHED_CLS, NULL, "GPL", insns, ARRAY_SIZE(insns), NULL);
76 if (pfd < 0) {
77 close(mfd);
78 return -1;
79 }
80
81 ret = bpf_prog_test_run_opts(pfd, &topts);
82 if (ret < 0 || topts.retval != 42) {
83 ret = -1;
84 } else {
85 assert(!bpf_map_lookup_elem(mfd, &zero, value));
86 ret = 0;
87 }
88 close(pfd);
89 close(mfd);
90 return ret;
91}
92
93static int map_subset(int map0, int map1)
94{
95 unsigned long long next_key = 0;
96 unsigned long long value0[nr_cpus], value1[nr_cpus];
97 int ret;
98
99 while (!bpf_map_get_next_key(map1, &next_key, &next_key)) {
100 assert(!bpf_map_lookup_elem(map1, &next_key, value1));
101 ret = bpf_map_lookup_elem(map0, &next_key, value0);
102 if (ret) {
103 printf("key:%llu not found from map. %s(%d)\n",
104 next_key, strerror(errno), errno);
105 return 0;
106 }
107 if (value0[0] != value1[0]) {
108 printf("key:%llu value0:%llu != value1:%llu\n",
109 next_key, value0[0], value1[0]);
110 return 0;
111 }
112 }
113 return 1;
114}
115
116static int map_equal(int lru_map, int expected)
117{
118 return map_subset(lru_map, expected) && map_subset(expected, lru_map);
119}
120
121static int sched_next_online(int pid, int *next_to_try)
122{
123 cpu_set_t cpuset;
124 int next = *next_to_try;
125 int ret = -1;
126
127 while (next < nr_cpus) {
128 CPU_ZERO(&cpuset);
129 CPU_SET(next++, &cpuset);
130 if (!sched_setaffinity(pid, sizeof(cpuset), &cpuset)) {
131 ret = 0;
132 break;
133 }
134 }
135
136 *next_to_try = next;
137 return ret;
138}
139
140/* Size of the LRU map is 2
141 * Add key=1 (+1 key)
142 * Add key=2 (+1 key)
143 * Lookup Key=1
144 * Add Key=3
145 * => Key=2 will be removed by LRU
146 * Iterate map. Only found key=1 and key=3
147 */
148static void test_lru_sanity0(int map_type, int map_flags)
149{
150 unsigned long long key, value[nr_cpus];
151 int lru_map_fd, expected_map_fd;
152 int next_cpu = 0;
153
154 printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
155 map_flags);
156
157 assert(sched_next_online(0, &next_cpu) != -1);
158
159 if (map_flags & BPF_F_NO_COMMON_LRU)
160 lru_map_fd = create_map(map_type, map_flags, 2 * nr_cpus);
161 else
162 lru_map_fd = create_map(map_type, map_flags, 2);
163 assert(lru_map_fd != -1);
164
165 expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, 2);
166 assert(expected_map_fd != -1);
167
168 value[0] = 1234;
169
170 /* insert key=1 element */
171
172 key = 1;
173 assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
174 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
175 BPF_NOEXIST));
176
177 /* BPF_NOEXIST means: add new element if it doesn't exist */
178 assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST) == -EEXIST);
179 /* key=1 already exists */
180
181 assert(bpf_map_update_elem(lru_map_fd, &key, value, -1) == -EINVAL);
182
183 /* insert key=2 element */
184
185 /* check that key=2 is not found */
186 key = 2;
187 assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -ENOENT);
188
189 /* BPF_EXIST means: update existing element */
190 assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_EXIST) == -ENOENT);
191 /* key=2 is not there */
192
193 assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
194
195 /* insert key=3 element */
196
197 /* check that key=3 is not found */
198 key = 3;
199 assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -ENOENT);
200
201 /* check that key=1 can be found and mark the ref bit to
202 * stop LRU from removing key=1
203 */
204 key = 1;
205 assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
206 assert(value[0] == 1234);
207
208 key = 3;
209 assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
210 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
211 BPF_NOEXIST));
212
213 /* key=2 has been removed from the LRU */
214 key = 2;
215 assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -ENOENT);
216
217 /* lookup elem key=1 and delete it, then check it doesn't exist */
218 key = 1;
219 assert(!bpf_map_lookup_and_delete_elem(lru_map_fd, &key, &value));
220 assert(value[0] == 1234);
221
222 /* remove the same element from the expected map */
223 assert(!bpf_map_delete_elem(expected_map_fd, &key));
224
225 assert(map_equal(lru_map_fd, expected_map_fd));
226
227 close(expected_map_fd);
228 close(lru_map_fd);
229
230 printf("Pass\n");
231}
232
233/* Size of the LRU map is 1.5*tgt_free
234 * Insert 1 to tgt_free (+tgt_free keys)
235 * Lookup 1 to tgt_free/2
236 * Insert 1+tgt_free to 2*tgt_free (+tgt_free keys)
237 * => 1+tgt_free/2 to LOCALFREE_TARGET will be removed by LRU
238 */
239static void test_lru_sanity1(int map_type, int map_flags, unsigned int tgt_free)
240{
241 unsigned long long key, end_key, value[nr_cpus];
242 int lru_map_fd, expected_map_fd;
243 unsigned int batch_size;
244 unsigned int map_size;
245 int next_cpu = 0;
246
247 if (map_flags & BPF_F_NO_COMMON_LRU)
248 /* This test is only applicable to common LRU list */
249 return;
250
251 printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
252 map_flags);
253
254 assert(sched_next_online(0, &next_cpu) != -1);
255
256 batch_size = tgt_free / 2;
257 assert(batch_size * 2 == tgt_free);
258
259 map_size = tgt_free + batch_size;
260 lru_map_fd = create_map(map_type, map_flags, map_size);
261 assert(lru_map_fd != -1);
262
263 expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, map_size);
264 assert(expected_map_fd != -1);
265
266 value[0] = 1234;
267
268 /* Insert 1 to tgt_free (+tgt_free keys) */
269 end_key = 1 + tgt_free;
270 for (key = 1; key < end_key; key++)
271 assert(!bpf_map_update_elem(lru_map_fd, &key, value,
272 BPF_NOEXIST));
273
274 /* Lookup 1 to tgt_free/2 */
275 end_key = 1 + batch_size;
276 for (key = 1; key < end_key; key++) {
277 assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
278 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
279 BPF_NOEXIST));
280 }
281
282 /* Insert 1+tgt_free to 2*tgt_free
283 * => 1+tgt_free/2 to LOCALFREE_TARGET will be
284 * removed by LRU
285 */
286 key = 1 + tgt_free;
287 end_key = key + tgt_free;
288 for (; key < end_key; key++) {
289 assert(!bpf_map_update_elem(lru_map_fd, &key, value,
290 BPF_NOEXIST));
291 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
292 BPF_NOEXIST));
293 }
294
295 assert(map_equal(lru_map_fd, expected_map_fd));
296
297 close(expected_map_fd);
298 close(lru_map_fd);
299
300 printf("Pass\n");
301}
302
303/* Size of the LRU map 1.5 * tgt_free
304 * Insert 1 to tgt_free (+tgt_free keys)
305 * Update 1 to tgt_free/2
306 * => The original 1 to tgt_free/2 will be removed due to
307 * the LRU shrink process
308 * Re-insert 1 to tgt_free/2 again and do a lookup immeidately
309 * Insert 1+tgt_free to tgt_free*3/2
310 * Insert 1+tgt_free*3/2 to tgt_free*5/2
311 * => Key 1+tgt_free to tgt_free*3/2
312 * will be removed from LRU because it has never
313 * been lookup and ref bit is not set
314 */
315static void test_lru_sanity2(int map_type, int map_flags, unsigned int tgt_free)
316{
317 unsigned long long key, value[nr_cpus];
318 unsigned long long end_key;
319 int lru_map_fd, expected_map_fd;
320 unsigned int batch_size;
321 unsigned int map_size;
322 int next_cpu = 0;
323
324 if (map_flags & BPF_F_NO_COMMON_LRU)
325 /* This test is only applicable to common LRU list */
326 return;
327
328 printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
329 map_flags);
330
331 assert(sched_next_online(0, &next_cpu) != -1);
332
333 batch_size = tgt_free / 2;
334 assert(batch_size * 2 == tgt_free);
335
336 map_size = tgt_free + batch_size;
337 lru_map_fd = create_map(map_type, map_flags, map_size);
338 assert(lru_map_fd != -1);
339
340 expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, map_size);
341 assert(expected_map_fd != -1);
342
343 value[0] = 1234;
344
345 /* Insert 1 to tgt_free (+tgt_free keys) */
346 end_key = 1 + tgt_free;
347 for (key = 1; key < end_key; key++)
348 assert(!bpf_map_update_elem(lru_map_fd, &key, value,
349 BPF_NOEXIST));
350
351 /* Any bpf_map_update_elem will require to acquire a new node
352 * from LRU first.
353 *
354 * The local list is running out of free nodes.
355 * It gets from the global LRU list which tries to
356 * shrink the inactive list to get tgt_free
357 * number of free nodes.
358 *
359 * Hence, the oldest key 1 to tgt_free/2
360 * are removed from the LRU list.
361 */
362 key = 1;
363 if (map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH) {
364 assert(!bpf_map_update_elem(lru_map_fd, &key, value,
365 BPF_NOEXIST));
366 assert(!bpf_map_delete_elem(lru_map_fd, &key));
367 } else {
368 assert(bpf_map_update_elem(lru_map_fd, &key, value,
369 BPF_EXIST));
370 }
371
372 /* Re-insert 1 to tgt_free/2 again and do a lookup
373 * immeidately.
374 */
375 end_key = 1 + batch_size;
376 value[0] = 4321;
377 for (key = 1; key < end_key; key++) {
378 assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -ENOENT);
379 assert(!bpf_map_update_elem(lru_map_fd, &key, value,
380 BPF_NOEXIST));
381 assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
382 assert(value[0] == 4321);
383 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
384 BPF_NOEXIST));
385 }
386
387 value[0] = 1234;
388
389 /* Insert 1+tgt_free to tgt_free*3/2 */
390 end_key = 1 + tgt_free + batch_size;
391 for (key = 1 + tgt_free; key < end_key; key++)
392 /* These newly added but not referenced keys will be
393 * gone during the next LRU shrink.
394 */
395 assert(!bpf_map_update_elem(lru_map_fd, &key, value,
396 BPF_NOEXIST));
397
398 /* Insert 1+tgt_free*3/2 to tgt_free*5/2 */
399 end_key = key + tgt_free;
400 for (; key < end_key; key++) {
401 assert(!bpf_map_update_elem(lru_map_fd, &key, value,
402 BPF_NOEXIST));
403 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
404 BPF_NOEXIST));
405 }
406
407 assert(map_equal(lru_map_fd, expected_map_fd));
408
409 close(expected_map_fd);
410 close(lru_map_fd);
411
412 printf("Pass\n");
413}
414
415/* Size of the LRU map is 2*tgt_free
416 * It is to test the active/inactive list rotation
417 * Insert 1 to 2*tgt_free (+2*tgt_free keys)
418 * Lookup key 1 to tgt_free*3/2
419 * Add 1+2*tgt_free to tgt_free*5/2 (+tgt_free/2 keys)
420 * => key 1+tgt_free*3/2 to 2*tgt_free are removed from LRU
421 */
422static void test_lru_sanity3(int map_type, int map_flags, unsigned int tgt_free)
423{
424 unsigned long long key, end_key, value[nr_cpus];
425 int lru_map_fd, expected_map_fd;
426 unsigned int batch_size;
427 unsigned int map_size;
428 int next_cpu = 0;
429
430 if (map_flags & BPF_F_NO_COMMON_LRU)
431 /* This test is only applicable to common LRU list */
432 return;
433
434 printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
435 map_flags);
436
437 assert(sched_next_online(0, &next_cpu) != -1);
438
439 batch_size = tgt_free / 2;
440 assert(batch_size * 2 == tgt_free);
441
442 map_size = tgt_free * 2;
443 lru_map_fd = create_map(map_type, map_flags, map_size);
444 assert(lru_map_fd != -1);
445
446 expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, map_size);
447 assert(expected_map_fd != -1);
448
449 value[0] = 1234;
450
451 /* Insert 1 to 2*tgt_free (+2*tgt_free keys) */
452 end_key = 1 + (2 * tgt_free);
453 for (key = 1; key < end_key; key++)
454 assert(!bpf_map_update_elem(lru_map_fd, &key, value,
455 BPF_NOEXIST));
456
457 /* Lookup key 1 to tgt_free*3/2 */
458 end_key = tgt_free + batch_size;
459 for (key = 1; key < end_key; key++) {
460 assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
461 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
462 BPF_NOEXIST));
463 }
464
465 /* Add 1+2*tgt_free to tgt_free*5/2
466 * (+tgt_free/2 keys)
467 */
468 key = 2 * tgt_free + 1;
469 end_key = key + batch_size;
470 for (; key < end_key; key++) {
471 assert(!bpf_map_update_elem(lru_map_fd, &key, value,
472 BPF_NOEXIST));
473 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
474 BPF_NOEXIST));
475 }
476
477 assert(map_equal(lru_map_fd, expected_map_fd));
478
479 close(expected_map_fd);
480 close(lru_map_fd);
481
482 printf("Pass\n");
483}
484
485/* Test deletion */
486static void test_lru_sanity4(int map_type, int map_flags, unsigned int tgt_free)
487{
488 int lru_map_fd, expected_map_fd;
489 unsigned long long key, value[nr_cpus];
490 unsigned long long end_key;
491 int next_cpu = 0;
492
493 printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
494 map_flags);
495
496 assert(sched_next_online(0, &next_cpu) != -1);
497
498 if (map_flags & BPF_F_NO_COMMON_LRU)
499 lru_map_fd = create_map(map_type, map_flags,
500 3 * tgt_free * nr_cpus);
501 else
502 lru_map_fd = create_map(map_type, map_flags, 3 * tgt_free);
503 assert(lru_map_fd != -1);
504
505 expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0,
506 3 * tgt_free);
507 assert(expected_map_fd != -1);
508
509 value[0] = 1234;
510
511 for (key = 1; key <= 2 * tgt_free; key++)
512 assert(!bpf_map_update_elem(lru_map_fd, &key, value,
513 BPF_NOEXIST));
514
515 key = 1;
516 assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
517
518 for (key = 1; key <= tgt_free; key++) {
519 assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
520 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
521 BPF_NOEXIST));
522 }
523
524 for (; key <= 2 * tgt_free; key++) {
525 assert(!bpf_map_delete_elem(lru_map_fd, &key));
526 assert(bpf_map_delete_elem(lru_map_fd, &key));
527 }
528
529 end_key = key + 2 * tgt_free;
530 for (; key < end_key; key++) {
531 assert(!bpf_map_update_elem(lru_map_fd, &key, value,
532 BPF_NOEXIST));
533 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
534 BPF_NOEXIST));
535 }
536
537 assert(map_equal(lru_map_fd, expected_map_fd));
538
539 close(expected_map_fd);
540 close(lru_map_fd);
541
542 printf("Pass\n");
543}
544
545static void do_test_lru_sanity5(unsigned long long last_key, int map_fd)
546{
547 unsigned long long key, value[nr_cpus];
548
549 /* Ensure the last key inserted by previous CPU can be found */
550 assert(!bpf_map_lookup_elem_with_ref_bit(map_fd, last_key, value));
551 value[0] = 1234;
552
553 key = last_key + 1;
554 assert(!bpf_map_update_elem(map_fd, &key, value, BPF_NOEXIST));
555 assert(!bpf_map_lookup_elem_with_ref_bit(map_fd, key, value));
556
557 /* Cannot find the last key because it was removed by LRU */
558 assert(bpf_map_lookup_elem(map_fd, &last_key, value) == -ENOENT);
559}
560
561/* Test map with only one element */
562static void test_lru_sanity5(int map_type, int map_flags)
563{
564 unsigned long long key, value[nr_cpus];
565 int next_cpu = 0;
566 int map_fd;
567
568 if (map_flags & BPF_F_NO_COMMON_LRU)
569 return;
570
571 printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
572 map_flags);
573
574 map_fd = create_map(map_type, map_flags, 1);
575 assert(map_fd != -1);
576
577 value[0] = 1234;
578 key = 0;
579 assert(!bpf_map_update_elem(map_fd, &key, value, BPF_NOEXIST));
580
581 while (sched_next_online(0, &next_cpu) != -1) {
582 pid_t pid;
583
584 pid = fork();
585 if (pid == 0) {
586 do_test_lru_sanity5(key, map_fd);
587 exit(0);
588 } else if (pid == -1) {
589 printf("couldn't spawn process to test key:%llu\n",
590 key);
591 exit(1);
592 } else {
593 int status;
594
595 assert(waitpid(pid, &status, 0) == pid);
596 assert(status == 0);
597 key++;
598 }
599 }
600
601 close(map_fd);
602 /* At least one key should be tested */
603 assert(key > 0);
604
605 printf("Pass\n");
606}
607
608/* Test list rotation for BPF_F_NO_COMMON_LRU map */
609static void test_lru_sanity6(int map_type, int map_flags, int tgt_free)
610{
611 int lru_map_fd, expected_map_fd;
612 unsigned long long key, value[nr_cpus];
613 unsigned int map_size = tgt_free * 2;
614 int next_cpu = 0;
615
616 if (!(map_flags & BPF_F_NO_COMMON_LRU))
617 return;
618
619 printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
620 map_flags);
621
622 assert(sched_next_online(0, &next_cpu) != -1);
623
624 expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, map_size);
625 assert(expected_map_fd != -1);
626
627 lru_map_fd = create_map(map_type, map_flags, map_size * nr_cpus);
628 assert(lru_map_fd != -1);
629
630 value[0] = 1234;
631
632 for (key = 1; key <= tgt_free; key++) {
633 assert(!bpf_map_update_elem(lru_map_fd, &key, value,
634 BPF_NOEXIST));
635 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
636 BPF_NOEXIST));
637 }
638
639 for (; key <= tgt_free * 2; key++) {
640 unsigned long long stable_key;
641
642 /* Make ref bit sticky for key: [1, tgt_free] */
643 for (stable_key = 1; stable_key <= tgt_free; stable_key++) {
644 /* Mark the ref bit */
645 assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd,
646 stable_key, value));
647 }
648 assert(!bpf_map_update_elem(lru_map_fd, &key, value,
649 BPF_NOEXIST));
650 }
651
652 for (; key <= tgt_free * 3; key++) {
653 assert(!bpf_map_update_elem(lru_map_fd, &key, value,
654 BPF_NOEXIST));
655 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
656 BPF_NOEXIST));
657 }
658
659 assert(map_equal(lru_map_fd, expected_map_fd));
660
661 close(expected_map_fd);
662 close(lru_map_fd);
663
664 printf("Pass\n");
665}
666
667/* Size of the LRU map is 2
668 * Add key=1 (+1 key)
669 * Add key=2 (+1 key)
670 * Lookup Key=1 (datapath)
671 * Lookup Key=2 (syscall)
672 * Add Key=3
673 * => Key=2 will be removed by LRU
674 * Iterate map. Only found key=1 and key=3
675 */
676static void test_lru_sanity7(int map_type, int map_flags)
677{
678 unsigned long long key, value[nr_cpus];
679 int lru_map_fd, expected_map_fd;
680 int next_cpu = 0;
681
682 printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
683 map_flags);
684
685 assert(sched_next_online(0, &next_cpu) != -1);
686
687 if (map_flags & BPF_F_NO_COMMON_LRU)
688 lru_map_fd = create_map(map_type, map_flags, 2 * nr_cpus);
689 else
690 lru_map_fd = create_map(map_type, map_flags, 2);
691 assert(lru_map_fd != -1);
692
693 expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, 2);
694 assert(expected_map_fd != -1);
695
696 value[0] = 1234;
697
698 /* insert key=1 element */
699
700 key = 1;
701 assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
702 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
703 BPF_NOEXIST));
704
705 /* BPF_NOEXIST means: add new element if it doesn't exist */
706 assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST) == -EEXIST);
707 /* key=1 already exists */
708
709 /* insert key=2 element */
710
711 /* check that key=2 is not found */
712 key = 2;
713 assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -ENOENT);
714
715 /* BPF_EXIST means: update existing element */
716 assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_EXIST) == -ENOENT);
717 /* key=2 is not there */
718
719 assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
720
721 /* insert key=3 element */
722
723 /* check that key=3 is not found */
724 key = 3;
725 assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -ENOENT);
726
727 /* check that key=1 can be found and mark the ref bit to
728 * stop LRU from removing key=1
729 */
730 key = 1;
731 assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
732 assert(value[0] == 1234);
733
734 /* check that key=2 can be found and do _not_ mark ref bit.
735 * this will be evicted on next update.
736 */
737 key = 2;
738 assert(!bpf_map_lookup_elem(lru_map_fd, &key, value));
739 assert(value[0] == 1234);
740
741 key = 3;
742 assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
743 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
744 BPF_NOEXIST));
745
746 /* key=2 has been removed from the LRU */
747 key = 2;
748 assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -ENOENT);
749
750 assert(map_equal(lru_map_fd, expected_map_fd));
751
752 close(expected_map_fd);
753 close(lru_map_fd);
754
755 printf("Pass\n");
756}
757
758/* Size of the LRU map is 2
759 * Add key=1 (+1 key)
760 * Add key=2 (+1 key)
761 * Lookup Key=1 (syscall)
762 * Lookup Key=2 (datapath)
763 * Add Key=3
764 * => Key=1 will be removed by LRU
765 * Iterate map. Only found key=2 and key=3
766 */
767static void test_lru_sanity8(int map_type, int map_flags)
768{
769 unsigned long long key, value[nr_cpus];
770 int lru_map_fd, expected_map_fd;
771 int next_cpu = 0;
772
773 printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
774 map_flags);
775
776 assert(sched_next_online(0, &next_cpu) != -1);
777
778 if (map_flags & BPF_F_NO_COMMON_LRU)
779 lru_map_fd = create_map(map_type, map_flags, 2 * nr_cpus);
780 else
781 lru_map_fd = create_map(map_type, map_flags, 2);
782 assert(lru_map_fd != -1);
783
784 expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, 2);
785 assert(expected_map_fd != -1);
786
787 value[0] = 1234;
788
789 /* insert key=1 element */
790
791 key = 1;
792 assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
793
794 /* BPF_NOEXIST means: add new element if it doesn't exist */
795 assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST) == -EEXIST);
796 /* key=1 already exists */
797
798 /* insert key=2 element */
799
800 /* check that key=2 is not found */
801 key = 2;
802 assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -ENOENT);
803
804 /* BPF_EXIST means: update existing element */
805 assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_EXIST) == -ENOENT);
806 /* key=2 is not there */
807
808 assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
809 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
810 BPF_NOEXIST));
811
812 /* insert key=3 element */
813
814 /* check that key=3 is not found */
815 key = 3;
816 assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -ENOENT);
817
818 /* check that key=1 can be found and do _not_ mark ref bit.
819 * this will be evicted on next update.
820 */
821 key = 1;
822 assert(!bpf_map_lookup_elem(lru_map_fd, &key, value));
823 assert(value[0] == 1234);
824
825 /* check that key=2 can be found and mark the ref bit to
826 * stop LRU from removing key=2
827 */
828 key = 2;
829 assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
830 assert(value[0] == 1234);
831
832 key = 3;
833 assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
834 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
835 BPF_NOEXIST));
836
837 /* key=1 has been removed from the LRU */
838 key = 1;
839 assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -ENOENT);
840
841 assert(map_equal(lru_map_fd, expected_map_fd));
842
843 close(expected_map_fd);
844 close(lru_map_fd);
845
846 printf("Pass\n");
847}
848
849int main(int argc, char **argv)
850{
851 int map_types[] = {BPF_MAP_TYPE_LRU_HASH,
852 BPF_MAP_TYPE_LRU_PERCPU_HASH};
853 int map_flags[] = {0, BPF_F_NO_COMMON_LRU};
854 int t, f;
855
856 setbuf(stdout, NULL);
857
858 nr_cpus = bpf_num_possible_cpus();
859 assert(nr_cpus != -1);
860 printf("nr_cpus:%d\n\n", nr_cpus);
861
862 /* Use libbpf 1.0 API mode */
863 libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
864
865 for (f = 0; f < ARRAY_SIZE(map_flags); f++) {
866 unsigned int tgt_free = (map_flags[f] & BPF_F_NO_COMMON_LRU) ?
867 PERCPU_FREE_TARGET : LOCAL_FREE_TARGET;
868
869 for (t = 0; t < ARRAY_SIZE(map_types); t++) {
870 test_lru_sanity0(map_types[t], map_flags[f]);
871 test_lru_sanity1(map_types[t], map_flags[f], tgt_free);
872 test_lru_sanity2(map_types[t], map_flags[f], tgt_free);
873 test_lru_sanity3(map_types[t], map_flags[f], tgt_free);
874 test_lru_sanity4(map_types[t], map_flags[f], tgt_free);
875 test_lru_sanity5(map_types[t], map_flags[f]);
876 test_lru_sanity6(map_types[t], map_flags[f], tgt_free);
877 test_lru_sanity7(map_types[t], map_flags[f]);
878 test_lru_sanity8(map_types[t], map_flags[f]);
879
880 printf("\n");
881 }
882 }
883
884 return 0;
885}
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright (c) 2016 Facebook
4 */
5#define _GNU_SOURCE
6#include <stdio.h>
7#include <unistd.h>
8#include <errno.h>
9#include <string.h>
10#include <assert.h>
11#include <sched.h>
12#include <stdlib.h>
13#include <time.h>
14
15#include <sys/wait.h>
16
17#include <bpf/bpf.h>
18#include <bpf/libbpf.h>
19
20#include "bpf_util.h"
21#include "bpf_rlimit.h"
22#include "../../../include/linux/filter.h"
23
24#define LOCAL_FREE_TARGET (128)
25#define PERCPU_FREE_TARGET (4)
26
27static int nr_cpus;
28
29static int create_map(int map_type, int map_flags, unsigned int size)
30{
31 int map_fd;
32
33 map_fd = bpf_create_map(map_type, sizeof(unsigned long long),
34 sizeof(unsigned long long), size, map_flags);
35
36 if (map_fd == -1)
37 perror("bpf_create_map");
38
39 return map_fd;
40}
41
42static int bpf_map_lookup_elem_with_ref_bit(int fd, unsigned long long key,
43 void *value)
44{
45 struct bpf_load_program_attr prog;
46 struct bpf_create_map_attr map;
47 struct bpf_insn insns[] = {
48 BPF_LD_MAP_VALUE(BPF_REG_9, 0, 0),
49 BPF_LD_MAP_FD(BPF_REG_1, fd),
50 BPF_LD_IMM64(BPF_REG_3, key),
51 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
52 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
53 BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, 0),
54 BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
55 BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4),
56 BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, 0),
57 BPF_STX_MEM(BPF_DW, BPF_REG_9, BPF_REG_1, 0),
58 BPF_MOV64_IMM(BPF_REG_0, 42),
59 BPF_JMP_IMM(BPF_JA, 0, 0, 1),
60 BPF_MOV64_IMM(BPF_REG_0, 1),
61 BPF_EXIT_INSN(),
62 };
63 __u8 data[64] = {};
64 int mfd, pfd, ret, zero = 0;
65 __u32 retval = 0;
66
67 memset(&map, 0, sizeof(map));
68 map.map_type = BPF_MAP_TYPE_ARRAY;
69 map.key_size = sizeof(int);
70 map.value_size = sizeof(unsigned long long);
71 map.max_entries = 1;
72
73 mfd = bpf_create_map_xattr(&map);
74 if (mfd < 0)
75 return -1;
76
77 insns[0].imm = mfd;
78
79 memset(&prog, 0, sizeof(prog));
80 prog.prog_type = BPF_PROG_TYPE_SCHED_CLS;
81 prog.insns = insns;
82 prog.insns_cnt = ARRAY_SIZE(insns);
83 prog.license = "GPL";
84
85 pfd = bpf_load_program_xattr(&prog, NULL, 0);
86 if (pfd < 0) {
87 close(mfd);
88 return -1;
89 }
90
91 ret = bpf_prog_test_run(pfd, 1, data, sizeof(data),
92 NULL, NULL, &retval, NULL);
93 if (ret < 0 || retval != 42) {
94 ret = -1;
95 } else {
96 assert(!bpf_map_lookup_elem(mfd, &zero, value));
97 ret = 0;
98 }
99 close(pfd);
100 close(mfd);
101 return ret;
102}
103
104static int map_subset(int map0, int map1)
105{
106 unsigned long long next_key = 0;
107 unsigned long long value0[nr_cpus], value1[nr_cpus];
108 int ret;
109
110 while (!bpf_map_get_next_key(map1, &next_key, &next_key)) {
111 assert(!bpf_map_lookup_elem(map1, &next_key, value1));
112 ret = bpf_map_lookup_elem(map0, &next_key, value0);
113 if (ret) {
114 printf("key:%llu not found from map. %s(%d)\n",
115 next_key, strerror(errno), errno);
116 return 0;
117 }
118 if (value0[0] != value1[0]) {
119 printf("key:%llu value0:%llu != value1:%llu\n",
120 next_key, value0[0], value1[0]);
121 return 0;
122 }
123 }
124 return 1;
125}
126
127static int map_equal(int lru_map, int expected)
128{
129 return map_subset(lru_map, expected) && map_subset(expected, lru_map);
130}
131
132static int sched_next_online(int pid, int *next_to_try)
133{
134 cpu_set_t cpuset;
135 int next = *next_to_try;
136 int ret = -1;
137
138 while (next < nr_cpus) {
139 CPU_ZERO(&cpuset);
140 CPU_SET(next++, &cpuset);
141 if (!sched_setaffinity(pid, sizeof(cpuset), &cpuset)) {
142 ret = 0;
143 break;
144 }
145 }
146
147 *next_to_try = next;
148 return ret;
149}
150
151/* Size of the LRU map is 2
152 * Add key=1 (+1 key)
153 * Add key=2 (+1 key)
154 * Lookup Key=1
155 * Add Key=3
156 * => Key=2 will be removed by LRU
157 * Iterate map. Only found key=1 and key=3
158 */
159static void test_lru_sanity0(int map_type, int map_flags)
160{
161 unsigned long long key, value[nr_cpus];
162 int lru_map_fd, expected_map_fd;
163 int next_cpu = 0;
164
165 printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
166 map_flags);
167
168 assert(sched_next_online(0, &next_cpu) != -1);
169
170 if (map_flags & BPF_F_NO_COMMON_LRU)
171 lru_map_fd = create_map(map_type, map_flags, 2 * nr_cpus);
172 else
173 lru_map_fd = create_map(map_type, map_flags, 2);
174 assert(lru_map_fd != -1);
175
176 expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, 2);
177 assert(expected_map_fd != -1);
178
179 value[0] = 1234;
180
181 /* insert key=1 element */
182
183 key = 1;
184 assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
185 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
186 BPF_NOEXIST));
187
188 /* BPF_NOEXIST means: add new element if it doesn't exist */
189 assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST) == -1
190 /* key=1 already exists */
191 && errno == EEXIST);
192
193 assert(bpf_map_update_elem(lru_map_fd, &key, value, -1) == -1 &&
194 errno == EINVAL);
195
196 /* insert key=2 element */
197
198 /* check that key=2 is not found */
199 key = 2;
200 assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
201 errno == ENOENT);
202
203 /* BPF_EXIST means: update existing element */
204 assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_EXIST) == -1 &&
205 /* key=2 is not there */
206 errno == ENOENT);
207
208 assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
209
210 /* insert key=3 element */
211
212 /* check that key=3 is not found */
213 key = 3;
214 assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
215 errno == ENOENT);
216
217 /* check that key=1 can be found and mark the ref bit to
218 * stop LRU from removing key=1
219 */
220 key = 1;
221 assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
222 assert(value[0] == 1234);
223
224 key = 3;
225 assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
226 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
227 BPF_NOEXIST));
228
229 /* key=2 has been removed from the LRU */
230 key = 2;
231 assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
232 errno == ENOENT);
233
234 assert(map_equal(lru_map_fd, expected_map_fd));
235
236 close(expected_map_fd);
237 close(lru_map_fd);
238
239 printf("Pass\n");
240}
241
242/* Size of the LRU map is 1.5*tgt_free
243 * Insert 1 to tgt_free (+tgt_free keys)
244 * Lookup 1 to tgt_free/2
245 * Insert 1+tgt_free to 2*tgt_free (+tgt_free keys)
246 * => 1+tgt_free/2 to LOCALFREE_TARGET will be removed by LRU
247 */
248static void test_lru_sanity1(int map_type, int map_flags, unsigned int tgt_free)
249{
250 unsigned long long key, end_key, value[nr_cpus];
251 int lru_map_fd, expected_map_fd;
252 unsigned int batch_size;
253 unsigned int map_size;
254 int next_cpu = 0;
255
256 if (map_flags & BPF_F_NO_COMMON_LRU)
257 /* This test is only applicable to common LRU list */
258 return;
259
260 printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
261 map_flags);
262
263 assert(sched_next_online(0, &next_cpu) != -1);
264
265 batch_size = tgt_free / 2;
266 assert(batch_size * 2 == tgt_free);
267
268 map_size = tgt_free + batch_size;
269 lru_map_fd = create_map(map_type, map_flags, map_size);
270 assert(lru_map_fd != -1);
271
272 expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, map_size);
273 assert(expected_map_fd != -1);
274
275 value[0] = 1234;
276
277 /* Insert 1 to tgt_free (+tgt_free keys) */
278 end_key = 1 + tgt_free;
279 for (key = 1; key < end_key; key++)
280 assert(!bpf_map_update_elem(lru_map_fd, &key, value,
281 BPF_NOEXIST));
282
283 /* Lookup 1 to tgt_free/2 */
284 end_key = 1 + batch_size;
285 for (key = 1; key < end_key; key++) {
286 assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
287 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
288 BPF_NOEXIST));
289 }
290
291 /* Insert 1+tgt_free to 2*tgt_free
292 * => 1+tgt_free/2 to LOCALFREE_TARGET will be
293 * removed by LRU
294 */
295 key = 1 + tgt_free;
296 end_key = key + tgt_free;
297 for (; key < end_key; key++) {
298 assert(!bpf_map_update_elem(lru_map_fd, &key, value,
299 BPF_NOEXIST));
300 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
301 BPF_NOEXIST));
302 }
303
304 assert(map_equal(lru_map_fd, expected_map_fd));
305
306 close(expected_map_fd);
307 close(lru_map_fd);
308
309 printf("Pass\n");
310}
311
312/* Size of the LRU map 1.5 * tgt_free
313 * Insert 1 to tgt_free (+tgt_free keys)
314 * Update 1 to tgt_free/2
315 * => The original 1 to tgt_free/2 will be removed due to
316 * the LRU shrink process
317 * Re-insert 1 to tgt_free/2 again and do a lookup immeidately
318 * Insert 1+tgt_free to tgt_free*3/2
319 * Insert 1+tgt_free*3/2 to tgt_free*5/2
320 * => Key 1+tgt_free to tgt_free*3/2
321 * will be removed from LRU because it has never
322 * been lookup and ref bit is not set
323 */
324static void test_lru_sanity2(int map_type, int map_flags, unsigned int tgt_free)
325{
326 unsigned long long key, value[nr_cpus];
327 unsigned long long end_key;
328 int lru_map_fd, expected_map_fd;
329 unsigned int batch_size;
330 unsigned int map_size;
331 int next_cpu = 0;
332
333 if (map_flags & BPF_F_NO_COMMON_LRU)
334 /* This test is only applicable to common LRU list */
335 return;
336
337 printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
338 map_flags);
339
340 assert(sched_next_online(0, &next_cpu) != -1);
341
342 batch_size = tgt_free / 2;
343 assert(batch_size * 2 == tgt_free);
344
345 map_size = tgt_free + batch_size;
346 lru_map_fd = create_map(map_type, map_flags, map_size);
347 assert(lru_map_fd != -1);
348
349 expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, map_size);
350 assert(expected_map_fd != -1);
351
352 value[0] = 1234;
353
354 /* Insert 1 to tgt_free (+tgt_free keys) */
355 end_key = 1 + tgt_free;
356 for (key = 1; key < end_key; key++)
357 assert(!bpf_map_update_elem(lru_map_fd, &key, value,
358 BPF_NOEXIST));
359
360 /* Any bpf_map_update_elem will require to acquire a new node
361 * from LRU first.
362 *
363 * The local list is running out of free nodes.
364 * It gets from the global LRU list which tries to
365 * shrink the inactive list to get tgt_free
366 * number of free nodes.
367 *
368 * Hence, the oldest key 1 to tgt_free/2
369 * are removed from the LRU list.
370 */
371 key = 1;
372 if (map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH) {
373 assert(!bpf_map_update_elem(lru_map_fd, &key, value,
374 BPF_NOEXIST));
375 assert(!bpf_map_delete_elem(lru_map_fd, &key));
376 } else {
377 assert(bpf_map_update_elem(lru_map_fd, &key, value,
378 BPF_EXIST));
379 }
380
381 /* Re-insert 1 to tgt_free/2 again and do a lookup
382 * immeidately.
383 */
384 end_key = 1 + batch_size;
385 value[0] = 4321;
386 for (key = 1; key < end_key; key++) {
387 assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
388 errno == ENOENT);
389 assert(!bpf_map_update_elem(lru_map_fd, &key, value,
390 BPF_NOEXIST));
391 assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
392 assert(value[0] == 4321);
393 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
394 BPF_NOEXIST));
395 }
396
397 value[0] = 1234;
398
399 /* Insert 1+tgt_free to tgt_free*3/2 */
400 end_key = 1 + tgt_free + batch_size;
401 for (key = 1 + tgt_free; key < end_key; key++)
402 /* These newly added but not referenced keys will be
403 * gone during the next LRU shrink.
404 */
405 assert(!bpf_map_update_elem(lru_map_fd, &key, value,
406 BPF_NOEXIST));
407
408 /* Insert 1+tgt_free*3/2 to tgt_free*5/2 */
409 end_key = key + tgt_free;
410 for (; key < end_key; key++) {
411 assert(!bpf_map_update_elem(lru_map_fd, &key, value,
412 BPF_NOEXIST));
413 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
414 BPF_NOEXIST));
415 }
416
417 assert(map_equal(lru_map_fd, expected_map_fd));
418
419 close(expected_map_fd);
420 close(lru_map_fd);
421
422 printf("Pass\n");
423}
424
425/* Size of the LRU map is 2*tgt_free
426 * It is to test the active/inactive list rotation
427 * Insert 1 to 2*tgt_free (+2*tgt_free keys)
428 * Lookup key 1 to tgt_free*3/2
429 * Add 1+2*tgt_free to tgt_free*5/2 (+tgt_free/2 keys)
430 * => key 1+tgt_free*3/2 to 2*tgt_free are removed from LRU
431 */
432static void test_lru_sanity3(int map_type, int map_flags, unsigned int tgt_free)
433{
434 unsigned long long key, end_key, value[nr_cpus];
435 int lru_map_fd, expected_map_fd;
436 unsigned int batch_size;
437 unsigned int map_size;
438 int next_cpu = 0;
439
440 if (map_flags & BPF_F_NO_COMMON_LRU)
441 /* This test is only applicable to common LRU list */
442 return;
443
444 printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
445 map_flags);
446
447 assert(sched_next_online(0, &next_cpu) != -1);
448
449 batch_size = tgt_free / 2;
450 assert(batch_size * 2 == tgt_free);
451
452 map_size = tgt_free * 2;
453 lru_map_fd = create_map(map_type, map_flags, map_size);
454 assert(lru_map_fd != -1);
455
456 expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, map_size);
457 assert(expected_map_fd != -1);
458
459 value[0] = 1234;
460
461 /* Insert 1 to 2*tgt_free (+2*tgt_free keys) */
462 end_key = 1 + (2 * tgt_free);
463 for (key = 1; key < end_key; key++)
464 assert(!bpf_map_update_elem(lru_map_fd, &key, value,
465 BPF_NOEXIST));
466
467 /* Lookup key 1 to tgt_free*3/2 */
468 end_key = tgt_free + batch_size;
469 for (key = 1; key < end_key; key++) {
470 assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
471 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
472 BPF_NOEXIST));
473 }
474
475 /* Add 1+2*tgt_free to tgt_free*5/2
476 * (+tgt_free/2 keys)
477 */
478 key = 2 * tgt_free + 1;
479 end_key = key + batch_size;
480 for (; key < end_key; key++) {
481 assert(!bpf_map_update_elem(lru_map_fd, &key, value,
482 BPF_NOEXIST));
483 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
484 BPF_NOEXIST));
485 }
486
487 assert(map_equal(lru_map_fd, expected_map_fd));
488
489 close(expected_map_fd);
490 close(lru_map_fd);
491
492 printf("Pass\n");
493}
494
495/* Test deletion */
496static void test_lru_sanity4(int map_type, int map_flags, unsigned int tgt_free)
497{
498 int lru_map_fd, expected_map_fd;
499 unsigned long long key, value[nr_cpus];
500 unsigned long long end_key;
501 int next_cpu = 0;
502
503 printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
504 map_flags);
505
506 assert(sched_next_online(0, &next_cpu) != -1);
507
508 if (map_flags & BPF_F_NO_COMMON_LRU)
509 lru_map_fd = create_map(map_type, map_flags,
510 3 * tgt_free * nr_cpus);
511 else
512 lru_map_fd = create_map(map_type, map_flags, 3 * tgt_free);
513 assert(lru_map_fd != -1);
514
515 expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0,
516 3 * tgt_free);
517 assert(expected_map_fd != -1);
518
519 value[0] = 1234;
520
521 for (key = 1; key <= 2 * tgt_free; key++)
522 assert(!bpf_map_update_elem(lru_map_fd, &key, value,
523 BPF_NOEXIST));
524
525 key = 1;
526 assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
527
528 for (key = 1; key <= tgt_free; key++) {
529 assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
530 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
531 BPF_NOEXIST));
532 }
533
534 for (; key <= 2 * tgt_free; key++) {
535 assert(!bpf_map_delete_elem(lru_map_fd, &key));
536 assert(bpf_map_delete_elem(lru_map_fd, &key));
537 }
538
539 end_key = key + 2 * tgt_free;
540 for (; key < end_key; key++) {
541 assert(!bpf_map_update_elem(lru_map_fd, &key, value,
542 BPF_NOEXIST));
543 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
544 BPF_NOEXIST));
545 }
546
547 assert(map_equal(lru_map_fd, expected_map_fd));
548
549 close(expected_map_fd);
550 close(lru_map_fd);
551
552 printf("Pass\n");
553}
554
555static void do_test_lru_sanity5(unsigned long long last_key, int map_fd)
556{
557 unsigned long long key, value[nr_cpus];
558
559 /* Ensure the last key inserted by previous CPU can be found */
560 assert(!bpf_map_lookup_elem_with_ref_bit(map_fd, last_key, value));
561 value[0] = 1234;
562
563 key = last_key + 1;
564 assert(!bpf_map_update_elem(map_fd, &key, value, BPF_NOEXIST));
565 assert(!bpf_map_lookup_elem_with_ref_bit(map_fd, key, value));
566
567 /* Cannot find the last key because it was removed by LRU */
568 assert(bpf_map_lookup_elem(map_fd, &last_key, value) == -1 &&
569 errno == ENOENT);
570}
571
572/* Test map with only one element */
573static void test_lru_sanity5(int map_type, int map_flags)
574{
575 unsigned long long key, value[nr_cpus];
576 int next_cpu = 0;
577 int map_fd;
578
579 if (map_flags & BPF_F_NO_COMMON_LRU)
580 return;
581
582 printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
583 map_flags);
584
585 map_fd = create_map(map_type, map_flags, 1);
586 assert(map_fd != -1);
587
588 value[0] = 1234;
589 key = 0;
590 assert(!bpf_map_update_elem(map_fd, &key, value, BPF_NOEXIST));
591
592 while (sched_next_online(0, &next_cpu) != -1) {
593 pid_t pid;
594
595 pid = fork();
596 if (pid == 0) {
597 do_test_lru_sanity5(key, map_fd);
598 exit(0);
599 } else if (pid == -1) {
600 printf("couldn't spawn process to test key:%llu\n",
601 key);
602 exit(1);
603 } else {
604 int status;
605
606 assert(waitpid(pid, &status, 0) == pid);
607 assert(status == 0);
608 key++;
609 }
610 }
611
612 close(map_fd);
613 /* At least one key should be tested */
614 assert(key > 0);
615
616 printf("Pass\n");
617}
618
619/* Test list rotation for BPF_F_NO_COMMON_LRU map */
620static void test_lru_sanity6(int map_type, int map_flags, int tgt_free)
621{
622 int lru_map_fd, expected_map_fd;
623 unsigned long long key, value[nr_cpus];
624 unsigned int map_size = tgt_free * 2;
625 int next_cpu = 0;
626
627 if (!(map_flags & BPF_F_NO_COMMON_LRU))
628 return;
629
630 printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
631 map_flags);
632
633 assert(sched_next_online(0, &next_cpu) != -1);
634
635 expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, map_size);
636 assert(expected_map_fd != -1);
637
638 lru_map_fd = create_map(map_type, map_flags, map_size * nr_cpus);
639 assert(lru_map_fd != -1);
640
641 value[0] = 1234;
642
643 for (key = 1; key <= tgt_free; key++) {
644 assert(!bpf_map_update_elem(lru_map_fd, &key, value,
645 BPF_NOEXIST));
646 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
647 BPF_NOEXIST));
648 }
649
650 for (; key <= tgt_free * 2; key++) {
651 unsigned long long stable_key;
652
653 /* Make ref bit sticky for key: [1, tgt_free] */
654 for (stable_key = 1; stable_key <= tgt_free; stable_key++) {
655 /* Mark the ref bit */
656 assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd,
657 stable_key, value));
658 }
659 assert(!bpf_map_update_elem(lru_map_fd, &key, value,
660 BPF_NOEXIST));
661 }
662
663 for (; key <= tgt_free * 3; key++) {
664 assert(!bpf_map_update_elem(lru_map_fd, &key, value,
665 BPF_NOEXIST));
666 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
667 BPF_NOEXIST));
668 }
669
670 assert(map_equal(lru_map_fd, expected_map_fd));
671
672 close(expected_map_fd);
673 close(lru_map_fd);
674
675 printf("Pass\n");
676}
677
678/* Size of the LRU map is 2
679 * Add key=1 (+1 key)
680 * Add key=2 (+1 key)
681 * Lookup Key=1 (datapath)
682 * Lookup Key=2 (syscall)
683 * Add Key=3
684 * => Key=2 will be removed by LRU
685 * Iterate map. Only found key=1 and key=3
686 */
687static void test_lru_sanity7(int map_type, int map_flags)
688{
689 unsigned long long key, value[nr_cpus];
690 int lru_map_fd, expected_map_fd;
691 int next_cpu = 0;
692
693 printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
694 map_flags);
695
696 assert(sched_next_online(0, &next_cpu) != -1);
697
698 if (map_flags & BPF_F_NO_COMMON_LRU)
699 lru_map_fd = create_map(map_type, map_flags, 2 * nr_cpus);
700 else
701 lru_map_fd = create_map(map_type, map_flags, 2);
702 assert(lru_map_fd != -1);
703
704 expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, 2);
705 assert(expected_map_fd != -1);
706
707 value[0] = 1234;
708
709 /* insert key=1 element */
710
711 key = 1;
712 assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
713 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
714 BPF_NOEXIST));
715
716 /* BPF_NOEXIST means: add new element if it doesn't exist */
717 assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST) == -1
718 /* key=1 already exists */
719 && errno == EEXIST);
720
721 /* insert key=2 element */
722
723 /* check that key=2 is not found */
724 key = 2;
725 assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
726 errno == ENOENT);
727
728 /* BPF_EXIST means: update existing element */
729 assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_EXIST) == -1 &&
730 /* key=2 is not there */
731 errno == ENOENT);
732
733 assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
734
735 /* insert key=3 element */
736
737 /* check that key=3 is not found */
738 key = 3;
739 assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
740 errno == ENOENT);
741
742 /* check that key=1 can be found and mark the ref bit to
743 * stop LRU from removing key=1
744 */
745 key = 1;
746 assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
747 assert(value[0] == 1234);
748
749 /* check that key=2 can be found and do _not_ mark ref bit.
750 * this will be evicted on next update.
751 */
752 key = 2;
753 assert(!bpf_map_lookup_elem(lru_map_fd, &key, value));
754 assert(value[0] == 1234);
755
756 key = 3;
757 assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
758 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
759 BPF_NOEXIST));
760
761 /* key=2 has been removed from the LRU */
762 key = 2;
763 assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
764 errno == ENOENT);
765
766 assert(map_equal(lru_map_fd, expected_map_fd));
767
768 close(expected_map_fd);
769 close(lru_map_fd);
770
771 printf("Pass\n");
772}
773
774/* Size of the LRU map is 2
775 * Add key=1 (+1 key)
776 * Add key=2 (+1 key)
777 * Lookup Key=1 (syscall)
778 * Lookup Key=2 (datapath)
779 * Add Key=3
780 * => Key=1 will be removed by LRU
781 * Iterate map. Only found key=2 and key=3
782 */
783static void test_lru_sanity8(int map_type, int map_flags)
784{
785 unsigned long long key, value[nr_cpus];
786 int lru_map_fd, expected_map_fd;
787 int next_cpu = 0;
788
789 printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
790 map_flags);
791
792 assert(sched_next_online(0, &next_cpu) != -1);
793
794 if (map_flags & BPF_F_NO_COMMON_LRU)
795 lru_map_fd = create_map(map_type, map_flags, 2 * nr_cpus);
796 else
797 lru_map_fd = create_map(map_type, map_flags, 2);
798 assert(lru_map_fd != -1);
799
800 expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, 2);
801 assert(expected_map_fd != -1);
802
803 value[0] = 1234;
804
805 /* insert key=1 element */
806
807 key = 1;
808 assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
809
810 /* BPF_NOEXIST means: add new element if it doesn't exist */
811 assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST) == -1
812 /* key=1 already exists */
813 && errno == EEXIST);
814
815 /* insert key=2 element */
816
817 /* check that key=2 is not found */
818 key = 2;
819 assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
820 errno == ENOENT);
821
822 /* BPF_EXIST means: update existing element */
823 assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_EXIST) == -1 &&
824 /* key=2 is not there */
825 errno == ENOENT);
826
827 assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
828 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
829 BPF_NOEXIST));
830
831 /* insert key=3 element */
832
833 /* check that key=3 is not found */
834 key = 3;
835 assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
836 errno == ENOENT);
837
838 /* check that key=1 can be found and do _not_ mark ref bit.
839 * this will be evicted on next update.
840 */
841 key = 1;
842 assert(!bpf_map_lookup_elem(lru_map_fd, &key, value));
843 assert(value[0] == 1234);
844
845 /* check that key=2 can be found and mark the ref bit to
846 * stop LRU from removing key=2
847 */
848 key = 2;
849 assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
850 assert(value[0] == 1234);
851
852 key = 3;
853 assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
854 assert(!bpf_map_update_elem(expected_map_fd, &key, value,
855 BPF_NOEXIST));
856
857 /* key=1 has been removed from the LRU */
858 key = 1;
859 assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
860 errno == ENOENT);
861
862 assert(map_equal(lru_map_fd, expected_map_fd));
863
864 close(expected_map_fd);
865 close(lru_map_fd);
866
867 printf("Pass\n");
868}
869
870int main(int argc, char **argv)
871{
872 int map_types[] = {BPF_MAP_TYPE_LRU_HASH,
873 BPF_MAP_TYPE_LRU_PERCPU_HASH};
874 int map_flags[] = {0, BPF_F_NO_COMMON_LRU};
875 int t, f;
876
877 setbuf(stdout, NULL);
878
879 nr_cpus = bpf_num_possible_cpus();
880 assert(nr_cpus != -1);
881 printf("nr_cpus:%d\n\n", nr_cpus);
882
883 for (f = 0; f < sizeof(map_flags) / sizeof(*map_flags); f++) {
884 unsigned int tgt_free = (map_flags[f] & BPF_F_NO_COMMON_LRU) ?
885 PERCPU_FREE_TARGET : LOCAL_FREE_TARGET;
886
887 for (t = 0; t < sizeof(map_types) / sizeof(*map_types); t++) {
888 test_lru_sanity0(map_types[t], map_flags[f]);
889 test_lru_sanity1(map_types[t], map_flags[f], tgt_free);
890 test_lru_sanity2(map_types[t], map_flags[f], tgt_free);
891 test_lru_sanity3(map_types[t], map_flags[f], tgt_free);
892 test_lru_sanity4(map_types[t], map_flags[f], tgt_free);
893 test_lru_sanity5(map_types[t], map_flags[f]);
894 test_lru_sanity6(map_types[t], map_flags[f], tgt_free);
895 test_lru_sanity7(map_types[t], map_flags[f]);
896 test_lru_sanity8(map_types[t], map_flags[f]);
897
898 printf("\n");
899 }
900 }
901
902 return 0;
903}