Skip to content

Commit 15ac1ad

Browse files
olsajiriAlexei Starovoitov
authored andcommitted
selftests/bpf: Add test for sleepable program tailcalls
Adding test that makes sure we can't mix sleepable and non-sleepable bpf programs in the BPF_MAP_TYPE_PROG_ARRAY map and that we can do tail call in the sleepable program. Signed-off-by: Jiri Olsa <jolsa@kernel.org> Link: https://lore.kernel.org/r/20260130081208.1130204-3-jolsa@kernel.org Signed-off-by: Alexei Starovoitov <ast@kernel.org>
1 parent 0f0c332 commit 15ac1ad

2 files changed

Lines changed: 117 additions & 0 deletions

File tree

tools/testing/selftests/bpf/prog_tests/tailcalls.c

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "tailcall_freplace.skel.h"
99
#include "tc_bpf2bpf.skel.h"
1010
#include "tailcall_fail.skel.h"
11+
#include "tailcall_sleepable.skel.h"
1112

1213
/* test_tailcall_1 checks basic functionality by patching multiple locations
1314
* in a single program for a single tail call slot with nop->jmp, jmp->nop
@@ -1653,6 +1654,77 @@ static void test_tailcall_failure()
16531654
RUN_TESTS(tailcall_fail);
16541655
}
16551656

1657+
noinline void uprobe_sleepable_trigger(void)
1658+
{
1659+
asm volatile ("");
1660+
}
1661+
1662+
static void test_tailcall_sleepable(void)
1663+
{
1664+
LIBBPF_OPTS(bpf_uprobe_opts, opts);
1665+
struct tailcall_sleepable *skel;
1666+
int prog_fd, map_fd;
1667+
int err, key;
1668+
1669+
skel = tailcall_sleepable__open();
1670+
if (!ASSERT_OK_PTR(skel, "tailcall_sleepable__open"))
1671+
return;
1672+
1673+
/*
1674+
* Test that we can't load uprobe_normal and uprobe_sleepable_1,
1675+
* because they share tailcall map.
1676+
*/
1677+
bpf_program__set_autoload(skel->progs.uprobe_normal, true);
1678+
bpf_program__set_autoload(skel->progs.uprobe_sleepable_1, true);
1679+
1680+
err = tailcall_sleepable__load(skel);
1681+
if (!ASSERT_ERR(err, "tailcall_sleepable__load"))
1682+
goto out;
1683+
1684+
tailcall_sleepable__destroy(skel);
1685+
1686+
/*
1687+
* Test that we can tail call from sleepable to sleepable program.
1688+
*/
1689+
skel = tailcall_sleepable__open();
1690+
if (!ASSERT_OK_PTR(skel, "tailcall_sleepable__open"))
1691+
return;
1692+
1693+
bpf_program__set_autoload(skel->progs.uprobe_sleepable_1, true);
1694+
bpf_program__set_autoload(skel->progs.uprobe_sleepable_2, true);
1695+
1696+
err = tailcall_sleepable__load(skel);
1697+
if (!ASSERT_OK(err, "tailcall_sleepable__load"))
1698+
goto out;
1699+
1700+
/* Add sleepable uprobe_sleepable_2 to jmp_table[0]. */
1701+
key = 0;
1702+
prog_fd = bpf_program__fd(skel->progs.uprobe_sleepable_2);
1703+
map_fd = bpf_map__fd(skel->maps.jmp_table);
1704+
err = bpf_map_update_elem(map_fd, &key, &prog_fd, BPF_ANY);
1705+
if (!ASSERT_OK(err, "update jmp_table"))
1706+
goto out;
1707+
1708+
skel->bss->my_pid = getpid();
1709+
1710+
/* Attach uprobe_sleepable_1 to uprobe_sleepable_trigger and hit it. */
1711+
opts.func_name = "uprobe_sleepable_trigger";
1712+
skel->links.uprobe_sleepable_1 = bpf_program__attach_uprobe_opts(
1713+
skel->progs.uprobe_sleepable_1,
1714+
-1,
1715+
"/proc/self/exe",
1716+
0 /* offset */,
1717+
&opts);
1718+
if (!ASSERT_OK_PTR(skel->links.uprobe_sleepable_1, "bpf_program__attach_uprobe_opts"))
1719+
goto out;
1720+
1721+
uprobe_sleepable_trigger();
1722+
ASSERT_EQ(skel->bss->executed, 1, "executed");
1723+
1724+
out:
1725+
tailcall_sleepable__destroy(skel);
1726+
}
1727+
16561728
void test_tailcalls(void)
16571729
{
16581730
if (test__start_subtest("tailcall_1"))
@@ -1707,4 +1779,6 @@ void test_tailcalls(void)
17071779
test_tailcall_bpf2bpf_freplace();
17081780
if (test__start_subtest("tailcall_failure"))
17091781
test_tailcall_failure();
1782+
if (test__start_subtest("tailcall_sleepable"))
1783+
test_tailcall_sleepable();
17101784
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
#include <linux/bpf.h>
3+
#include <bpf/bpf_helpers.h>
4+
#include "bpf_misc.h"
5+
#include "bpf_test_utils.h"
6+
7+
struct {
8+
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
9+
__uint(max_entries, 1);
10+
__uint(key_size, sizeof(__u32));
11+
__array(values, void (void));
12+
} jmp_table SEC(".maps");
13+
14+
SEC("?uprobe")
15+
int uprobe_normal(void *ctx)
16+
{
17+
bpf_tail_call_static(ctx, &jmp_table, 0);
18+
return 0;
19+
}
20+
21+
SEC("?uprobe.s")
22+
int uprobe_sleepable_1(void *ctx)
23+
{
24+
bpf_tail_call_static(ctx, &jmp_table, 0);
25+
return 0;
26+
}
27+
28+
int executed = 0;
29+
int my_pid = 0;
30+
31+
SEC("?uprobe.s")
32+
int uprobe_sleepable_2(void *ctx)
33+
{
34+
int pid = bpf_get_current_pid_tgid() >> 32;
35+
36+
if (pid != my_pid)
37+
return 0;
38+
39+
executed++;
40+
return 0;
41+
}
42+
43+
char __license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)