]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
selftests/bpf: Test cgroup link replace with BPF_F_PREORDER
authorAmery Hung <ameryhung@gmail.com>
Fri, 19 Jun 2026 06:35:20 +0000 (23:35 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Mon, 22 Jun 2026 01:10:04 +0000 (18:10 -0700)
Add a cgroup_preorder case that attaches a normal and a BPF_F_PREORDER
program to a cgroup (effective order [2, 1]), then replaces the normal
link's program via bpf_link_update() and checks the effective order
becomes [2, 3] — i.e. only the non-preorder slot changes. Without the
replace_effective_prog() fix the array is corrupted and the order is
wrong.

Signed-off-by: Amery Hung <ameryhung@gmail.com>
Acked-by: Yonghong Song <yonghong.song@linux.dev>
Link: https://lore.kernel.org/r/20260619063520.2690547-3-ameryhung@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
tools/testing/selftests/bpf/prog_tests/cgroup_preorder.c

index d4d583872fa261b2542f8cb8215f8c1e5c995264..d2ccf409dfe3398fb2bcac4fb430debf5b0860df 100644 (file)
@@ -102,6 +102,82 @@ close_skel:
        return err;
 }
 
+/*
+ * Replacing a link's program (bpf_link_update) must target the correct slot in
+ * the effective array even when a BPF_F_PREORDER program is attached to the
+ * same cgroup. All programs here are attached to a single cgroup; "parent" is
+ * reused only as a third distinct program.
+ *
+ * Attach child(1) normally and child_2(2) with BPF_F_PREORDER, so the effective
+ * order is [2, 1]. Then replace child(1)'s program with parent(3): only the
+ * non-preorder slot changes, giving [2, 3].
+ */
+static int run_link_replace_test(int cgroup_fd, int sock_fd)
+{
+       LIBBPF_OPTS(bpf_link_create_opts, create_opts);
+       int err = 0, normal_link = -1, preorder_link = -1;
+       struct cgroup_preorder *skel = NULL;
+       enum bpf_attach_type atype;
+       __u8 *result, buf = 0x00;
+       socklen_t optlen = 1;
+
+       skel = cgroup_preorder__open_and_load();
+       if (!ASSERT_OK_PTR(skel, "cgroup_preorder__open_and_load"))
+               return -1;
+
+       err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1);
+       if (!ASSERT_OK(err, "setsockopt"))
+               goto close_skel;
+
+       atype = bpf_program__expected_attach_type(skel->progs.child);
+
+       create_opts.flags = 0;
+       normal_link = bpf_link_create(bpf_program__fd(skel->progs.child),
+                                     cgroup_fd, atype, &create_opts);
+       if (!ASSERT_GE(normal_link, 0, "create_normal_link")) {
+               err = normal_link;
+               goto close_skel;
+       }
+
+       create_opts.flags = BPF_F_PREORDER;
+       preorder_link = bpf_link_create(bpf_program__fd(skel->progs.child_2),
+                                       cgroup_fd, atype, &create_opts);
+       if (!ASSERT_GE(preorder_link, 0, "create_preorder_link")) {
+               err = preorder_link;
+               goto close_links;
+       }
+
+       result = skel->bss->result;
+       skel->bss->idx = 0;
+       memset(result, 0, 4);
+
+       err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
+       if (!ASSERT_OK(err, "getsockopt-before"))
+               goto close_links;
+       ASSERT_TRUE(result[0] == 2 && result[1] == 1, "order before update");
+
+       /* Replace the normal link's program child(1) -> parent(3). */
+       err = bpf_link_update(normal_link, bpf_program__fd(skel->progs.parent), NULL);
+       if (!ASSERT_OK(err, "bpf_link_update"))
+               goto close_links;
+
+       skel->bss->idx = 0;
+       memset(result, 0, 4);
+
+       err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
+       if (!ASSERT_OK(err, "getsockopt-after"))
+               goto close_links;
+       ASSERT_TRUE(result[0] == 2 && result[1] == 3, "order after update");
+
+close_links:
+       if (preorder_link >= 0)
+               close(preorder_link);
+       close(normal_link);
+close_skel:
+       cgroup_preorder__destroy(skel);
+       return err;
+}
+
 void test_cgroup_preorder(void)
 {
        int cg_parent = -1, cg_child = -1, sock_fd = -1;
@@ -120,6 +196,7 @@ void test_cgroup_preorder(void)
 
        ASSERT_OK(run_getsockopt_test(cg_parent, cg_child, sock_fd, false), "getsockopt_test_1");
        ASSERT_OK(run_getsockopt_test(cg_parent, cg_child, sock_fd, true), "getsockopt_test_2");
+       ASSERT_OK(run_link_replace_test(cg_child, sock_fd), "link_replace_test");
 
 out:
        close(sock_fd);