On 6/2/26 12:35 AM, Lin Ma wrote:
Add tail-call selftests for prog-array ownership when cgroup storage is
in use. Verify that loading succeeds when callers and callees reuse the
owner's cgroup storage map, and that loading fails for a different
storage map or for a storage-less bridge program.

Signed-off-by: Lin Ma <[email protected]>
Signed-off-by: Rongzhen Cui <[email protected]>
Signed-off-by: Jingguo Tan <[email protected]>
---
  .../selftests/bpf/prog_tests/tailcalls.c      | 103 ++++++++++++++++++
  .../bpf/progs/tailcall_cgrp_storage.c         |  44 ++++++++
  .../progs/tailcall_cgrp_storage_no_storage.c  |  20 ++++
  .../bpf/progs/tailcall_cgrp_storage_owner.c   |  32 ++++++
  4 files changed, 199 insertions(+)
  create mode 100644 tools/testing/selftests/bpf/progs/tailcall_cgrp_storage.c
  create mode 100644 
tools/testing/selftests/bpf/progs/tailcall_cgrp_storage_no_storage.c
  create mode 100644 
tools/testing/selftests/bpf/progs/tailcall_cgrp_storage_owner.c

diff --git a/tools/testing/selftests/bpf/prog_tests/tailcalls.c 
b/tools/testing/selftests/bpf/prog_tests/tailcalls.c
index 7d534fde0af9..c1403ab20eae 100644
--- a/tools/testing/selftests/bpf/prog_tests/tailcalls.c
+++ b/tools/testing/selftests/bpf/prog_tests/tailcalls.c
@@ -8,6 +8,9 @@
  #include "tailcall_freplace.skel.h"
  #include "tc_bpf2bpf.skel.h"
  #include "tailcall_fail.skel.h"
+#include "tailcall_cgrp_storage_owner.skel.h"
+#include "tailcall_cgrp_storage_no_storage.skel.h"
+#include "tailcall_cgrp_storage.skel.h"
  #include "tailcall_sleepable.skel.h"
/* test_tailcall_1 checks basic functionality by patching multiple locations
@@ -1654,6 +1657,100 @@ static void test_tailcall_failure()
        RUN_TESTS(tailcall_fail);
  }
+static void test_tailcall_cgrp_storage(void)
+{
+       struct tailcall_cgrp_storage_owner *owner_skel = NULL;
+       struct tailcall_cgrp_storage *skel = NULL;
+       int err, key = 0, prog_array_fd, prog_fd, storage_map_fd;
+
+       owner_skel = tailcall_cgrp_storage_owner__open_and_load();
+       if (!ASSERT_OK_PTR(owner_skel, "owner_open_and_load"))
+               return;
+
+       prog_array_fd = bpf_map__fd(owner_skel->maps.prog_array);
+       storage_map_fd = bpf_map__fd(owner_skel->maps.storage_map);
+
+       skel = tailcall_cgrp_storage__open();
+       if (!ASSERT_OK_PTR(skel, "tailcall_cgrp_storage__open"))
+               goto out;
+
+       err = bpf_map__reuse_fd(skel->maps.prog_array, prog_array_fd);
+       if (!ASSERT_OK(err, "reuse_prog_array"))
+               goto out;
+
+       err = bpf_map__reuse_fd(skel->maps.storage_map, storage_map_fd);
+       if (!ASSERT_OK(err, "reuse_storage_map"))
+               goto out;
+
+       err = bpf_object__load(skel->obj);
+       if (!ASSERT_OK(err, "tailcall_cgrp_storage__load"))
+               goto out;
+
+       prog_fd = bpf_program__fd(skel->progs.callee_prog);
+       err = bpf_map_update_elem(prog_array_fd, &key, &prog_fd, BPF_ANY);
+       ASSERT_OK(err, "update_prog_array");
+
+out:
+       tailcall_cgrp_storage__destroy(skel);
+       tailcall_cgrp_storage_owner__destroy(owner_skel);
+}
+
+static void test_tailcall_cgrp_storage_diff_storage(void)
+{
+       struct tailcall_cgrp_storage_owner *owner_skel = NULL;
+       struct tailcall_cgrp_storage *skel = NULL;
+       int err, prog_array_fd;
+
+       owner_skel = tailcall_cgrp_storage_owner__open_and_load();
+       if (!ASSERT_OK_PTR(owner_skel, "owner_open_and_load"))
+               return;
+
+       prog_array_fd = bpf_map__fd(owner_skel->maps.prog_array);
+
+       skel = tailcall_cgrp_storage__open();
+       if (!ASSERT_OK_PTR(skel, "tailcall_cgrp_storage__open"))
+               goto out;
+
+       err = bpf_map__reuse_fd(skel->maps.prog_array, prog_array_fd);
+       if (!ASSERT_OK(err, "reuse_prog_array"))
+               goto out;
+
+       err = bpf_object__load(skel->obj);
+       ASSERT_ERR(err, "tailcall_cgrp_storage__load");
+
+out:
+       tailcall_cgrp_storage__destroy(skel);
+       tailcall_cgrp_storage_owner__destroy(owner_skel);
+}
+
+static void test_tailcall_cgrp_storage_no_storage(void)
+{
+       struct tailcall_cgrp_storage_owner *owner_skel = NULL;
+       struct tailcall_cgrp_storage_no_storage *skel = NULL;
+       int err, prog_array_fd;
+
+       owner_skel = tailcall_cgrp_storage_owner__open_and_load();
+       if (!ASSERT_OK_PTR(owner_skel, "owner_open_and_load"))
+               return;
+
+       prog_array_fd = bpf_map__fd(owner_skel->maps.prog_array);
+
+       skel = tailcall_cgrp_storage_no_storage__open();
+       if (!ASSERT_OK_PTR(skel, "tailcall_cgrp_storage_no_storage__open"))
+               goto out;
+
+       err = bpf_map__reuse_fd(skel->maps.prog_array, prog_array_fd);
+       if (!ASSERT_OK(err, "reuse_prog_array"))
+               goto out;
+
+       err = bpf_object__load(skel->obj);
+       ASSERT_ERR(err, "tailcall_cgrp_storage_no_storage__load");
+
+out:
+       tailcall_cgrp_storage_no_storage__destroy(skel);
+       tailcall_cgrp_storage_owner__destroy(owner_skel);
+}
+
  noinline void uprobe_sleepable_trigger(void)
  {
        asm volatile ("");
@@ -1777,6 +1874,12 @@ void test_tailcalls(void)
                test_tailcall_freplace();
        if (test__start_subtest("tailcall_bpf2bpf_freplace"))
                test_tailcall_bpf2bpf_freplace();
+       if (test__start_subtest("tailcall_cgrp_storage"))
+               test_tailcall_cgrp_storage();
+       if (test__start_subtest("tailcall_cgrp_storage_diff_storage"))
+               test_tailcall_cgrp_storage_diff_storage();
+       if (test__start_subtest("tailcall_cgrp_storage_no_storage"))
+               test_tailcall_cgrp_storage_no_storage();

Since the failure is due to 'A -> B(no storage) -> C(storage)' in your commit 
message,
you should create a selftest for that. Current selftests are good but not 
addressing
the issue in patch 1.

        if (test__start_subtest("tailcall_failure"))
                test_tailcall_failure();
        if (test__start_subtest("tailcall_sleepable"))

[...]


Reply via email to