aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/tools/testing/selftests/bpf/map_tests/map_in_map_batch_ops.c
blob: 66191ae9863c1d4589ecf367cc9bfa4eac54399d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
// SPDX-License-Identifier: GPL-2.0

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>

#include <bpf/bpf.h>
#include <bpf/libbpf.h>

#include <test_maps.h>

#define OUTER_MAP_ENTRIES 10

static __u32 get_map_id_from_fd(int map_fd)
{
	struct bpf_map_info map_info = {};
	uint32_t info_len = sizeof(map_info);
	int ret;

	ret = bpf_map_get_info_by_fd(map_fd, &map_info, &info_len);
	CHECK(ret < 0, "Finding map info failed", "error:%s\n",
	      strerror(errno));

	return map_info.id;
}

/* This creates number of OUTER_MAP_ENTRIES maps that will be stored
 * in outer map and return the created map_fds
 */
static void create_inner_maps(enum bpf_map_type map_type,
			      __u32 *inner_map_fds)
{
	int map_fd, map_index, ret;
	__u32 map_key = 0, map_id;
	char map_name[16];

	for (map_index = 0; map_index < OUTER_MAP_ENTRIES; map_index++) {
		memset(map_name, 0, sizeof(map_name));
		snprintf(map_name, sizeof(map_name), "inner_map_fd_%d", map_index);
		map_fd = bpf_map_create(map_type, map_name, sizeof(__u32),
					sizeof(__u32), 1, NULL);
		CHECK(map_fd < 0,
		      "inner bpf_map_create() failed",
		      "map_type=(%d) map_name(%s), error:%s\n",
		      map_type, map_name, strerror(errno));

		/* keep track of the inner map fd as it is required
		 * to add records in outer map
		 */
		inner_map_fds[map_index] = map_fd;

		/* Add entry into this created map
		 * eg: map1 key = 0, value = map1's map id
		 *     map2 key = 0, value = map2's map id
		 */
		map_id = get_map_id_from_fd(map_fd);
		ret = bpf_map_update_elem(map_fd, &map_key, &map_id, 0);
		CHECK(ret != 0,
		      "bpf_map_update_elem failed",
		      "map_type=(%d) map_name(%s), error:%s\n",
		      map_type, map_name, strerror(errno));
	}
}

static int create_outer_map(enum bpf_map_type map_type, __u32 inner_map_fd)
{
	int outer_map_fd;
	LIBBPF_OPTS(bpf_map_create_opts, attr);

	attr.inner_map_fd = inner_map_fd;
	outer_map_fd = bpf_map_create(map_type, "outer_map", sizeof(__u32),
				      sizeof(__u32), OUTER_MAP_ENTRIES,
				      &attr);
	CHECK(outer_map_fd < 0,
	      "outer bpf_map_create()",
	      "map_type=(%d), error:%s\n",
	      map_type, strerror(errno));

	return outer_map_fd;
}

static void validate_fetch_results(int outer_map_fd,
				   __u32 *fetched_keys, __u32 *fetched_values,
				   __u32 max_entries_fetched)
{
	__u32 inner_map_key, inner_map_value;
	int inner_map_fd, entry, err;
	__u32 outer_map_value;

	for (entry = 0; entry < max_entries_fetched; ++entry) {
		outer_map_value = fetched_values[entry];
		inner_map_fd = bpf_map_get_fd_by_id(outer_map_value);
		CHECK(inner_map_fd < 0,
		      "Failed to get inner map fd",
		      "from id(%d), error=%s\n",
		      outer_map_value, strerror(errno));
		err = bpf_map_get_next_key(inner_map_fd, NULL, &inner_map_key);
		CHECK(err != 0,
		      "Failed to get inner map key",
		      "error=%s\n", strerror(errno));

		err = bpf_map_lookup_elem(inner_map_fd, &inner_map_key,
					  &inner_map_value);

		close(inner_map_fd);

		CHECK(err != 0,
		      "Failed to get inner map value",
		      "for key(%d), error=%s\n",
		      inner_map_key, strerror(errno));

		/* Actual value validation */
		CHECK(outer_map_value != inner_map_value,
		      "Failed to validate inner map value",
		      "fetched(%d) and lookedup(%d)!\n",
		      outer_map_value, inner_map_value);
	}
}

