Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

+122 -1
+1
include/linux/bpf.h
··· 287 287 enum bpf_prog_type type; 288 288 bool jited; 289 289 bool xdp_has_frags; 290 + bool sleepable; 290 291 u64 storage_cookie[MAX_BPF_CGROUP_STORAGE_TYPE]; 291 292 const struct btf_type *attach_func_proto; 292 293 enum bpf_attach_type expected_attach_type;
+3 -1
kernel/bpf/core.c
··· 2401 2401 map->owner->type = prog_type; 2402 2402 map->owner->jited = fp->jited; 2403 2403 map->owner->xdp_has_frags = aux->xdp_has_frags; 2404 + map->owner->sleepable = fp->sleepable; 2404 2405 map->owner->expected_attach_type = fp->expected_attach_type; 2405 2406 map->owner->attach_func_proto = aux->attach_func_proto; 2406 2407 for_each_cgroup_storage_type(i) { ··· 2413 2412 } else { 2414 2413 ret = map->owner->type == prog_type && 2415 2414 map->owner->jited == fp->jited && 2416 - map->owner->xdp_has_frags == aux->xdp_has_frags; 2415 + map->owner->xdp_has_frags == aux->xdp_has_frags && 2416 + map->owner->sleepable == fp->sleepable; 2417 2417 if (ret && 2418 2418 map->map_type == BPF_MAP_TYPE_PROG_ARRAY && 2419 2419 map->owner->expected_attach_type != fp->expected_attach_type)
+1
kernel/bpf/verifier.c
··· 21386 21386 case BPF_MAP_TYPE_STACK: 21387 21387 case BPF_MAP_TYPE_ARENA: 21388 21388 case BPF_MAP_TYPE_INSN_ARRAY: 21389 + case BPF_MAP_TYPE_PROG_ARRAY: 21389 21390 break; 21390 21391 default: 21391 21392 verbose(env,
+74
tools/testing/selftests/bpf/prog_tests/tailcalls.c
··· 8 8 #include "tailcall_freplace.skel.h" 9 9 #include "tc_bpf2bpf.skel.h" 10 10 #include "tailcall_fail.skel.h" 11 + #include "tailcall_sleepable.skel.h" 11 12 12 13 /* test_tailcall_1 checks basic functionality by patching multiple locations 13 14 * in a single program for a single tail call slot with nop->jmp, jmp->nop ··· 1654 1653 RUN_TESTS(tailcall_fail); 1655 1654 } 1656 1655 1656 + noinline void uprobe_sleepable_trigger(void) 1657 + { 1658 + asm volatile (""); 1659 + } 1660 + 1661 + static void test_tailcall_sleepable(void) 1662 + { 1663 + LIBBPF_OPTS(bpf_uprobe_opts, opts); 1664 + struct tailcall_sleepable *skel; 1665 + int prog_fd, map_fd; 1666 + int err, key; 1667 + 1668 + skel = tailcall_sleepable__open(); 1669 + if (!ASSERT_OK_PTR(skel, "tailcall_sleepable__open")) 1670 + return; 1671 + 1672 + /* 1673 + * Test that we can't load uprobe_normal and uprobe_sleepable_1, 1674 + * because they share tailcall map. 1675 + */ 1676 + bpf_program__set_autoload(skel->progs.uprobe_normal, true); 1677 + bpf_program__set_autoload(skel->progs.uprobe_sleepable_1, true); 1678 + 1679 + err = tailcall_sleepable__load(skel); 1680 + if (!ASSERT_ERR(err, "tailcall_sleepable__load")) 1681 + goto out; 1682 + 1683 + tailcall_sleepable__destroy(skel); 1684 + 1685 + /* 1686 + * Test that we can tail call from sleepable to sleepable program. 1687 + */ 1688 + skel = tailcall_sleepable__open(); 1689 + if (!ASSERT_OK_PTR(skel, "tailcall_sleepable__open")) 1690 + return; 1691 + 1692 + bpf_program__set_autoload(skel->progs.uprobe_sleepable_1, true); 1693 + bpf_program__set_autoload(skel->progs.uprobe_sleepable_2, true); 1694 + 1695 + err = tailcall_sleepable__load(skel); 1696 + if (!ASSERT_OK(err, "tailcall_sleepable__load")) 1697 + goto out; 1698 + 1699 + /* Add sleepable uprobe_sleepable_2 to jmp_table[0]. */ 1700 + key = 0; 1701 + prog_fd = bpf_program__fd(skel->progs.uprobe_sleepable_2); 1702 + map_fd = bpf_map__fd(skel->maps.jmp_table); 1703 + err = bpf_map_update_elem(map_fd, &key, &prog_fd, BPF_ANY); 1704 + if (!ASSERT_OK(err, "update jmp_table")) 1705 + goto out; 1706 + 1707 + skel->bss->my_pid = getpid(); 1708 + 1709 + /* Attach uprobe_sleepable_1 to uprobe_sleepable_trigger and hit it. */ 1710 + opts.func_name = "uprobe_sleepable_trigger"; 1711 + skel->links.uprobe_sleepable_1 = bpf_program__attach_uprobe_opts( 1712 + skel->progs.uprobe_sleepable_1, 1713 + -1, 1714 + "/proc/self/exe", 1715 + 0 /* offset */, 1716 + &opts); 1717 + if (!ASSERT_OK_PTR(skel->links.uprobe_sleepable_1, "bpf_program__attach_uprobe_opts")) 1718 + goto out; 1719 + 1720 + uprobe_sleepable_trigger(); 1721 + ASSERT_EQ(skel->bss->executed, 1, "executed"); 1722 + 1723 + out: 1724 + tailcall_sleepable__destroy(skel); 1725 + } 1726 + 1657 1727 void test_tailcalls(void) 1658 1728 { 1659 1729 if (test__start_subtest("tailcall_1")) ··· 1779 1707 test_tailcall_bpf2bpf_freplace(); 1780 1708 if (test__start_subtest("tailcall_failure")) 1781 1709 test_tailcall_failure(); 1710 + if (test__start_subtest("tailcall_sleepable")) 1711 + test_tailcall_sleepable(); 1782 1712 }
+43
tools/testing/selftests/bpf/progs/tailcall_sleepable.c
··· 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";