Skip to content

Commit b18a761

Browse files
author
Alexei Starovoitov
committed
Merge branch 'bpf-tail-calls-in-sleepable-programs'
Jiri Olsa says: ==================== this patchset allows sleepable programs to use tail calls. At the moment we need to have separate sleepable uprobe program to retrieve user space data and pass it to complex program with tail calls. It'd be great if the program with tail calls could be sleepable and do the data retrieval directly. ==================== Link: https://patch.msgid.link/20260130081208.1130204-1-jolsa@kernel.org Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2 parents 95dbe21 + 15ac1ad commit b18a761

5 files changed

Lines changed: 122 additions & 1 deletion

File tree

include/linux/bpf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ struct bpf_map_owner {
287287
enum bpf_prog_type type;
288288
bool jited;
289289
bool xdp_has_frags;
290+
bool sleepable;
290291
u64 storage_cookie[MAX_BPF_CGROUP_STORAGE_TYPE];
291292
const struct btf_type *attach_func_proto;
292293
enum bpf_attach_type expected_attach_type;

kernel/bpf/core.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2401,6 +2401,7 @@ static bool __bpf_prog_map_compatible(struct bpf_map *map,
24012401
map->owner->type = prog_type;
24022402
map->owner->jited = fp->jited;
24032403
map->owner->xdp_has_frags = aux->xdp_has_frags;
2404+
map->owner->sleepable = fp->sleepable;
24042405
map->owner->expected_attach_type = fp->expected_attach_type;
24052406
map->owner->attach_func_proto = aux->attach_func_proto;
24062407
for_each_cgroup_storage_type(i) {
@@ -2412,7 +2413,8 @@ static bool __bpf_prog_map_compatible(struct bpf_map *map,
24122413
} else {
24132414
ret = map->owner->type == prog_type &&
24142415
map->owner->jited == fp->jited &&
2415-
map->owner->xdp_has_frags == aux->xdp_has_frags;
2416+
map->owner->xdp_has_frags == aux->xdp_has_frags &&
2417+
map->owner->sleepable == fp->sleepable;
24162418
if (ret &&
24172419
map->map_type == BPF_MAP_TYPE_PROG_ARRAY &&
24182420
map->owner->expected_attach_type != fp->expected_attach_type)

kernel/bpf/verifier.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21386,6 +21386,7 @@ static int check_map_prog_compatibility(struct bpf_verifier_env *env,
2138621386
case BPF_MAP_TYPE_STACK:
2138721387
case BPF_MAP_TYPE_ARENA:
2138821388
case BPF_MAP_TYPE_INSN_ARRAY:
21389+
case BPF_MAP_TYPE_PROG_ARRAY:
2138921390
break;
2139021391
default:
2139121392
verbose(env,

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)