1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2020 Facebook */
3 
4 #include <test_progs.h>
5 #include <sys/stat.h>
6 
7 #include "test_link_pinning.skel.h"
8 
9 static int duration = 0;
10 
test_link_pinning_subtest(struct bpf_program * prog,struct test_link_pinning__bss * bss)11 void test_link_pinning_subtest(struct bpf_program *prog,
12 			       struct test_link_pinning__bss *bss)
13 {
14 	const char *link_pin_path = "/sys/fs/bpf/pinned_link_test";
15 	struct stat statbuf = {};
16 	struct bpf_link *link;
17 	int err, i;
18 
19 	link = bpf_program__attach(prog);
20 	if (CHECK(IS_ERR(link), "link_attach", "err: %ld\n", PTR_ERR(link)))
21 		goto cleanup;
22 
23 	bss->in = 1;
24 	usleep(1);
25 	CHECK(bss->out != 1, "res_check1", "exp %d, got %d\n", 1, bss->out);
26 
27 	/* pin link */
28 	err = bpf_link__pin(link, link_pin_path);
29 	if (CHECK(err, "link_pin", "err: %d\n", err))
30 		goto cleanup;
31 
32 	CHECK(strcmp(link_pin_path, bpf_link__pin_path(link)), "pin_path1",
33 	      "exp %s, got %s\n", link_pin_path, bpf_link__pin_path(link));
34 
35 	/* check that link was pinned */
36 	err = stat(link_pin_path, &statbuf);
37 	if (CHECK(err, "stat_link", "err %d errno %d\n", err, errno))
38 		goto cleanup;
39 
40 	bss->in = 2;
41 	usleep(1);
42 	CHECK(bss->out != 2, "res_check2", "exp %d, got %d\n", 2, bss->out);
43 
44 	/* destroy link, pinned link should keep program attached */
45 	bpf_link__destroy(link);
46 	link = NULL;
47 
48 	bss->in = 3;
49 	usleep(1);
50 	CHECK(bss->out != 3, "res_check3", "exp %d, got %d\n", 3, bss->out);
51 
52 	/* re-open link from BPFFS */
53 	link = bpf_link__open(link_pin_path);
54 	if (CHECK(IS_ERR(link), "link_open", "err: %ld\n", PTR_ERR(link)))
55 		goto cleanup;
56 
57 	CHECK(strcmp(link_pin_path, bpf_link__pin_path(link)), "pin_path2",
58 	      "exp %s, got %s\n", link_pin_path, bpf_link__pin_path(link));
59 
60 	/* unpin link from BPFFS, program still attached */
61 	err = bpf_link__unpin(link);
62 	if (CHECK(err, "link_unpin", "err: %d\n", err))
63 		goto cleanup;
64 
65 	/* still active, as we have FD open now */
66 	bss->in = 4;
67 	usleep(1);
68 	CHECK(bss->out != 4, "res_check4", "exp %d, got %d\n", 4, bss->out);
69 
70 	bpf_link__destroy(link);
71 	link = NULL;
72 
73 	/* Validate it's finally detached.
74 	 * Actual detachment might get delayed a bit, so there is no reliable
75 	 * way to validate it immediately here, let's count up for long enough
76 	 * and see if eventually output stops being updated
77 	 */
78 	for (i = 5; i < 10000; i++) {
79 		bss->in = i;
80 		usleep(1);
81 		if (bss->out == i - 1)
82 			break;
83 	}
84 	CHECK(i == 10000, "link_attached", "got to iteration #%d\n", i);
85 
86 cleanup:
87 	if (!IS_ERR(link))
88 		bpf_link__destroy(link);
89 }
90 
test_link_pinning(void)91 void test_link_pinning(void)
92 {
93 	struct test_link_pinning* skel;
94 
95 	skel = test_link_pinning__open_and_load();
96 	if (CHECK(!skel, "skel_open", "failed to open skeleton\n"))
97 		return;
98 
99 	if (test__start_subtest("pin_raw_tp"))
100 		test_link_pinning_subtest(skel->progs.raw_tp_prog, skel->bss);
101 	if (test__start_subtest("pin_tp_btf"))
102 		test_link_pinning_subtest(skel->progs.tp_btf_prog, skel->bss);
103 
104 	test_link_pinning__destroy(skel);
105 }
106