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
map_batch_update(int map_fd,__u32 max_entries,int * keys,void * values,bool is_pcpu)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
map_batch_verify(int * visited,__u32 max_entries,int * keys,void * values,bool is_pcpu)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
__test_map_lookup_and_delete_batch(bool is_pcpu)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, errno == 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
htab_map_batch_ops(void)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
htab_percpu_map_batch_ops(void)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
test_htab_map_batch_ops(void)274 void test_htab_map_batch_ops(void)
275 {
276 htab_map_batch_ops();
277 htab_percpu_map_batch_ops();
278 }
279