diff --git a/tests/README.md b/tests/README.md index a31a36c389..9f85865c7e 100644 --- a/tests/README.md +++ b/tests/README.md @@ -367,3 +367,7 @@ BTF, or `bpftool` or `clang` is unavailable. drops on rejection, so a working guard blocks the ping, proving the kfunc ran and returned without crashing. +- **xdp percpu**: with the ping pinned to the last online CPU, which is where + the veth runs the receive softirq, the callback of a percpu script reports + that CPU as its instance id, and no other; skipped on a single CPU. + diff --git a/tests/runtime/percpu_fail.lua b/tests/runtime/percpu_fail.lua index 6575a9a29c..b00326c98a 100644 --- a/tests/runtime/percpu_fail.lua +++ b/tests/runtime/percpu_fail.lua @@ -6,11 +6,7 @@ local lunatik = require("lunatik") -local env = lunatik._ENV -local count = (env.percpu_fail_count or 0) + 1 -env.percpu_fail_count = count -if count == 2 then - env.percpu_fail_count = nil +if lunatik.cpu() == 1 then error("intentional error on the second instance") end diff --git a/tests/xdp/Makefile b/tests/xdp/Makefile index 11b4fe2557..896cd9614a 100644 --- a/tests/xdp/Makefile +++ b/tests/xdp/Makefile @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: (c) 2026 Ashwani Kumar Kamal # SPDX-License-Identifier: MIT OR GPL-2.0-only -all: xdp_pass.bpf.o xdp_drop.bpf.o xdp_detach.bpf.o xdp_zerokey.bpf.o +all: xdp_pass.bpf.o xdp_drop.bpf.o xdp_detach.bpf.o xdp_zerokey.bpf.o xdp_percpu.bpf.o vmlinux.h: bpftool btf dump file /sys/kernel/btf/vmlinux format c > $@ @@ -18,6 +18,9 @@ xdp_detach.bpf.o: xdp_detach.bpf.c vmlinux.h xdp_zerokey.bpf.o: xdp_zerokey.bpf.c vmlinux.h clang -target bpf -Wall -O2 -c -g $< +xdp_percpu.bpf.o: xdp_percpu.bpf.c vmlinux.h + clang -target bpf -Wall -O2 -c -g $< + clean: - rm -f vmlinux.h xdp_pass.bpf.o xdp_drop.bpf.o xdp_detach.bpf.o xdp_zerokey.bpf.o + rm -f vmlinux.h xdp_pass.bpf.o xdp_drop.bpf.o xdp_detach.bpf.o xdp_zerokey.bpf.o xdp_percpu.bpf.o diff --git a/tests/xdp/percpu.lua b/tests/xdp/percpu.lua new file mode 100644 index 0000000000..ae72d44ce8 --- /dev/null +++ b/tests/xdp/percpu.lua @@ -0,0 +1,29 @@ +-- +-- SPDX-FileCopyrightText: (c) 2026 Ring Zero Desenvolvimento de Software LTDA +-- SPDX-License-Identifier: MIT OR GPL-2.0-only +-- +-- Kernel-side script for the xdp percpu dispatch test (see test_xdp.sh). + +local lunatik = require("lunatik") +local xdp = require("xdp") +local action = require("linux.xdp") + +local ETH_HI = 12 -- ethertype, then IPv4 protocol +local ETH_LO = 13 +local IPPROTO = 23 +local ICMP = 1 + +local cpu = lunatik.cpu() + +local function test_percpu(ctx) + local packet = ctx:packet() + -- the namespace also emits IPv6 autoconf traffic, from a context the test does not pin + if packet:getuint8(ETH_HI) == 0x08 and packet:getuint8(ETH_LO) == 0x00 + and packet:getuint8(IPPROTO) == ICMP then + print("xdp percpu test hit: cpu " .. cpu) + end + ctx:action(action.PASS) +end + +xdp.attach(test_percpu) + diff --git a/tests/xdp/test_xdp.sh b/tests/xdp/test_xdp.sh index fcfd341090..e108e83b26 100755 --- a/tests/xdp/test_xdp.sh +++ b/tests/xdp/test_xdp.sh @@ -21,7 +21,7 @@ DIR="$(dirname "$(readlink -f "$0")")" source "$DIR/../lib.sh" ktap_header -ktap_plan 5 +ktap_plan 6 skip_all() { @@ -31,6 +31,7 @@ skip_all() ktap_skip "xdp detach: callback stops firing and traffic resumes" ktap_skip "xdp attach: refuses a sleepable runtime" ktap_skip "xdp zero-key: a zero-sized key is rejected without a crash" + ktap_skip "xdp percpu: the callback runs on the instance of the receiving CPU" ktap_totals exit 0 } @@ -43,11 +44,12 @@ command -v clang > /dev/null 2>&1 || skip_all "clang not available" cleanup() { bpftool net detach xdp dev "$IFACE" 2>/dev/null - rm -f "${PIN}_pass" "${PIN}_drop" "${PIN}_detach" "${PIN}_zerokey" + rm -f "${PIN}_pass" "${PIN}_drop" "${PIN}_detach" "${PIN}_zerokey" "${PIN}_percpu" lunatik stop tests/xdp/pass > /dev/null 2>&1 lunatik stop tests/xdp/drop > /dev/null 2>&1 lunatik stop tests/xdp/detach > /dev/null 2>&1 lunatik stop tests/xdp/attach_sleepable > /dev/null 2>&1 + lunatik stop tests/xdp/percpu > /dev/null 2>&1 ip netns del "$NETNS" 2>/dev/null ip link del "$IFACE" 2>/dev/null } @@ -150,6 +152,36 @@ zerokey_case() ktap_pass "xdp zero-key: a zero-sized key is rejected without a crash" } +# the veth runs the receive softirq on the sending CPU, so the pinned ping picks the instance +percpu_case() +{ + local title="xdp percpu: the callback runs on the instance of the receiving CPU" + local cpu hits + cpu=$(sed 's/.*[-,]//' /sys/devices/system/cpu/online) + [ "$cpu" -ge 1 ] || { ktap_skip "$title (needs >1 CPU)"; return 0; } + command -v taskset > /dev/null 2>&1 || { ktap_skip "$title (taskset not available)"; return 0; } + + bpftool prog load "$DIR/xdp_percpu.bpf.o" "${PIN}_percpu" type xdp || + { ktap_fail "xdp percpu: failed to load XDP program"; return 1; } + bpftool net attach xdp pinned "${PIN}_percpu" dev "$IFACE" || + { ktap_fail "xdp percpu: failed to attach XDP program"; bpftool prog unpin "${PIN}_percpu"; return 1; } + + mark_dmesg + run_script "tests/xdp/percpu" softirq percpu + check_dmesg || { ktap_fail "xdp percpu: script raised an error"; return 1; } + taskset -c "$cpu" ip netns exec "$NETNS" ping -c 3 -W 2 "$TARGET" > /dev/null 2>&1 + + bpftool net detach xdp dev "$IFACE" 2>/dev/null + rm -f "${PIN}_percpu" + lunatik stop tests/xdp/percpu > /dev/null 2>&1 + + hits=$(dmesg_since | grep -o "xdp percpu test hit: cpu [0-9]*" | sort -u) + [ -n "$hits" ] || { ktap_fail "xdp percpu: the callback did not run"; return 1; } + [ "$hits" = "xdp percpu test hit: cpu $cpu" ] || + { ktap_fail "xdp percpu: expected the instance of CPU $cpu, got: $(echo $hits)"; return 1; } + ktap_pass "$title" +} + run_case xdp_pass.bpf.o "${PIN}_pass" pass.lua yes "xdp pass" \ "xdp pass: verdict enforced, packet and argument content verified" softirq run_case xdp_drop.bpf.o "${PIN}_drop" drop.lua no "xdp drop" \ @@ -163,6 +195,7 @@ lunatik stop tests/xdp/attach_sleepable > /dev/null 2>&1 ktap_pass "xdp attach: refuses a sleepable runtime" zerokey_case +percpu_case ktap_totals diff --git a/tests/xdp/xdp_percpu.bpf.c b/tests/xdp/xdp_percpu.bpf.c new file mode 100644 index 0000000000..fbe04cd818 --- /dev/null +++ b/tests/xdp/xdp_percpu.bpf.c @@ -0,0 +1,23 @@ +/* +* SPDX-FileCopyrightText: (c) 2026 Ring Zero Desenvolvimento de Software LTDA +* SPDX-License-Identifier: MIT OR GPL-2.0-only +*/ + +#include "vmlinux.h" +#include + +extern int bpf_luaxdp_run(char *key, size_t key__sz, struct xdp_md *ctx, + void *arg, size_t arg__sz) __ksym; + +static char runtime[] = "tests/xdp/percpu"; + +SEC("xdp") +int test_xdp_percpu(struct xdp_md *ctx) +{ + int ret; + ret = bpf_luaxdp_run(runtime, sizeof(runtime), ctx, NULL, 0); + return ret < 0 ? XDP_PASS : ret; +} + +char _license[] SEC("license") = "Dual MIT/GPL"; +