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 'selftests-bpf-introduce-execution-context-detection-helpers'

Changwoo Min says:

====================
selftests/bpf: Introduce execution context detection helpers

This series introduces four new BPF-native inline helpers -- bpf_in_nmi(),
bpf_in_hardirq(), bpf_in_serving_softirq(), and bpf_in_task() -- to allow
BPF programs to query the current execution context.

Following the feedback on v1, these are implemented in bpf_experimental.h
as inline helpers wrapping get_preempt_count(). This approach allows the
logic to be JIT-inlined for better performance compared to a kfunc call,
while providing the granular context detection (e.g., hardirq vs. softirq)
required by subsystems like sched_ext.

The series includes a new selftest suite, exe_ctx, which uses bpf_testmod
to verify context detection across Task, HardIRQ, and SoftIRQ boundaries
via irq_work and tasklets. NMI context testing is omitted as NMIs cannot
be triggered deterministically within software-only BPF CI environments.

ChangeLog v2 -> v3:
- Added exe_ctx to DENYLIST.s390x since new helpers are supported only
on x86 and arm64 (patch 2).
- Added comments to helpers describing supported architectures (patch 1).

ChangeLog v1 -> v2:
- Dropped the core kernel kfunc implementations, and implemented context
detection as inline BPF helpers in bpf_experimental.h.
- Renamed the selftest suite from ctx_kfunc to exe_ctx to reflect the
change from kfuncs to helpers.
- Updated BPF programs to use the new inline helpers.
- Swapped clean-up order between tasklet and irqwork in bpf_testmod to
avoid re-scheduling the already-killed tasklet (reported by bot+bpf-ci).
====================

Link: https://patch.msgid.link/20260125115413.117502-1-changwoo@igalia.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

