1// SPDX-License-Identifier: GPL-2.0
2
3#include <sys/types.h>
4#include <sys/stat.h>
5#include <unistd.h>
6#include <test_progs.h>
7
8__u32 get_map_id(struct bpf_object *obj, const char *name)
9{
10	struct bpf_map_info map_info = {};
11	__u32 map_info_len, duration = 0;
12	struct bpf_map *map;
13	int err;
14
15	map_info_len = sizeof(map_info);
16
17	map = bpf_object__find_map_by_name(obj, name);
18	if (CHECK(!map, "find map", "NULL map"))
19		return 0;
20
21	err = bpf_map_get_info_by_fd(bpf_map__fd(map),
22				     &map_info, &map_info_len);
23	CHECK(err, "get map info", "err %d errno %d", err, errno);
24	return map_info.id;
25}
26
27void test_pinning(void)
28{
29	const char *file_invalid = "./test_pinning_invalid.bpf.o";
30	const char *custpinpath = "/sys/fs/bpf/custom/pinmap";
31	const char *nopinpath = "/sys/fs/bpf/nopinmap";
32	const char *nopinpath2 = "/sys/fs/bpf/nopinmap2";
33	const char *custpath = "/sys/fs/bpf/custom";
34	const char *pinpath = "/sys/fs/bpf/pinmap";
35	const char *file = "./test_pinning.bpf.o";
36	__u32 map_id, map_id2, duration = 0;
37	struct stat statbuf = {};
38	struct bpf_object *obj;
39	struct bpf_map *map;
40	int err, map_fd;
41	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts,
42		.pin_root_path = custpath,
43	);
44
45	/* check that opening fails with invalid pinning value in map def */
46	obj = bpf_object__open_file(file_invalid, NULL);
47	err = libbpf_get_error(obj);
48	if (CHECK(err != -EINVAL, "invalid open", "err %d errno %d\n", err, errno)) {
49		obj = NULL;
50		goto out;
51	}
52
53	/* open the valid object file  */
54	obj = bpf_object__open_file(file, NULL);
55	err = libbpf_get_error(obj);
56	if (CHECK(err, "default open", "err %d errno %d\n", err, errno)) {
57		obj = NULL;
58		goto out;
59	}
60
61	err = bpf_object__load(obj);
62	if (CHECK(err, "default load", "err %d errno %d\n", err, errno))
63		goto out;
64
65	/* check that pinmap was pinned */
66	err = stat(pinpath, &statbuf);
67	if (CHECK(err, "stat pinpath", "err %d errno %d\n", err, errno))
68		goto out;
69
70	/* check that nopinmap was *not* pinned */
71	err = stat(nopinpath, &statbuf);
72	if (CHECK(!err || errno != ENOENT, "stat nopinpath",
73		  "err %d errno %d\n", err, errno))
74		goto out;
75
76	/* check that nopinmap2 was *not* pinned */
77	err = stat(nopinpath2, &statbuf);
78	if (CHECK(!err || errno != ENOENT, "stat nopinpath2",
79		  "err %d errno %d\n", err, errno))
80		goto out;
81
82	map_id = get_map_id(obj, "pinmap");
83	if (!map_id)
84		goto out;
85
86	bpf_object__close(obj);
87
88	obj = bpf_object__open_file(file, NULL);
89	if (CHECK_FAIL(libbpf_get_error(obj))) {
90		obj = NULL;
91		goto out;
92	}
93
94	err = bpf_object__load(obj);
95	if (CHECK(err, "default load", "err %d errno %d\n", err, errno))
96		goto out;
97
98	/* check that same map ID was reused for second load */
99	map_id2 = get_map_id(obj, "pinmap");
100	if (CHECK(map_id != map_id2, "check reuse",
101		  "err %d errno %d id %d id2 %d\n", err, errno, map_id, map_id2))
102		goto out;
103
104	/* should be no-op to re-pin same map */
105	map = bpf_object__find_map_by_name(obj, "pinmap");
106	if (CHECK(!map, "find map", "NULL map"))
107		goto out;
108
109	err = bpf_map__pin(map, NULL);
110	if (CHECK(err, "re-pin map", "err %d errno %d\n", err, errno))
111		goto out;
112
113	/* but error to pin at different location */
114	err = bpf_map__pin(map, "/sys/fs/bpf/other");
115	if (CHECK(!err, "pin map different", "err %d errno %d\n", err, errno))
116		goto out;
117
118	/* unpin maps with a pin_path set */
119	err = bpf_object__unpin_maps(obj, NULL);
120	if (CHECK(err, "unpin maps", "err %d errno %d\n", err, errno))
121		goto out;
122
123	/* and re-pin them... */
124	err = bpf_object__pin_maps(obj, NULL);
125	if (CHECK(err, "pin maps", "err %d errno %d\n", err, errno))
126		goto out;
127
128	/* get pinning path */
129	if (!ASSERT_STREQ(bpf_map__pin_path(map), pinpath, "get pin path"))
130		goto out;
131
132	/* set pinning path of other map and re-pin all */
133	map = bpf_object__find_map_by_name(obj, "nopinmap");
134	if (CHECK(!map, "find map", "NULL map"))
135		goto out;
136
137	err = bpf_map__set_pin_path(map, custpinpath);
138	if (CHECK(err, "set pin path", "err %d errno %d\n", err, errno))
139		goto out;
140
141	/* get pinning path after set */
142	if (!ASSERT_STREQ(bpf_map__pin_path(map), custpinpath,
143			  "get pin path after set"))
144		goto out;
145
146	/* should only pin the one unpinned map */
147	err = bpf_object__pin_maps(obj, NULL);
148	if (CHECK(err, "pin maps", "err %d errno %d\n", err, errno))
149		goto out;
150
151	/* check that nopinmap was pinned at the custom path */
152	err = stat(custpinpath, &statbuf);
153	if (CHECK(err, "stat custpinpath", "err %d errno %d\n", err, errno))
154		goto out;
155
156	/* remove the custom pin path to re-test it with auto-pinning below */
157	err = unlink(custpinpath);
158	if (CHECK(err, "unlink custpinpath", "err %d errno %d\n", err, errno))
159		goto out;
160
161	err = rmdir(custpath);
162	if (CHECK(err, "rmdir custpindir", "err %d errno %d\n", err, errno))
163		goto out;
164
165	bpf_object__close(obj);
166
167	/* open the valid object file again */
168	obj = bpf_object__open_file(file, NULL);
169	err = libbpf_get_error(obj);
170	if (CHECK(err, "default open", "err %d errno %d\n", err, errno)) {
171		obj = NULL;
172		goto out;
173	}
174
175	/* set pin paths so that nopinmap2 will attempt to reuse the map at
176	 * pinpath (which will fail), but not before pinmap has already been
177	 * reused
178	 */
179	bpf_object__for_each_map(map, obj) {
180		if (!strcmp(bpf_map__name(map), "nopinmap"))
181			err = bpf_map__set_pin_path(map, nopinpath2);
182		else if (!strcmp(bpf_map__name(map), "nopinmap2"))
183			err = bpf_map__set_pin_path(map, pinpath);
184		else
185			continue;
186
187		if (CHECK(err, "set pin path", "err %d errno %d\n", err, errno))
188			goto out;
189	}
190
191	/* should fail because of map parameter mismatch */
192	err = bpf_object__load(obj);
193	if (CHECK(err != -EINVAL, "param mismatch load", "err %d errno %d\n", err, errno))
194		goto out;
195
196	/* nopinmap2 should have been pinned and cleaned up again */
197	err = stat(nopinpath2, &statbuf);
198	if (CHECK(!err || errno != ENOENT, "stat nopinpath2",
199		  "err %d errno %d\n", err, errno))
200		goto out;
201
202	/* pinmap should still be there */
203	err = stat(pinpath, &statbuf);
204	if (CHECK(err, "stat pinpath", "err %d errno %d\n", err, errno))
205		goto out;
206
207	bpf_object__close(obj);
208
209	/* test auto-pinning at custom path with open opt */
210	obj = bpf_object__open_file(file, &opts);
211	if (CHECK_FAIL(libbpf_get_error(obj))) {
212		obj = NULL;
213		goto out;
214	}
215
216	err = bpf_object__load(obj);
217	if (CHECK(err, "custom load", "err %d errno %d\n", err, errno))
218		goto out;
219
220	/* check that pinmap was pinned at the custom path */
221	err = stat(custpinpath, &statbuf);
222	if (CHECK(err, "stat custpinpath", "err %d errno %d\n", err, errno))
223		goto out;
224
225	/* remove the custom pin path to re-test it with reuse fd below */
226	err = unlink(custpinpath);
227	if (CHECK(err, "unlink custpinpath", "err %d errno %d\n", err, errno))
228		goto out;
229
230	err = rmdir(custpath);
231	if (CHECK(err, "rmdir custpindir", "err %d errno %d\n", err, errno))
232		goto out;
233
234	bpf_object__close(obj);
235
236	/* test pinning at custom path with reuse fd */
237	obj = bpf_object__open_file(file, NULL);
238	err = libbpf_get_error(obj);
239	if (CHECK(err, "default open", "err %d errno %d\n", err, errno)) {
240		obj = NULL;
241		goto out;
242	}
243
244	map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, NULL, sizeof(__u32),
245				sizeof(__u64), 1, NULL);
246	if (CHECK(map_fd < 0, "create pinmap manually", "fd %d\n", map_fd))
247		goto out;
248
249	map = bpf_object__find_map_by_name(obj, "pinmap");
250	if (CHECK(!map, "find map", "NULL map"))
251		goto close_map_fd;
252
253	err = bpf_map__reuse_fd(map, map_fd);
254	if (CHECK(err, "reuse pinmap fd", "err %d errno %d\n", err, errno))
255		goto close_map_fd;
256
257	err = bpf_map__set_pin_path(map, custpinpath);
258	if (CHECK(err, "set pin path", "err %d errno %d\n", err, errno))
259		goto close_map_fd;
260
261	err = bpf_object__load(obj);
262	if (CHECK(err, "custom load", "err %d errno %d\n", err, errno))
263		goto close_map_fd;
264
265	/* check that pinmap was pinned at the custom path */
266	err = stat(custpinpath, &statbuf);
267	if (CHECK(err, "stat custpinpath", "err %d errno %d\n", err, errno))
268		goto close_map_fd;
269
270close_map_fd:
271	close(map_fd);
272out:
273	unlink(pinpath);
274	unlink(nopinpath);
275	unlink(nopinpath2);
276	unlink(custpinpath);
277	rmdir(custpath);
278	if (obj)
279		bpf_object__close(obj);
280}
281