Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/bpf: Handle batch operations for map-in-map bpf-maps

This patch adds up test cases that handles 4 combinations:
a) outer map: BPF_MAP_TYPE_ARRAY_OF_MAPS
inner maps: BPF_MAP_TYPE_ARRAY and BPF_MAP_TYPE_HASH
b) outer map: BPF_MAP_TYPE_HASH_OF_MAPS
inner maps: BPF_MAP_TYPE_ARRAY and BPF_MAP_TYPE_HASH

Signed-off-by: Takshak Chahande <ctakshak@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Yonghong Song <yhs@fb.com>
Link: https://lore.kernel.org/bpf/20220510082221.2390540-2-ctakshak@fb.com

authored by

Takshak Chahande and committed by
Alexei Starovoitov
a82ebb09 9263dddc

+252
+252
tools/testing/selftests/bpf/map_tests/map_in_map_batch_ops.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 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 <test_maps.h> 12 + 13 + #define OUTER_MAP_ENTRIES 10 14 + 15 + static __u32 get_map_id_from_fd(int map_fd) 16 + { 17 + struct bpf_map_info map_info = {}; 18 + uint32_t info_len = sizeof(map_info); 19 + int ret; 20 + 21 + ret = bpf_obj_get_info_by_fd(map_fd, &map_info, &info_len); 22 + CHECK(ret < 0, "Finding map info failed", "error:%s\n", 23 + strerror(errno)); 24 + 25 + return map_info.id; 26 + } 27 + 28 + /* This creates number of OUTER_MAP_ENTRIES maps that will be stored 29 + * in outer map and return the created map_fds 30 + */ 31 + static void create_inner_maps(enum bpf_map_type map_type, 32 + __u32 *inner_map_fds) 33 + { 34 + int map_fd, map_index, ret; 35 + __u32 map_key = 0, map_id; 36 + char map_name[15]; 37 + 38 + for (map_index = 0; map_index < OUTER_MAP_ENTRIES; map_index++) { 39 + memset(map_name, 0, sizeof(map_name)); 40 + sprintf(map_name, "inner_map_fd_%d", map_index); 41 + map_fd = bpf_map_create(map_type, map_name, sizeof(__u32), 42 + sizeof(__u32), 1, NULL); 43 + CHECK(map_fd < 0, 44 + "inner bpf_map_create() failed", 45 + "map_type=(%d) map_name(%s), error:%s\n", 46 + map_type, map_name, strerror(errno)); 47 + 48 + /* keep track of the inner map fd as it is required 49 + * to add records in outer map 50 + */ 51 + inner_map_fds[map_index] = map_fd; 52 + 53 + /* Add entry into this created map 54 + * eg: map1 key = 0, value = map1's map id 55 + * map2 key = 0, value = map2's map id 56 + */ 57 + map_id = get_map_id_from_fd(map_fd); 58 + ret = bpf_map_update_elem(map_fd, &map_key, &map_id, 0); 59 + CHECK(ret != 0, 60 + "bpf_map_update_elem failed", 61 + "map_type=(%d) map_name(%s), error:%s\n", 62 + map_type, map_name, strerror(errno)); 63 + } 64 + } 65 + 66 + static int create_outer_map(enum bpf_map_type map_type, __u32 inner_map_fd) 67 + { 68 + int outer_map_fd; 69 + LIBBPF_OPTS(bpf_map_create_opts, attr); 70 + 71 + attr.inner_map_fd = inner_map_fd; 72 + outer_map_fd = bpf_map_create(map_type, "outer_map", sizeof(__u32), 73 + sizeof(__u32), OUTER_MAP_ENTRIES, 74 + &attr); 75 + CHECK(outer_map_fd < 0, 76 + "outer bpf_map_create()", 77 + "map_type=(%d), error:%s\n", 78 + map_type, strerror(errno)); 79 + 80 + return outer_map_fd; 81 + } 82 + 83 + static void validate_fetch_results(int outer_map_fd, 84 + __u32 *fetched_keys, __u32 *fetched_values, 85 + __u32 max_entries_fetched) 86 + { 87 + __u32 inner_map_key, inner_map_value; 88 + int inner_map_fd, entry, err; 89 + __u32 outer_map_value; 90 + 91 + for (entry = 0; entry < max_entries_fetched; ++entry) { 92 + outer_map_value = fetched_values[entry]; 93 + inner_map_fd = bpf_map_get_fd_by_id(outer_map_value); 94 + CHECK(inner_map_fd < 0, 95 + "Failed to get inner map fd", 96 + "from id(%d), error=%s\n", 97 + outer_map_value, strerror(errno)); 98 + err = bpf_map_get_next_key(inner_map_fd, NULL, &inner_map_key); 99 + CHECK(err != 0, 100 + "Failed to get inner map key", 101 + "error=%s\n", strerror(errno)); 102 + 103 + err = bpf_map_lookup_elem(inner_map_fd, &inner_map_key, 104 + &inner_map_value); 105 + 106 + close(inner_map_fd); 107 + 108 + CHECK(err != 0, 109 + "Failed to get inner map value", 110 + "for key(%d), error=%s\n", 111 + inner_map_key, strerror(errno)); 112 + 113 + /* Actual value validation */ 114 + CHECK(outer_map_value != inner_map_value, 115 + "Failed to validate inner map value", 116 + "fetched(%d) and lookedup(%d)!\n", 117 + outer_map_value, inner_map_value); 118 + } 119 + } 120 + 121 + static void fetch_and_validate(int outer_map_fd, 122 + struct bpf_map_batch_opts *opts, 123 + __u32 batch_size, bool delete_entries) 124 + { 125 + __u32 *fetched_keys, *fetched_values, total_fetched = 0; 126 + __u32 batch_key = 0, fetch_count, step_size; 127 + int err, max_entries = OUTER_MAP_ENTRIES; 128 + __u32 value_size = sizeof(__u32); 129 + 130 + /* Total entries needs to be fetched */ 131 + fetched_keys = calloc(max_entries, value_size); 132 + fetched_values = calloc(max_entries, value_size); 133 + CHECK((!fetched_keys || !fetched_values), 134 + "Memory allocation failed for fetched_keys or fetched_values", 135 + "error=%s\n", strerror(errno)); 136 + 137 + for (step_size = batch_size; 138 + step_size <= max_entries; 139 + step_size += batch_size) { 140 + fetch_count = step_size; 141 + err = delete_entries 142 + ? bpf_map_lookup_and_delete_batch(outer_map_fd, 143 + total_fetched ? &batch_key : NULL, 144 + &batch_key, 145 + fetched_keys + total_fetched, 146 + fetched_values + total_fetched, 147 + &fetch_count, opts) 148 + : bpf_map_lookup_batch(outer_map_fd, 149 + total_fetched ? &batch_key : NULL, 150 + &batch_key, 151 + fetched_keys + total_fetched, 152 + fetched_values + total_fetched, 153 + &fetch_count, opts); 154 + 155 + if (err && errno == ENOSPC) { 156 + /* Fetch again with higher batch size */ 157 + total_fetched = 0; 158 + continue; 159 + } 160 + 161 + CHECK((err < 0 && (errno != ENOENT)), 162 + "lookup with steps failed", 163 + "error: %s\n", strerror(errno)); 164 + 165 + /* Update the total fetched number */ 166 + total_fetched += fetch_count; 167 + if (err) 168 + break; 169 + } 170 + 171 + CHECK((total_fetched != max_entries), 172 + "Unable to fetch expected entries !", 173 + "total_fetched(%d) and max_entries(%d) error: (%d):%s\n", 174 + total_fetched, max_entries, errno, strerror(errno)); 175 + 176 + /* validate the fetched entries */ 177 + validate_fetch_results(outer_map_fd, fetched_keys, 178 + fetched_values, total_fetched); 179 + printf("batch_op(%s) is successful with batch_size(%d)\n", 180 + delete_entries ? "LOOKUP_AND_DELETE" : "LOOKUP", batch_size); 181 + 182 + free(fetched_keys); 183 + free(fetched_values); 184 + } 185 + 186 + static void _map_in_map_batch_ops(enum bpf_map_type outer_map_type, 187 + enum bpf_map_type inner_map_type) 188 + { 189 + __u32 *outer_map_keys, *inner_map_fds; 190 + __u32 max_entries = OUTER_MAP_ENTRIES; 191 + LIBBPF_OPTS(bpf_map_batch_opts, opts); 192 + __u32 value_size = sizeof(__u32); 193 + int batch_size[2] = {5, 10}; 194 + __u32 map_index, op_index; 195 + int outer_map_fd, ret; 196 + 197 + outer_map_keys = calloc(max_entries, value_size); 198 + inner_map_fds = calloc(max_entries, value_size); 199 + CHECK((!outer_map_keys || !inner_map_fds), 200 + "Memory allocation failed for outer_map_keys or inner_map_fds", 201 + "error=%s\n", strerror(errno)); 202 + 203 + create_inner_maps(inner_map_type, inner_map_fds); 204 + 205 + outer_map_fd = create_outer_map(outer_map_type, *inner_map_fds); 206 + /* create outer map keys */ 207 + for (map_index = 0; map_index < max_entries; map_index++) 208 + outer_map_keys[map_index] = 209 + ((outer_map_type == BPF_MAP_TYPE_ARRAY_OF_MAPS) 210 + ? 9 : 1000) - map_index; 211 + 212 + /* batch operation - map_update */ 213 + ret = bpf_map_update_batch(outer_map_fd, outer_map_keys, 214 + inner_map_fds, &max_entries, &opts); 215 + CHECK(ret != 0, 216 + "Failed to update the outer map batch ops", 217 + "error=%s\n", strerror(errno)); 218 + 219 + /* batch operation - map_lookup */ 220 + for (op_index = 0; op_index < 2; ++op_index) 221 + fetch_and_validate(outer_map_fd, &opts, 222 + batch_size[op_index], false); 223 + 224 + /* batch operation - map_lookup_delete */ 225 + if (outer_map_type == BPF_MAP_TYPE_HASH_OF_MAPS) 226 + fetch_and_validate(outer_map_fd, &opts, 227 + max_entries, true /*delete*/); 228 + 229 + /* close all map fds */ 230 + for (map_index = 0; map_index < max_entries; map_index++) 231 + close(inner_map_fds[map_index]); 232 + close(outer_map_fd); 233 + 234 + free(inner_map_fds); 235 + free(outer_map_keys); 236 + } 237 + 238 + void test_map_in_map_batch_ops_array(void) 239 + { 240 + _map_in_map_batch_ops(BPF_MAP_TYPE_ARRAY_OF_MAPS, BPF_MAP_TYPE_ARRAY); 241 + printf("%s:PASS with inner ARRAY map\n", __func__); 242 + _map_in_map_batch_ops(BPF_MAP_TYPE_ARRAY_OF_MAPS, BPF_MAP_TYPE_HASH); 243 + printf("%s:PASS with inner HASH map\n", __func__); 244 + } 245 + 246 + void test_map_in_map_batch_ops_hash(void) 247 + { 248 + _map_in_map_batch_ops(BPF_MAP_TYPE_HASH_OF_MAPS, BPF_MAP_TYPE_ARRAY); 249 + printf("%s:PASS with inner ARRAY map\n", __func__); 250 + _map_in_map_batch_ops(BPF_MAP_TYPE_HASH_OF_MAPS, BPF_MAP_TYPE_HASH); 251 + printf("%s:PASS with inner HASH map\n", __func__); 252 + }