| // SPDX-License-Identifier: GPL-2.0 |
| /* Copyright (c) 2020 Facebook */ |
| |
| #include <test_progs.h> |
| #include <sys/stat.h> |
| |
| #include "test_link_pinning.skel.h" |
| |
| static int duration = 0; |
| |
| void test_link_pinning_subtest(struct bpf_program *prog, |
| struct test_link_pinning__bss *bss) |
| { |
| const char *link_pin_path = "/sys/fs/bpf/pinned_link_test"; |
| struct stat statbuf = {}; |
| struct bpf_link *link; |
| int err, i; |
| |
| link = bpf_program__attach(prog); |
| if (CHECK(IS_ERR(link), "link_attach", "err: %ld\n", PTR_ERR(link))) |
| goto cleanup; |
| |
| bss->in = 1; |
| usleep(1); |
| CHECK(bss->out != 1, "res_check1", "exp %d, got %d\n", 1, bss->out); |
| |
| /* pin link */ |
| err = bpf_link__pin(link, link_pin_path); |
| if (CHECK(err, "link_pin", "err: %d\n", err)) |
| goto cleanup; |
| |
| CHECK(strcmp(link_pin_path, bpf_link__pin_path(link)), "pin_path1", |
| "exp %s, got %s\n", link_pin_path, bpf_link__pin_path(link)); |
| |
| /* check that link was pinned */ |
| err = stat(link_pin_path, &statbuf); |
| if (CHECK(err, "stat_link", "err %d errno %d\n", err, errno)) |
| goto cleanup; |
| |
| bss->in = 2; |
| usleep(1); |
| CHECK(bss->out != 2, "res_check2", "exp %d, got %d\n", 2, bss->out); |
| |
| /* destroy link, pinned link should keep program attached */ |
| bpf_link__destroy(link); |
| link = NULL; |
| |
| bss->in = 3; |
| usleep(1); |
| CHECK(bss->out != 3, "res_check3", "exp %d, got %d\n", 3, bss->out); |
| |
| /* re-open link from BPFFS */ |
| link = bpf_link__open(link_pin_path); |
| if (CHECK(IS_ERR(link), "link_open", "err: %ld\n", PTR_ERR(link))) |
| goto cleanup; |
| |
| CHECK(strcmp(link_pin_path, bpf_link__pin_path(link)), "pin_path2", |
| "exp %s, got %s\n", link_pin_path, bpf_link__pin_path(link)); |
| |
| /* unpin link from BPFFS, program still attached */ |
| err = bpf_link__unpin(link); |
| if (CHECK(err, "link_unpin", "err: %d\n", err)) |
| goto cleanup; |
| |
| /* still active, as we have FD open now */ |
| bss->in = 4; |
| usleep(1); |
| CHECK(bss->out != 4, "res_check4", "exp %d, got %d\n", 4, bss->out); |
| |
| bpf_link__destroy(link); |
| link = NULL; |
| |
| /* Validate it's finally detached. |
| * Actual detachment might get delayed a bit, so there is no reliable |
| * way to validate it immediately here, let's count up for long enough |
| * and see if eventually output stops being updated |
| */ |
| for (i = 5; i < 10000; i++) { |
| bss->in = i; |
| usleep(1); |
| if (bss->out == i - 1) |
| break; |
| } |
| CHECK(i == 10000, "link_attached", "got to iteration #%d\n", i); |
| |
| cleanup: |
| if (!IS_ERR(link)) |
| bpf_link__destroy(link); |
| } |
| |
| void test_link_pinning(void) |
| { |
| struct test_link_pinning* skel; |
| |
| skel = test_link_pinning__open_and_load(); |
| if (CHECK(!skel, "skel_open", "failed to open skeleton\n")) |
| return; |
| |
| if (test__start_subtest("pin_raw_tp")) |
| test_link_pinning_subtest(skel->progs.raw_tp_prog, skel->bss); |
| if (test__start_subtest("pin_tp_btf")) |
| test_link_pinning_subtest(skel->progs.tp_btf_prog, skel->bss); |
| |
| test_link_pinning__destroy(skel); |
| } |