map_in_map_batch_ops.c (7705B)
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 15static __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 */ 31static 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 66static 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 83static 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 121static 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 186static 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 238void 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 246void 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}