Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v5.11 197 lines 6.3 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/* Copyright (c) 2020 Facebook */ 3 4#include <test_progs.h> 5 6#include "test_btf_map_in_map.skel.h" 7 8static int duration; 9 10static __u32 bpf_map_id(struct bpf_map *map) 11{ 12 struct bpf_map_info info; 13 __u32 info_len = sizeof(info); 14 int err; 15 16 memset(&info, 0, info_len); 17 err = bpf_obj_get_info_by_fd(bpf_map__fd(map), &info, &info_len); 18 if (err) 19 return 0; 20 return info.id; 21} 22 23/* 24 * Trigger synchronize_rcu() in kernel. 25 * 26 * ARRAY_OF_MAPS/HASH_OF_MAPS lookup/update operations trigger synchronize_rcu() 27 * if looking up an existing non-NULL element or updating the map with a valid 28 * inner map FD. Use this fact to trigger synchronize_rcu(): create map-in-map, 29 * create a trivial ARRAY map, update map-in-map with ARRAY inner map. Then 30 * cleanup. At the end, at least one synchronize_rcu() would be called. 31 */ 32static int kern_sync_rcu(void) 33{ 34 int inner_map_fd, outer_map_fd, err, zero = 0; 35 36 inner_map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, 4, 4, 1, 0); 37 if (CHECK(inner_map_fd < 0, "inner_map_create", "failed %d\n", -errno)) 38 return -1; 39 40 outer_map_fd = bpf_create_map_in_map(BPF_MAP_TYPE_ARRAY_OF_MAPS, NULL, 41 sizeof(int), inner_map_fd, 1, 0); 42 if (CHECK(outer_map_fd < 0, "outer_map_create", "failed %d\n", -errno)) { 43 close(inner_map_fd); 44 return -1; 45 } 46 47 err = bpf_map_update_elem(outer_map_fd, &zero, &inner_map_fd, 0); 48 if (err) 49 err = -errno; 50 CHECK(err, "outer_map_update", "failed %d\n", err); 51 close(inner_map_fd); 52 close(outer_map_fd); 53 return err; 54} 55 56static void test_lookup_update(void) 57{ 58 int map1_fd, map2_fd, map3_fd, map4_fd, map5_fd, map1_id, map2_id; 59 int outer_arr_fd, outer_hash_fd, outer_arr_dyn_fd; 60 struct test_btf_map_in_map *skel; 61 int err, key = 0, val, i, fd; 62 63 skel = test_btf_map_in_map__open_and_load(); 64 if (CHECK(!skel, "skel_open", "failed to open&load skeleton\n")) 65 return; 66 67 err = test_btf_map_in_map__attach(skel); 68 if (CHECK(err, "skel_attach", "skeleton attach failed: %d\n", err)) 69 goto cleanup; 70 71 map1_fd = bpf_map__fd(skel->maps.inner_map1); 72 map2_fd = bpf_map__fd(skel->maps.inner_map2); 73 map3_fd = bpf_map__fd(skel->maps.inner_map3); 74 map4_fd = bpf_map__fd(skel->maps.inner_map4); 75 map5_fd = bpf_map__fd(skel->maps.inner_map5); 76 outer_arr_dyn_fd = bpf_map__fd(skel->maps.outer_arr_dyn); 77 outer_arr_fd = bpf_map__fd(skel->maps.outer_arr); 78 outer_hash_fd = bpf_map__fd(skel->maps.outer_hash); 79 80 /* inner1 = input, inner2 = input + 1, inner3 = input + 2 */ 81 bpf_map_update_elem(outer_arr_fd, &key, &map1_fd, 0); 82 bpf_map_update_elem(outer_hash_fd, &key, &map2_fd, 0); 83 bpf_map_update_elem(outer_arr_dyn_fd, &key, &map3_fd, 0); 84 skel->bss->input = 1; 85 usleep(1); 86 bpf_map_lookup_elem(map1_fd, &key, &val); 87 CHECK(val != 1, "inner1", "got %d != exp %d\n", val, 1); 88 bpf_map_lookup_elem(map2_fd, &key, &val); 89 CHECK(val != 2, "inner2", "got %d != exp %d\n", val, 2); 90 bpf_map_lookup_elem(map3_fd, &key, &val); 91 CHECK(val != 3, "inner3", "got %d != exp %d\n", val, 3); 92 93 /* inner2 = input, inner1 = input + 1, inner4 = input + 2 */ 94 bpf_map_update_elem(outer_arr_fd, &key, &map2_fd, 0); 95 bpf_map_update_elem(outer_hash_fd, &key, &map1_fd, 0); 96 bpf_map_update_elem(outer_arr_dyn_fd, &key, &map4_fd, 0); 97 skel->bss->input = 3; 98 usleep(1); 99 bpf_map_lookup_elem(map1_fd, &key, &val); 100 CHECK(val != 4, "inner1", "got %d != exp %d\n", val, 4); 101 bpf_map_lookup_elem(map2_fd, &key, &val); 102 CHECK(val != 3, "inner2", "got %d != exp %d\n", val, 3); 103 bpf_map_lookup_elem(map4_fd, &key, &val); 104 CHECK(val != 5, "inner4", "got %d != exp %d\n", val, 5); 105 106 /* inner5 = input + 2 */ 107 bpf_map_update_elem(outer_arr_dyn_fd, &key, &map5_fd, 0); 108 skel->bss->input = 5; 109 usleep(1); 110 bpf_map_lookup_elem(map5_fd, &key, &val); 111 CHECK(val != 7, "inner5", "got %d != exp %d\n", val, 7); 112 113 for (i = 0; i < 5; i++) { 114 val = i % 2 ? map1_fd : map2_fd; 115 err = bpf_map_update_elem(outer_hash_fd, &key, &val, 0); 116 if (CHECK_FAIL(err)) { 117 printf("failed to update hash_of_maps on iter #%d\n", i); 118 goto cleanup; 119 } 120 err = bpf_map_update_elem(outer_arr_fd, &key, &val, 0); 121 if (CHECK_FAIL(err)) { 122 printf("failed to update array_of_maps on iter #%d\n", i); 123 goto cleanup; 124 } 125 val = i % 2 ? map4_fd : map5_fd; 126 err = bpf_map_update_elem(outer_arr_dyn_fd, &key, &val, 0); 127 if (CHECK_FAIL(err)) { 128 printf("failed to update array_of_maps (dyn) on iter #%d\n", i); 129 goto cleanup; 130 } 131 } 132 133 map1_id = bpf_map_id(skel->maps.inner_map1); 134 map2_id = bpf_map_id(skel->maps.inner_map2); 135 CHECK(map1_id == 0, "map1_id", "failed to get ID 1\n"); 136 CHECK(map2_id == 0, "map2_id", "failed to get ID 2\n"); 137 138 test_btf_map_in_map__destroy(skel); 139 skel = NULL; 140 141 /* we need to either wait for or force synchronize_rcu(), before 142 * checking for "still exists" condition, otherwise map could still be 143 * resolvable by ID, causing false positives. 144 * 145 * Older kernels (5.8 and earlier) freed map only after two 146 * synchronize_rcu()s, so trigger two, to be entirely sure. 147 */ 148 CHECK(kern_sync_rcu(), "sync_rcu", "failed\n"); 149 CHECK(kern_sync_rcu(), "sync_rcu", "failed\n"); 150 151 fd = bpf_map_get_fd_by_id(map1_id); 152 if (CHECK(fd >= 0, "map1_leak", "inner_map1 leaked!\n")) { 153 close(fd); 154 goto cleanup; 155 } 156 fd = bpf_map_get_fd_by_id(map2_id); 157 if (CHECK(fd >= 0, "map2_leak", "inner_map2 leaked!\n")) { 158 close(fd); 159 goto cleanup; 160 } 161 162cleanup: 163 test_btf_map_in_map__destroy(skel); 164} 165 166static void test_diff_size(void) 167{ 168 struct test_btf_map_in_map *skel; 169 int err, inner_map_fd, zero = 0; 170 171 skel = test_btf_map_in_map__open_and_load(); 172 if (CHECK(!skel, "skel_open", "failed to open&load skeleton\n")) 173 return; 174 175 inner_map_fd = bpf_map__fd(skel->maps.sockarr_sz2); 176 err = bpf_map_update_elem(bpf_map__fd(skel->maps.outer_sockarr), &zero, 177 &inner_map_fd, 0); 178 CHECK(err, "outer_sockarr inner map size check", 179 "cannot use a different size inner_map\n"); 180 181 inner_map_fd = bpf_map__fd(skel->maps.inner_map_sz2); 182 err = bpf_map_update_elem(bpf_map__fd(skel->maps.outer_arr), &zero, 183 &inner_map_fd, 0); 184 CHECK(!err, "outer_arr inner map size check", 185 "incorrectly updated with a different size inner_map\n"); 186 187 test_btf_map_in_map__destroy(skel); 188} 189 190void test_btf_map_in_map(void) 191{ 192 if (test__start_subtest("lookup_update")) 193 test_lookup_update(); 194 195 if (test__start_subtest("diff_size")) 196 test_diff_size(); 197}