+202
+1
tools/testing/selftests/bpf/DENYLIST.s390x
··· 1 1 # TEMPORARY 2 2 # Alphabetical order 3 + exe_ctx # execution context check (e.g., hardirq, softirq, etc) 3 4 get_stack_raw_tp # user_stack corrupted user stack (no backchain userspace) 4 5 stacktrace_build_id # compare_map_keys stackid_hmap vs. stackmap err -2 errno 2 (?)
+58
tools/testing/selftests/bpf/bpf_experimental.h
··· 610 610 #define HARDIRQ_MASK (__IRQ_MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT) 611 611 #define NMI_MASK (__IRQ_MASK(NMI_BITS) << NMI_SHIFT) 612 612 613 + #define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT) 614 + 613 615 extern bool CONFIG_PREEMPT_RT __kconfig __weak; 614 616 #ifdef bpf_target_x86 615 617 extern const int __preempt_count __ksym; ··· 650 648 (tsk->softirq_disable_cnt & SOFTIRQ_MASK); 651 649 } 652 650 651 + /* Description 652 + * Report whether it is in NMI context. Only works on the following archs: 653 + * * x86 654 + * * arm64 655 + */ 656 + static inline int bpf_in_nmi(void) 657 + { 658 + return get_preempt_count() & NMI_MASK; 659 + } 660 + 661 + /* Description 662 + * Report whether it is in hard IRQ context. Only works on the following archs: 663 + * * x86 664 + * * arm64 665 + */ 666 + static inline int bpf_in_hardirq(void) 667 + { 668 + return get_preempt_count() & HARDIRQ_MASK; 669 + } 670 + 671 + /* Description 672 + * Report whether it is in softirq context. Only works on the following archs: 673 + * * x86 674 + * * arm64 675 + */ 676 + static inline int bpf_in_serving_softirq(void) 677 + { 678 + struct task_struct___preempt_rt *tsk; 679 + int pcnt; 680 + 681 + pcnt = get_preempt_count(); 682 + if (!CONFIG_PREEMPT_RT) 683 + return (pcnt & SOFTIRQ_MASK) & SOFTIRQ_OFFSET; 684 + 685 + tsk = (void *) bpf_get_current_task_btf(); 686 + return (tsk->softirq_disable_cnt & SOFTIRQ_MASK) & SOFTIRQ_OFFSET; 687 + } 688 + 689 + /* Description 690 + * Report whether it is in task context. Only works on the following archs: 691 + * * x86 692 + * * arm64 693 + */ 694 + static inline int bpf_in_task(void) 695 + { 696 + struct task_struct___preempt_rt *tsk; 697 + int pcnt; 698 + 699 + pcnt = get_preempt_count(); 700 + if (!CONFIG_PREEMPT_RT) 701 + return !(pcnt & (NMI_MASK | HARDIRQ_MASK | SOFTIRQ_OFFSET)); 702 + 703 + tsk = (void *) bpf_get_current_task_btf(); 704 + return !((pcnt & (NMI_MASK | HARDIRQ_MASK)) | 705 + ((tsk->softirq_disable_cnt & SOFTIRQ_MASK) & SOFTIRQ_OFFSET)); 706 + } 653 707 #endif
+59
tools/testing/selftests/bpf/prog_tests/exe_ctx.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (c) 2026 Valve Corporation. 4 + * Author: Changwoo Min <changwoo@igalia.com> 5 + */ 6 + 7 + #include <test_progs.h> 8 + #include <sys/syscall.h> 9 + #include "test_ctx.skel.h" 10 + 11 + void test_exe_ctx(void) 12 + { 13 + LIBBPF_OPTS(bpf_test_run_opts, opts); 14 + cpu_set_t old_cpuset, target_cpuset; 15 + struct test_ctx *skel; 16 + int err, prog_fd; 17 + 18 + /* 1. Pin the current process to CPU 0. */ 19 + if (sched_getaffinity(0, sizeof(old_cpuset), &old_cpuset) == 0) { 20 + CPU_ZERO(&target_cpuset); 21 + CPU_SET(0, &target_cpuset); 22 + ASSERT_OK(sched_setaffinity(0, sizeof(target_cpuset), 23 + &target_cpuset), "setaffinity"); 24 + } 25 + 26 + skel = test_ctx__open_and_load(); 27 + if (!ASSERT_OK_PTR(skel, "skel_load")) 28 + goto restore_affinity; 29 + 30 + err = test_ctx__attach(skel); 31 + if (!ASSERT_OK(err, "skel_attach")) 32 + goto cleanup; 33 + 34 + /* 2. When we run this, the kernel will execute the BPF prog on CPU 0. */ 35 + prog_fd = bpf_program__fd(skel->progs.trigger_all_contexts); 36 + err = bpf_prog_test_run_opts(prog_fd, &opts); 37 + ASSERT_OK(err, "test_run_trigger"); 38 + 39 + /* 3. Wait for the local CPU's softirq/tasklet to finish. */ 40 + for (int i = 0; i < 1000; i++) { 41 + if (skel->bss->count_task > 0 && 42 + skel->bss->count_hardirq > 0 && 43 + skel->bss->count_softirq > 0) 44 + break; 45 + usleep(1000); /* Wait 1ms per iteration, up to 1 sec total */ 46 + } 47 + 48 + /* On CPU 0, these should now all be non-zero. */ 49 + ASSERT_GT(skel->bss->count_task, 0, "task_ok"); 50 + ASSERT_GT(skel->bss->count_hardirq, 0, "hardirq_ok"); 51 + ASSERT_GT(skel->bss->count_softirq, 0, "softirq_ok"); 52 + 53 + cleanup: 54 + test_ctx__destroy(skel); 55 + 56 + restore_affinity: 57 + ASSERT_OK(sched_setaffinity(0, sizeof(old_cpuset), &old_cpuset), 58 + "restore_affinity"); 59 + }
+48
tools/testing/selftests/bpf/progs/test_ctx.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (c) 2026 Valve Corporation. 4 + * Author: Changwoo Min <changwoo@igalia.com> 5 + */ 6 + 7 + #include "vmlinux.h" 8 + #include <bpf/bpf_helpers.h> 9 + #include <bpf/bpf_tracing.h> 10 + #include "bpf_experimental.h" 11 + 12 + char _license[] SEC("license") = "GPL"; 13 + 14 + extern void bpf_kfunc_trigger_ctx_check(void) __ksym; 15 + 16 + int count_hardirq; 17 + int count_softirq; 18 + int count_task; 19 + 20 + /* Triggered via bpf_prog_test_run from user-space */ 21 + SEC("syscall") 22 + int trigger_all_contexts(void *ctx) 23 + { 24 + if (bpf_in_task()) 25 + __sync_fetch_and_add(&count_task, 1); 26 + 27 + /* Trigger the firing of a hardirq and softirq for test. */ 28 + bpf_kfunc_trigger_ctx_check(); 29 + return 0; 30 + } 31 + 32 + /* Observer for HardIRQ */ 33 + SEC("fentry/bpf_testmod_test_hardirq_fn") 34 + int BPF_PROG(on_hardirq) 35 + { 36 + if (bpf_in_hardirq()) 37 + __sync_fetch_and_add(&count_hardirq, 1); 38 + return 0; 39 + } 40 + 41 + /* Observer for SoftIRQ */ 42 + SEC("fentry/bpf_testmod_test_softirq_fn") 43 + int BPF_PROG(on_softirq) 44 + { 45 + if (bpf_in_serving_softirq()) 46 + __sync_fetch_and_add(&count_softirq, 1); 47 + return 0; 48 + }
+32
tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
··· 1168 1168 __bpf_kfunc int bpf_kfunc_implicit_arg_legacy(int a, int b, struct bpf_prog_aux *aux); 1169 1169 __bpf_kfunc int bpf_kfunc_implicit_arg_legacy_impl(int a, int b, struct bpf_prog_aux *aux); 1170 1170 1171 + /* hook targets */ 1172 + noinline void bpf_testmod_test_hardirq_fn(void) { barrier(); } 1173 + noinline void bpf_testmod_test_softirq_fn(void) { barrier(); } 1174 + 1175 + /* Tasklet for SoftIRQ context */ 1176 + static void ctx_check_tasklet_fn(struct tasklet_struct *t) 1177 + { 1178 + bpf_testmod_test_softirq_fn(); 1179 + } 1180 + 1181 + DECLARE_TASKLET(ctx_check_tasklet, ctx_check_tasklet_fn); 1182 + 1183 + /* IRQ Work for HardIRQ context */ 1184 + static void ctx_check_irq_fn(struct irq_work *work) 1185 + { 1186 + bpf_testmod_test_hardirq_fn(); 1187 + tasklet_schedule(&ctx_check_tasklet); 1188 + } 1189 + 1190 + static struct irq_work ctx_check_irq = IRQ_WORK_INIT_HARD(ctx_check_irq_fn); 1191 + 1192 + /* The kfunc trigger */ 1193 + __bpf_kfunc void bpf_kfunc_trigger_ctx_check(void) 1194 + { 1195 + irq_work_queue(&ctx_check_irq); 1196 + } 1197 + 1171 1198 BTF_KFUNCS_START(bpf_testmod_check_kfunc_ids) 1172 1199 BTF_ID_FLAGS(func, bpf_testmod_test_mod_kfunc) 1173 1200 BTF_ID_FLAGS(func, bpf_kfunc_call_test1) ··· 1240 1213 BTF_ID_FLAGS(func, bpf_kfunc_implicit_arg, KF_IMPLICIT_ARGS) 1241 1214 BTF_ID_FLAGS(func, bpf_kfunc_implicit_arg_legacy, KF_IMPLICIT_ARGS) 1242 1215 BTF_ID_FLAGS(func, bpf_kfunc_implicit_arg_legacy_impl) 1216 + BTF_ID_FLAGS(func, bpf_kfunc_trigger_ctx_check) 1243 1217 BTF_KFUNCS_END(bpf_testmod_check_kfunc_ids) 1244 1218 1245 1219 static int bpf_testmod_ops_init(struct btf *btf) ··· 1871 1843 */ 1872 1844 while (refcount_read(&prog_test_struct.cnt) > 1) 1873 1845 msleep(20); 1846 + 1847 + /* Clean up irqwork and tasklet */ 1848 + irq_work_sync(&ctx_check_irq); 1849 + tasklet_kill(&ctx_check_tasklet); 1874 1850 1875 1851 bpf_kfunc_close_sock(); 1876 1852 sysfs_remove_bin_file(kernel_kobj, &bin_attr_bpf_testmod_file);
+4
tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
··· 169 169 struct prog_test_member *bpf_kfunc_get_default_trusted_ptr_test(void) __ksym; 170 170 void bpf_kfunc_put_default_trusted_ptr_test(struct prog_test_member *trusted_ptr) __ksym; 171 171 172 + void bpf_testmod_test_hardirq_fn(void); 173 + void bpf_testmod_test_softirq_fn(void); 174 + void bpf_kfunc_trigger_ctx_check(void) __ksym; 175 + 172 176 #endif /* _BPF_TESTMOD_KFUNC_H */