.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright (c) 2016 Facebook |
---|
3 | | - * |
---|
4 | | - * This program is free software; you can redistribute it and/or |
---|
5 | | - * modify it under the terms of version 2 of the GNU General Public |
---|
6 | | - * License as published by the Free Software Foundation. |
---|
7 | 4 | */ |
---|
8 | 5 | #define _GNU_SOURCE |
---|
9 | 6 | #include <stdio.h> |
---|
.. | .. |
---|
18 | 15 | #include <sys/wait.h> |
---|
19 | 16 | |
---|
20 | 17 | #include <bpf/bpf.h> |
---|
| 18 | +#include <bpf/libbpf.h> |
---|
21 | 19 | |
---|
22 | 20 | #include "bpf_util.h" |
---|
23 | 21 | #include "bpf_rlimit.h" |
---|
| 22 | +#include "../../../include/linux/filter.h" |
---|
24 | 23 | |
---|
25 | 24 | #define LOCAL_FREE_TARGET (128) |
---|
26 | 25 | #define PERCPU_FREE_TARGET (4) |
---|
.. | .. |
---|
38 | 37 | perror("bpf_create_map"); |
---|
39 | 38 | |
---|
40 | 39 | return map_fd; |
---|
| 40 | +} |
---|
| 41 | + |
---|
| 42 | +static 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; |
---|
41 | 102 | } |
---|
42 | 103 | |
---|
43 | 104 | static int map_subset(int map0, int map1) |
---|
.. | .. |
---|
87 | 148 | return ret; |
---|
88 | 149 | } |
---|
89 | 150 | |
---|
90 | | -/* Size of the LRU amp is 2 |
---|
| 151 | +/* Size of the LRU map is 2 |
---|
91 | 152 | * Add key=1 (+1 key) |
---|
92 | 153 | * Add key=2 (+1 key) |
---|
93 | 154 | * Lookup Key=1 |
---|
.. | .. |
---|
157 | 218 | * stop LRU from removing key=1 |
---|
158 | 219 | */ |
---|
159 | 220 | key = 1; |
---|
160 | | - assert(!bpf_map_lookup_elem(lru_map_fd, &key, value)); |
---|
| 221 | + assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value)); |
---|
161 | 222 | assert(value[0] == 1234); |
---|
162 | 223 | |
---|
163 | 224 | key = 3; |
---|
.. | .. |
---|
167 | 228 | |
---|
168 | 229 | /* key=2 has been removed from the LRU */ |
---|
169 | 230 | key = 2; |
---|
170 | | - assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1); |
---|
| 231 | + assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 && |
---|
| 232 | + errno == ENOENT); |
---|
171 | 233 | |
---|
172 | 234 | assert(map_equal(lru_map_fd, expected_map_fd)); |
---|
173 | 235 | |
---|
.. | .. |
---|
221 | 283 | /* Lookup 1 to tgt_free/2 */ |
---|
222 | 284 | end_key = 1 + batch_size; |
---|
223 | 285 | for (key = 1; key < end_key; key++) { |
---|
224 | | - assert(!bpf_map_lookup_elem(lru_map_fd, &key, value)); |
---|
| 286 | + assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value)); |
---|
225 | 287 | assert(!bpf_map_update_elem(expected_map_fd, &key, value, |
---|
226 | 288 | BPF_NOEXIST)); |
---|
227 | 289 | } |
---|
.. | .. |
---|
322 | 384 | end_key = 1 + batch_size; |
---|
323 | 385 | value[0] = 4321; |
---|
324 | 386 | for (key = 1; key < end_key; key++) { |
---|
325 | | - assert(bpf_map_lookup_elem(lru_map_fd, &key, value)); |
---|
| 387 | + assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 && |
---|
| 388 | + errno == ENOENT); |
---|
326 | 389 | assert(!bpf_map_update_elem(lru_map_fd, &key, value, |
---|
327 | 390 | BPF_NOEXIST)); |
---|
328 | | - assert(!bpf_map_lookup_elem(lru_map_fd, &key, value)); |
---|
| 391 | + assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value)); |
---|
329 | 392 | assert(value[0] == 4321); |
---|
330 | 393 | assert(!bpf_map_update_elem(expected_map_fd, &key, value, |
---|
331 | 394 | BPF_NOEXIST)); |
---|
.. | .. |
---|
404 | 467 | /* Lookup key 1 to tgt_free*3/2 */ |
---|
405 | 468 | end_key = tgt_free + batch_size; |
---|
406 | 469 | for (key = 1; key < end_key; key++) { |
---|
407 | | - assert(!bpf_map_lookup_elem(lru_map_fd, &key, value)); |
---|
| 470 | + assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value)); |
---|
408 | 471 | assert(!bpf_map_update_elem(expected_map_fd, &key, value, |
---|
409 | 472 | BPF_NOEXIST)); |
---|
410 | 473 | } |
---|
.. | .. |
---|
463 | 526 | assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST)); |
---|
464 | 527 | |
---|
465 | 528 | for (key = 1; key <= tgt_free; key++) { |
---|
466 | | - assert(!bpf_map_lookup_elem(lru_map_fd, &key, value)); |
---|
| 529 | + assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value)); |
---|
467 | 530 | assert(!bpf_map_update_elem(expected_map_fd, &key, value, |
---|
468 | 531 | BPF_NOEXIST)); |
---|
469 | 532 | } |
---|
.. | .. |
---|
494 | 557 | unsigned long long key, value[nr_cpus]; |
---|
495 | 558 | |
---|
496 | 559 | /* Ensure the last key inserted by previous CPU can be found */ |
---|
497 | | - assert(!bpf_map_lookup_elem(map_fd, &last_key, value)); |
---|
498 | | - |
---|
| 560 | + assert(!bpf_map_lookup_elem_with_ref_bit(map_fd, last_key, value)); |
---|
499 | 561 | value[0] = 1234; |
---|
500 | 562 | |
---|
501 | 563 | key = last_key + 1; |
---|
502 | 564 | assert(!bpf_map_update_elem(map_fd, &key, value, BPF_NOEXIST)); |
---|
503 | | - assert(!bpf_map_lookup_elem(map_fd, &key, value)); |
---|
| 565 | + assert(!bpf_map_lookup_elem_with_ref_bit(map_fd, key, value)); |
---|
504 | 566 | |
---|
505 | 567 | /* Cannot find the last key because it was removed by LRU */ |
---|
506 | | - assert(bpf_map_lookup_elem(map_fd, &last_key, value)); |
---|
| 568 | + assert(bpf_map_lookup_elem(map_fd, &last_key, value) == -1 && |
---|
| 569 | + errno == ENOENT); |
---|
507 | 570 | } |
---|
508 | 571 | |
---|
509 | 572 | /* Test map with only one element */ |
---|
.. | .. |
---|
590 | 653 | /* Make ref bit sticky for key: [1, tgt_free] */ |
---|
591 | 654 | for (stable_key = 1; stable_key <= tgt_free; stable_key++) { |
---|
592 | 655 | /* Mark the ref bit */ |
---|
593 | | - assert(!bpf_map_lookup_elem(lru_map_fd, &stable_key, |
---|
594 | | - value)); |
---|
| 656 | + assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, |
---|
| 657 | + stable_key, value)); |
---|
595 | 658 | } |
---|
596 | 659 | assert(!bpf_map_update_elem(lru_map_fd, &key, value, |
---|
597 | 660 | BPF_NOEXIST)); |
---|
.. | .. |
---|
603 | 666 | assert(!bpf_map_update_elem(expected_map_fd, &key, value, |
---|
604 | 667 | BPF_NOEXIST)); |
---|
605 | 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 | + */ |
---|
| 687 | +static 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 | + */ |
---|
| 783 | +static 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); |
---|
606 | 861 | |
---|
607 | 862 | assert(map_equal(lru_map_fd, expected_map_fd)); |
---|
608 | 863 | |
---|
.. | .. |
---|
637 | 892 | test_lru_sanity4(map_types[t], map_flags[f], tgt_free); |
---|
638 | 893 | test_lru_sanity5(map_types[t], map_flags[f]); |
---|
639 | 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]); |
---|
640 | 897 | |
---|
641 | 898 | printf("\n"); |
---|
642 | 899 | } |
---|