1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2019 Facebook */ 3 #include <stdio.h> 4 #include <errno.h> 5 #include <string.h> 6 #include <unistd.h> 7 8 #include <bpf/bpf.h> 9 #include <bpf/libbpf.h> 10 11 #include <bpf_util.h> 12 #include <test_maps.h> 13 14 static void map_batch_update(int map_fd, __u32 max_entries, int *keys, 15 void *values, bool is_pcpu) 16 { 17 typedef BPF_DECLARE_PERCPU(int, value); 18 value *v = NULL; 19 int i, j, err; 20 DECLARE_LIBBPF_OPTS(bpf_map_batch_opts, opts, 21 .elem_flags = 0, 22 .flags = 0, 23 ); 24 25 if (is_pcpu) 26 v = (value *)values; 27 28 for (i = 0; i < max_entries; i++) { 29 keys[i] = i + 1; 30 if (is_pcpu) 31 for (j = 0; j < bpf_num_possible_cpus(); j++) 32 bpf_percpu(v[i], j) = i + 2 + j; 33 else 34 ((int *)values)[i] = i + 2; 35 } 36 37 err = bpf_map_update_batch(map_fd, keys, values, &max_entries, &opts); 38 CHECK(err, "bpf_map_update_batch()", "error:%s\n", strerror(errno)); 39 } 40 41 static void map_batch_verify(int *visited, __u32 max_entries, 42 int *keys, void *values, bool is_pcpu) 43 { 44 typedef BPF_DECLARE_PERCPU(int, value); 45 value *v = NULL; 46 int i, j; 47 48 if (is_pcpu) 49 v = (value *)values; 50 51 memset(visited, 0, max_entries * sizeof(*visited)); 52 for (i = 0; i < max_entries; i++) { 53 54 if (is_pcpu) { 55 for (j = 0; j < bpf_num_possible_cpus(); j++) { 56 CHECK(keys[i] + 1 + j != bpf_percpu(v[i], j), 57 "key/value checking", 58 "error: i %d j %d key %d value %d\n", 59 i, j, keys[i], bpf_percpu(v[i], j)); 60 } 61 } else { 62 CHECK(keys[i] + 1 != ((int *)values)[i], 63 "key/value checking", 64 "error: i %d key %d value %d\n", i, keys[i], 65 ((int *)values)[i]); 66 } 67 68 visited[i] = 1; 69 70 } 71 for (i = 0; i < max_entries; i++) { 72 CHECK(visited[i] != 1, "visited checking", 73 "error: keys array at index %d missing\n", i); 74 } 75 } 76 77 void __test_map_lookup_and_delete_batch(bool is_pcpu) 78 { 79 __u32 batch, count, total, total_success; 80 typedef BPF_DECLARE_PERCPU(int, value); 81 int map_fd, *keys, *visited, key; 82 const __u32 max_entries = 10; 83 value pcpu_values[max_entries]; 84 int err, step, value_size; 85 bool nospace_err; 86 void *values; 87 DECLARE_LIBBPF_OPTS(bpf_map_batch_opts, opts, 88 .elem_flags = 0, 89 .flags = 0, 90 ); 91 92 map_fd = bpf_map_create(is_pcpu ? BPF_MAP_TYPE_PERCPU_HASH : BPF_MAP_TYPE_HASH, 93 "hash_map", sizeof(int), sizeof(int), max_entries, NULL); 94 CHECK(map_fd == -1, 95 "bpf_map_create()", "error:%s\n", strerror(errno)); 96 97 value_size = is_pcpu ? sizeof(value) : sizeof(int); 98 keys = malloc(max_entries * sizeof(int)); 99 if (is_pcpu) 100 values = pcpu_values; 101 else 102 values = malloc(max_entries * sizeof(int)); 103 visited = malloc(max_entries * sizeof(int)); 104 CHECK(!keys || !values || !visited, "malloc()", 105 "error:%s\n", strerror(errno)); 106 107 /* test 1: lookup/delete an empty hash table, -ENOENT */ 108 count = max_entries; 109 err = bpf_map_lookup_and_delete_batch(map_fd, NULL, &batch, keys, 110 values, &count, &opts); 111 CHECK((err && errno != ENOENT), "empty map", 112 "error: %s\n", strerror(errno)); 113 114 /* populate elements to the map */ 115 map_batch_update(map_fd, max_entries, keys, values, is_pcpu); 116 117 /* test 2: lookup/delete with count = 0, success */ 118 count = 0; 119 err = bpf_map_lookup_and_delete_batch(map_fd, NULL, &batch, keys, 120 values, &count, &opts); 121 CHECK(err, "count = 0", "error: %s\n", strerror(errno)); 122 123 /* test 3: lookup/delete with count = max_entries, success */ 124 memset(keys, 0, max_entries * sizeof(*keys)); 125 memset(values, 0, max_entries * value_size); 126 count = max_entries; 127 err = bpf_map_lookup_and_delete_batch(map_fd, NULL, &batch, keys, 128 values, &count, &opts); 129 CHECK((err && errno != ENOENT), "count = max_entries", 130 "error: %s\n", strerror(errno)); 131 CHECK(count != max_entries, "count = max_entries", 132 "count = %u, max_entries = %u\n", count, max_entries); 133 map_batch_verify(visited, max_entries, keys, values, is_pcpu); 134 135 /* bpf_map_get_next_key() should return -ENOENT for an empty map. */ 136 err = bpf_map_get_next_key(map_fd, NULL, &key); 137 CHECK(!err, "bpf_map_get_next_key()", "error: %s\n", strerror(errno)); 138 139 /* test 4: lookup/delete in a loop with various steps. */ 140 total_success = 0; 141 for (step = 1; step < max_entries; step++) { 142 map_batch_update(map_fd, max_entries, keys, values, is_pcpu); 143 memset(keys, 0, max_entries * sizeof(*keys)); 144 memset(values, 0, max_entries * value_size); 145 total = 0; 146 /* iteratively lookup/delete elements with 'step' 147 * elements each 148 */ 149 count = step; 150 nospace_err = false; 151 while (true) { 152 err = bpf_map_lookup_batch(map_fd, 153 total ? &batch : NULL, 154 &batch, keys + total, 155 values + 156 total * value_size, 157 &count, &opts); 158 /* It is possible that we are failing due to buffer size 159 * not big enough. In such cases, let us just exit and 160 * go with large steps. Not that a buffer size with 161 * max_entries should always work. 162 */ 163 if (err && errno == ENOSPC) { 164 nospace_err = true; 165 break; 166 } 167 168 CHECK((err && errno != ENOENT), "lookup with steps", 169 "error: %s\n", strerror(errno)); 170 171 total += count; 172 if (err) 173 break; 174 175 } 176 if (nospace_err == true) 177 continue; 178 179 CHECK(total != max_entries, "lookup with steps", 180 "total = %u, max_entries = %u\n", total, max_entries); 181 map_batch_verify(visited, max_entries, keys, values, is_pcpu); 182 183 total = 0; 184 count = step; 185 while (total < max_entries) { 186 if (max_entries - total < step) 187 count = max_entries - total; 188 err = bpf_map_delete_batch(map_fd, 189 keys + total, 190 &count, &opts); 191 CHECK((err && errno != ENOENT), "delete batch", 192 "error: %s\n", strerror(errno)); 193 total += count; 194 if (err) 195 break; 196 } 197 CHECK(total != max_entries, "delete with steps", 198 "total = %u, max_entries = %u\n", total, max_entries); 199 200 /* check map is empty, errono == ENOENT */ 201 err = bpf_map_get_next_key(map_fd, NULL, &key); 202 CHECK(!err || errno != ENOENT, "bpf_map_get_next_key()", 203 "error: %s\n", strerror(errno)); 204 205 /* iteratively lookup/delete elements with 'step' 206 * elements each 207 */ 208 map_batch_update(map_fd, max_entries, keys, values, is_pcpu); 209 memset(keys, 0, max_entries * sizeof(*keys)); 210 memset(values, 0, max_entries * value_size); 211 total = 0; 212 count = step; 213 nospace_err = false; 214 while (true) { 215 err = bpf_map_lookup_and_delete_batch(map_fd, 216 total ? &batch : NULL, 217 &batch, keys + total, 218 values + 219 total * value_size, 220 &count, &opts); 221 /* It is possible that we are failing due to buffer size 222 * not big enough. In such cases, let us just exit and 223 * go with large steps. Not that a buffer size with 224 * max_entries should always work. 225 */ 226 if (err && errno == ENOSPC) { 227 nospace_err = true; 228 break; 229 } 230 231 CHECK((err && errno != ENOENT), "lookup with steps", 232 "error: %s\n", strerror(errno)); 233 234 total += count; 235 if (err) 236 break; 237 } 238 239 if (nospace_err == true) 240 continue; 241 242 CHECK(total != max_entries, "lookup/delete with steps", 243 "total = %u, max_entries = %u\n", total, max_entries); 244 245 map_batch_verify(visited, max_entries, keys, values, is_pcpu); 246 err = bpf_map_get_next_key(map_fd, NULL, &key); 247 CHECK(!err, "bpf_map_get_next_key()", "error: %s\n", 248 strerror(errno)); 249 250 total_success++; 251 } 252 253 CHECK(total_success == 0, "check total_success", 254 "unexpected failure\n"); 255 free(keys); 256 free(visited); 257 if (!is_pcpu) 258 free(values); 259 close(map_fd); 260 } 261 262 void htab_map_batch_ops(void) 263 { 264 __test_map_lookup_and_delete_batch(false); 265 printf("test_%s:PASS\n", __func__); 266 } 267 268 void htab_percpu_map_batch_ops(void) 269 { 270 __test_map_lookup_and_delete_batch(true); 271 printf("test_%s:PASS\n", __func__); 272 } 273 274 void test_htab_map_batch_ops(void) 275 { 276 htab_map_batch_ops(); 277 htab_percpu_map_batch_ops(); 278 } 279
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.