xref: /linux/tools/testing/selftests/bpf/map_tests/map_in_map_batch_ops.c (revision 58f6259b7a08f8d47d4629609703d358b042f0fd)
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_map_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 }
253