1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2019 Facebook */
9 #include <bpf/libbpf.h>
12 #include <test_maps.h>
14 static void map_batch_update(int map_fd, __u32 max_entries, int *keys,
15 void *values, bool is_pcpu)
17 typedef BPF_DECLARE_PERCPU(int, value);
20 DECLARE_LIBBPF_OPTS(bpf_map_batch_opts, opts,
28 for (i = 0; i < max_entries; i++) {
31 for (j = 0; j < bpf_num_possible_cpus(); j++)
32 bpf_percpu(v[i], j) = i + 2 + j;
34 ((int *)values)[i] = i + 2;
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));
41 static void map_batch_verify(int *visited, __u32 max_entries,
42 int *keys, void *values, bool is_pcpu)
44 typedef BPF_DECLARE_PERCPU(int, value);
51 memset(visited, 0, max_entries * sizeof(*visited));
52 for (i = 0; i < max_entries; i++) {
55 for (j = 0; j < bpf_num_possible_cpus(); j++) {
56 CHECK(keys[i] + 1 + j != bpf_percpu(v[i], j),
58 "error: i %d j %d key %d value %d\n",
59 i, j, keys[i], bpf_percpu(v[i], j));
62 CHECK(keys[i] + 1 != ((int *)values)[i],
64 "error: i %d key %d value %d\n", i, keys[i],
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);
77 void __test_map_lookup_and_delete_batch(bool is_pcpu)
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;
87 DECLARE_LIBBPF_OPTS(bpf_map_batch_opts, opts,
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);
95 "bpf_map_create()", "error:%s\n", strerror(errno));
97 value_size = is_pcpu ? sizeof(value) : sizeof(int);
98 keys = malloc(max_entries * sizeof(int));
100 values = pcpu_values;
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));
107 /* test 1: lookup/delete an empty hash table, -ENOENT */
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));
114 /* populate elements to the map */
115 map_batch_update(map_fd, max_entries, keys, values, is_pcpu);
117 /* test 2: lookup/delete with count = 0, success */
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));
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);
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);
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));
139 /* test 4: lookup/delete in a loop with various steps. */
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);
146 /* iteratively lookup/delete elements with 'step'
152 err = bpf_map_lookup_batch(map_fd,
153 total ? &batch : NULL,
154 &batch, keys + total,
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.
163 if (err && errno == ENOSPC) {
168 CHECK((err && errno != ENOENT), "lookup with steps",
169 "error: %s\n", strerror(errno));
176 if (nospace_err == true)
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);
185 while (total < max_entries) {
186 if (max_entries - total < step)
187 count = max_entries - total;
188 err = bpf_map_delete_batch(map_fd,
191 CHECK((err && errno != ENOENT), "delete batch",
192 "error: %s\n", strerror(errno));
197 CHECK(total != max_entries, "delete with steps",
198 "total = %u, max_entries = %u\n", total, max_entries);
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));
205 /* iteratively lookup/delete elements with 'step'
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);
215 err = bpf_map_lookup_and_delete_batch(map_fd,
216 total ? &batch : NULL,
217 &batch, keys + total,
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.
226 if (err && errno == ENOSPC) {
231 CHECK((err && errno != ENOENT), "lookup with steps",
232 "error: %s\n", strerror(errno));
239 if (nospace_err == true)
242 CHECK(total != max_entries, "lookup/delete with steps",
243 "total = %u, max_entries = %u\n", total, max_entries);
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",
253 CHECK(total_success == 0, "check total_success",
254 "unexpected failure\n");
262 void htab_map_batch_ops(void)
264 __test_map_lookup_and_delete_batch(false);
265 printf("test_%s:PASS\n", __func__);
268 void htab_percpu_map_batch_ops(void)
270 __test_map_lookup_and_delete_batch(true);
271 printf("test_%s:PASS\n", __func__);
274 void test_htab_map_batch_ops(void)
276 htab_map_batch_ops();
277 htab_percpu_map_batch_ops();