static void fetch_and_validate(int outer_map_fd,
			       struct bpf_map_batch_opts *opts,
			       __u32 batch_size, bool delete_entries)
{
	__u32 *fetched_keys, *fetched_values, total_fetched = 0;
	__u32 batch_key = 0, fetch_count, step_size;
	int err, max_entries = OUTER_MAP_ENTRIES;
	__u32 value_size = sizeof(__u32);

	/* Total entries needs to be fetched */
	fetched_keys = calloc(max_entries, value_size);
	fetched_values = calloc(max_entries, value_size);
	CHECK((!fetched_keys || !fetched_values),
	      "Memory allocation failed for fetched_keys or fetched_values",
	      "error=%s\n", strerror(errno));

	for (step_size = batch_size;
	     step_size <= max_entries;
	     step_size += batch_size) {
		fetch_count = step_size;
		err = delete_entries
		      ? bpf_map_lookup_and_delete_batch(outer_map_fd,
				      total_fetched ? &batch_key : NULL,
				      &batch_key,
				      fetched_keys + total_fetched,
				      fetched_values + total_fetched,
				      &fetch_count, opts)
		      : bpf_map_lookup_batch(outer_map_fd,
				      total_fetched ? &batch_key : NULL,
				      &batch_key,
				      fetched_keys + total_fetched,
				      fetched_values + total_fetched,
				      &fetch_count, opts);

		if (err && errno == ENOSPC) {
			/* Fetch again with higher batch size */
			total_fetched = 0;
			continue;
		}

		CHECK((err < 0 && (errno != ENOENT)),
		      "lookup with steps failed",
		      "error: %s\n", strerror(errno));

		/* Update the total fetched number */
		total_fetched += fetch_count;
		if (err)
			break;
	}

	CHECK((total_fetched != max_entries),
	      "Unable to fetch expected entries !",
	      "total_fetched(%d) and max_entries(%d) error: (%d):%s\n",
	      total_fetched, max_entries, errno, strerror(errno));

	/* validate the fetched entries */
	validate_fetch_results(outer_map_fd, fetched_keys,
			       fetched_values, total_fetched);
	printf("batch_op(%s) is successful with batch_size(%d)\n",
	       delete_entries ? "LOOKUP_AND_DELETE" : "LOOKUP", batch_size);

	free(fetched_keys);
	free(fetched_values);
}

static void _map_in_map_batch_ops(enum bpf_map_type outer_map_type,
				  enum bpf_map_type inner_map_type)
{
	__u32 *outer_map_keys, *inner_map_fds;
	__u32 max_entries = OUTER_MAP_ENTRIES;
	LIBBPF_OPTS(bpf_map_batch_opts, opts);
	__u32 value_size = sizeof(__u32);
	int batch_size[2] = {5, 10};
	__u32 map_index, op_index;
	int outer_map_fd, ret;

	outer_map_keys = calloc(max_entries, value_size);
	inner_map_fds = calloc(max_entries, value_size);
	CHECK((!outer_map_keys || !inner_map_fds),
	      "Memory allocation failed for outer_map_keys or inner_map_fds",
	      "error=%s\n", strerror(errno));

	create_inner_maps(inner_map_type, inner_map_fds);

	outer_map_fd = create_outer_map(outer_map_type, *inner_map_fds);
	/* create outer map keys */
	for (map_index = 0; map_index < max_entries; map_index++)
		outer_map_keys[map_index] =
			((outer_map_type == BPF_MAP_TYPE_ARRAY_OF_MAPS)
			 ? 9 : 1000) - map_index;

	/* batch operation - map_update */
	ret = bpf_map_update_batch(outer_map_fd, outer_map_keys,
				   inner_map_fds, &max_entries, &opts);
	CHECK(ret != 0,
	      "Failed to update the outer map batch ops",
	      "error=%s\n", strerror(errno));

	/* batch operation - map_lookup */
	for (op_index = 0; op_index < 2; ++op_index)
		fetch_and_validate(outer_map_fd, &opts,
				   batch_size[op_index], false);

	/* batch operation - map_lookup_delete */
	if (outer_map_type == BPF_MAP_TYPE_HASH_OF_MAPS)
		fetch_and_validate(outer_map_fd, &opts,
				   max_entries, true /*delete*/);

	/* close all map fds */
	for (map_index = 0; map_index < max_entries; map_index++)
		close(inner_map_fds[map_index]);
	close(outer_map_fd);

	free(inner_map_fds);
	free(outer_map_keys);
}

void test_map_in_map_batch_ops_array(void)
{
	_map_in_map_batch_ops(BPF_MAP_TYPE_ARRAY_OF_MAPS, BPF_MAP_TYPE_ARRAY);
	printf("%s:PASS with inner ARRAY map\n", __func__);
	_map_in_map_batch_ops(BPF_MAP_TYPE_ARRAY_OF_MAPS, BPF_MAP_TYPE_HASH);
	printf("%s:PASS with inner HASH map\n", __func__);
}

void test_map_in_map_batch_ops_hash(void)
{
	_map_in_map_batch_ops(BPF_MAP_TYPE_HASH_OF_MAPS, BPF_MAP_TYPE_ARRAY);
	printf("%s:PASS with inner ARRAY map\n", __func__);
	_map_in_map_batch_ops(BPF_MAP_TYPE_HASH_OF_MAPS, BPF_MAP_TYPE_HASH);
	printf("%s:PASS with inner HASH map\n", __func__);
}