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_obj_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.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.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	/* set pinning path of other map and re-pin all */
129	map = bpf_object__find_map_by_name(obj, "nopinmap");
130	if (CHECK(!map, "find map", "NULL map"))
131		goto out;
132
133	err = bpf_map__set_pin_path(map, custpinpath);
134	if (CHECK(err, "set pin path", "err %d errno %d\n", err, errno))
135		goto out;
136
137	/* should only pin the one unpinned map */
138	err = bpf_object__pin_maps(obj, NULL);
139	if (CHECK(err, "pin maps", "err %d errno %d\n", err, errno))
140		goto out;
141
142	/* check that nopinmap was pinned at the custom path */
143	err = stat(custpinpath, &statbuf);
144	if (CHECK(err, "stat custpinpath", "err %d errno %d\n", err, errno))
145		goto out;
146
147	/* remove the custom pin path to re-test it with auto-pinning below */
148	err = unlink(custpinpath);
149	if (CHECK(err, "unlink custpinpath", "err %d errno %d\n", err, errno))
150		goto out;
151
152	err = rmdir(custpath);
153	if (CHECK(err, "rmdir custpindir", "err %d errno %d\n", err, errno))
154		goto out;
155
156	bpf_object__close(obj);
157
158	/* open the valid object file again */
159	obj = bpf_object__open_file(file, NULL);
160	err = libbpf_get_error(obj);
161	if (CHECK(err, "default open", "err %d errno %d\n", err, errno)) {
162		obj = NULL;
163		goto out;
164	}
165
166	/* set pin paths so that nopinmap2 will attempt to reuse the map at
167	 * pinpath (which will fail), but not before pinmap has already been
168	 * reused
169	 */
170	bpf_object__for_each_map(map, obj) {
171		if (!strcmp(bpf_map__name(map), "nopinmap"))
172			err = bpf_map__set_pin_path(map, nopinpath2);
173		else if (!strcmp(bpf_map__name(map), "nopinmap2"))
174			err = bpf_map__set_pin_path(map, pinpath);
175		else
176			continue;
177
178		if (CHECK(err, "set pin path", "err %d errno %d\n", err, errno))
179			goto out;
180	}
181
182	/* should fail because of map parameter mismatch */
183	err = bpf_object__load(obj);
184	if (CHECK(err != -EINVAL, "param mismatch load", "err %d errno %d\n", err, errno))
185		goto out;
186
187	/* nopinmap2 should have been pinned and cleaned up again */
188	err = stat(nopinpath2, &statbuf);
189	if (CHECK(!err || errno != ENOENT, "stat nopinpath2",
190		  "err %d errno %d\n", err, errno))
191		goto out;
192
193	/* pinmap should still be there */
194	err = stat(pinpath, &statbuf);
195	if (CHECK(err, "stat pinpath", "err %d errno %d\n", err, errno))
196		goto out;
197
198	bpf_object__close(obj);
199
200	/* test auto-pinning at custom path with open opt */
201	obj = bpf_object__open_file(file, &opts);
202	if (CHECK_FAIL(libbpf_get_error(obj))) {
203		obj = NULL;
204		goto out;
205	}
206
207	err = bpf_object__load(obj);
208	if (CHECK(err, "custom load", "err %d errno %d\n", err, errno))
209		goto out;
210
211	/* check that pinmap was pinned at the custom path */
212	err = stat(custpinpath, &statbuf);
213	if (CHECK(err, "stat custpinpath", "err %d errno %d\n", err, errno))
214		goto out;
215
216	/* remove the custom pin path to re-test it with reuse fd below */
217	err = unlink(custpinpath);
218	if (CHECK(err, "unlink custpinpath", "err %d errno %d\n", err, errno))
219		goto out;
220
221	err = rmdir(custpath);
222	if (CHECK(err, "rmdir custpindir", "err %d errno %d\n", err, errno))
223		goto out;
224
225	bpf_object__close(obj);
226
227	/* test pinning at custom path with reuse fd */
228	obj = bpf_object__open_file(file, NULL);
229	err = libbpf_get_error(obj);
230	if (CHECK(err, "default open", "err %d errno %d\n", err, errno)) {
231		obj = NULL;
232		goto out;
233	}
234
235	map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(__u32),
236				sizeof(__u64), 1, 0);
237	if (CHECK(map_fd < 0, "create pinmap manually", "fd %d\n", map_fd))
238		goto out;
239
240	map = bpf_object__find_map_by_name(obj, "pinmap");
241	if (CHECK(!map, "find map", "NULL map"))
242		goto close_map_fd;
243
244	err = bpf_map__reuse_fd(map, map_fd);
245	if (CHECK(err, "reuse pinmap fd", "err %d errno %d\n", err, errno))
246		goto close_map_fd;
247
248	err = bpf_map__set_pin_path(map, custpinpath);
249	if (CHECK(err, "set pin path", "err %d errno %d\n", err, errno))
250		goto close_map_fd;
251
252	err = bpf_object__load(obj);
253	if (CHECK(err, "custom load", "err %d errno %d\n", err, errno))
254		goto close_map_fd;
255
256	/* check that pinmap was pinned at the custom path */
257	err = stat(custpinpath, &statbuf);
258	if (CHECK(err, "stat custpinpath", "err %d errno %d\n", err, errno))
259		goto close_map_fd;
260
261close_map_fd:
262	close(map_fd);
263out:
264	unlink(pinpath);
265	unlink(nopinpath);
266	unlink(nopinpath2);
267	unlink(custpinpath);
268	rmdir(custpath);
269	if (obj)
270		bpf_object__close(obj);
271}
272