xref: /linux/tools/testing/selftests/bpf/prog_tests/map_in_map.c (revision 9208c05f9fdfd927ea160b97dfef3c379049fff2)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (C) 2023. Huawei Technologies Co., Ltd */
3 #define _GNU_SOURCE
4 #include <unistd.h>
5 #include <sys/syscall.h>
6 #include <test_progs.h>
7 #include <bpf/btf.h>
8 
9 #include "access_map_in_map.skel.h"
10 #include "update_map_in_htab.skel.h"
11 
12 struct thread_ctx {
13 	pthread_barrier_t barrier;
14 	int outer_map_fd;
15 	int start, abort;
16 	int loop, err;
17 };
18 
19 static int wait_for_start_or_abort(struct thread_ctx *ctx)
20 {
21 	while (!ctx->start && !ctx->abort)
22 		usleep(1);
23 	return ctx->abort ? -1 : 0;
24 }
25 
26 static void *update_map_fn(void *data)
27 {
28 	struct thread_ctx *ctx = data;
29 	int loop = ctx->loop, err = 0;
30 
31 	if (wait_for_start_or_abort(ctx) < 0)
32 		return NULL;
33 	pthread_barrier_wait(&ctx->barrier);
34 
35 	while (loop-- > 0) {
36 		int fd, zero = 0;
37 
38 		fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, NULL, 4, 4, 1, NULL);
39 		if (fd < 0) {
40 			err |= 1;
41 			pthread_barrier_wait(&ctx->barrier);
42 			continue;
43 		}
44 
45 		/* Remove the old inner map */
46 		if (bpf_map_update_elem(ctx->outer_map_fd, &zero, &fd, 0) < 0)
47 			err |= 2;
48 		close(fd);
49 		pthread_barrier_wait(&ctx->barrier);
50 	}
51 
52 	ctx->err = err;
53 
54 	return NULL;
55 }
56 
57 static void *access_map_fn(void *data)
58 {
59 	struct thread_ctx *ctx = data;
60 	int loop = ctx->loop;
61 
62 	if (wait_for_start_or_abort(ctx) < 0)
63 		return NULL;
64 	pthread_barrier_wait(&ctx->barrier);
65 
66 	while (loop-- > 0) {
67 		/* Access the old inner map */
68 		syscall(SYS_getpgid);
69 		pthread_barrier_wait(&ctx->barrier);
70 	}
71 
72 	return NULL;
73 }
74 
75 static void test_map_in_map_access(const char *prog_name, const char *map_name)
76 {
77 	struct access_map_in_map *skel;
78 	struct bpf_map *outer_map;
79 	struct bpf_program *prog;
80 	struct thread_ctx ctx;
81 	pthread_t tid[2];
82 	int err;
83 
84 	skel = access_map_in_map__open();
85 	if (!ASSERT_OK_PTR(skel, "access_map_in_map open"))
86 		return;
87 
88 	prog = bpf_object__find_program_by_name(skel->obj, prog_name);
89 	if (!ASSERT_OK_PTR(prog, "find program"))
90 		goto out;
91 	bpf_program__set_autoload(prog, true);
92 
93 	outer_map = bpf_object__find_map_by_name(skel->obj, map_name);
94 	if (!ASSERT_OK_PTR(outer_map, "find map"))
95 		goto out;
96 
97 	err = access_map_in_map__load(skel);
98 	if (!ASSERT_OK(err, "access_map_in_map load"))
99 		goto out;
100 
101 	err = access_map_in_map__attach(skel);
102 	if (!ASSERT_OK(err, "access_map_in_map attach"))
103 		goto out;
104 
105 	skel->bss->tgid = getpid();
106 
107 	memset(&ctx, 0, sizeof(ctx));
108 	pthread_barrier_init(&ctx.barrier, NULL, 2);
109 	ctx.outer_map_fd = bpf_map__fd(outer_map);
110 	ctx.loop = 4;
111 
112 	err = pthread_create(&tid[0], NULL, update_map_fn, &ctx);
113 	if (!ASSERT_OK(err, "close_thread"))
114 		goto out;
115 
116 	err = pthread_create(&tid[1], NULL, access_map_fn, &ctx);
117 	if (!ASSERT_OK(err, "read_thread")) {
118 		ctx.abort = 1;
119 		pthread_join(tid[0], NULL);
120 		goto out;
121 	}
122 
123 	ctx.start = 1;
124 	pthread_join(tid[0], NULL);
125 	pthread_join(tid[1], NULL);
126 
127 	ASSERT_OK(ctx.err, "err");
128 out:
129 	access_map_in_map__destroy(skel);
130 }
131 
132 static void add_del_fd_htab(int outer_fd)
133 {
134 	int inner_fd, err;
135 	int key = 1;
136 
137 	inner_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "arr1", 4, 4, 1, NULL);
138 	if (!ASSERT_OK_FD(inner_fd, "inner1"))
139 		return;
140 	err = bpf_map_update_elem(outer_fd, &key, &inner_fd, BPF_NOEXIST);
141 	close(inner_fd);
142 	if (!ASSERT_OK(err, "add"))
143 		return;
144 
145 	/* Delete */
146 	err = bpf_map_delete_elem(outer_fd, &key);
147 	ASSERT_OK(err, "del");
148 }
149 
150 static void overwrite_fd_htab(int outer_fd)
151 {
152 	int inner_fd, err;
153 	int key = 1;
154 
155 	inner_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "arr1", 4, 4, 1, NULL);
156 	if (!ASSERT_OK_FD(inner_fd, "inner1"))
157 		return;
158 	err = bpf_map_update_elem(outer_fd, &key, &inner_fd, BPF_NOEXIST);
159 	close(inner_fd);
160 	if (!ASSERT_OK(err, "add"))
161 		return;
162 
163 	/* Overwrite */
164 	inner_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "arr2", 4, 4, 1, NULL);
165 	if (!ASSERT_OK_FD(inner_fd, "inner2"))
166 		goto out;
167 	err = bpf_map_update_elem(outer_fd, &key, &inner_fd, BPF_EXIST);
168 	close(inner_fd);
169 	if (!ASSERT_OK(err, "overwrite"))
170 		goto out;
171 
172 	err = bpf_map_delete_elem(outer_fd, &key);
173 	ASSERT_OK(err, "del");
174 	return;
175 out:
176 	bpf_map_delete_elem(outer_fd, &key);
177 }
178 
179 static void lookup_delete_fd_htab(int outer_fd)
180 {
181 	int key = 1, value;
182 	int inner_fd, err;
183 
184 	inner_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "arr1", 4, 4, 1, NULL);
185 	if (!ASSERT_OK_FD(inner_fd, "inner1"))
186 		return;
187 	err = bpf_map_update_elem(outer_fd, &key, &inner_fd, BPF_NOEXIST);
188 	close(inner_fd);
189 	if (!ASSERT_OK(err, "add"))
190 		return;
191 
192 	/* lookup_and_delete is not supported for htab of maps */
193 	err = bpf_map_lookup_and_delete_elem(outer_fd, &key, &value);
194 	ASSERT_EQ(err, -ENOTSUPP, "lookup_del");
195 
196 	err = bpf_map_delete_elem(outer_fd, &key);
197 	ASSERT_OK(err, "del");
198 }
199 
200 static void batched_lookup_delete_fd_htab(int outer_fd)
201 {
202 	int keys[2] = {1, 2}, values[2];
203 	unsigned int cnt, batch;
204 	int inner_fd, err;
205 
206 	inner_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "arr1", 4, 4, 1, NULL);
207 	if (!ASSERT_OK_FD(inner_fd, "inner1"))
208 		return;
209 
210 	err = bpf_map_update_elem(outer_fd, &keys[0], &inner_fd, BPF_NOEXIST);
211 	close(inner_fd);
212 	if (!ASSERT_OK(err, "add1"))
213 		return;
214 
215 	inner_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "arr2", 4, 4, 1, NULL);
216 	if (!ASSERT_OK_FD(inner_fd, "inner2"))
217 		goto out;
218 	err = bpf_map_update_elem(outer_fd, &keys[1], &inner_fd, BPF_NOEXIST);
219 	close(inner_fd);
220 	if (!ASSERT_OK(err, "add2"))
221 		goto out;
222 
223 	/* batched lookup_and_delete */
224 	cnt = ARRAY_SIZE(keys);
225 	err = bpf_map_lookup_and_delete_batch(outer_fd, NULL, &batch, keys, values, &cnt, NULL);
226 	ASSERT_TRUE((!err || err == -ENOENT), "delete_batch ret");
227 	ASSERT_EQ(cnt, ARRAY_SIZE(keys), "delete_batch cnt");
228 
229 out:
230 	bpf_map_delete_elem(outer_fd, &keys[0]);
231 }
232 
233 static void test_update_map_in_htab(bool preallocate)
234 {
235 	struct update_map_in_htab *skel;
236 	int err, fd;
237 
238 	skel = update_map_in_htab__open();
239 	if (!ASSERT_OK_PTR(skel, "open"))
240 		return;
241 
242 	err = update_map_in_htab__load(skel);
243 	if (!ASSERT_OK(err, "load"))
244 		goto out;
245 
246 	fd = preallocate ? bpf_map__fd(skel->maps.outer_htab_map) :
247 			   bpf_map__fd(skel->maps.outer_alloc_htab_map);
248 
249 	add_del_fd_htab(fd);
250 	overwrite_fd_htab(fd);
251 	lookup_delete_fd_htab(fd);
252 	batched_lookup_delete_fd_htab(fd);
253 out:
254 	update_map_in_htab__destroy(skel);
255 }
256 
257 void test_map_in_map(void)
258 {
259 	if (test__start_subtest("acc_map_in_array"))
260 		test_map_in_map_access("access_map_in_array", "outer_array_map");
261 	if (test__start_subtest("sleepable_acc_map_in_array"))
262 		test_map_in_map_access("sleepable_access_map_in_array", "outer_array_map");
263 	if (test__start_subtest("acc_map_in_htab"))
264 		test_map_in_map_access("access_map_in_htab", "outer_htab_map");
265 	if (test__start_subtest("sleepable_acc_map_in_htab"))
266 		test_map_in_map_access("sleepable_access_map_in_htab", "outer_htab_map");
267 	if (test__start_subtest("update_map_in_htab"))
268 		test_update_map_in_htab(true);
269 	if (test__start_subtest("update_map_in_alloc_htab"))
270 		test_update_map_in_htab(false);
271 }
272