diff --git a/Makefile b/Makefile index 162206b..4ce1b53 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ # toolchain.lock) so a generated project stays self-contained. `sync-toolchain` # refreshes both from a tag. TOOLCHAIN_REPO ?= git@github.com:yeet-src/toolchain.git -TOOLCHAIN_TAG ?= v0.1 +TOOLCHAIN_TAG ?= v0.10.1 new: @scripts/new "$(DEST)" $(NAME) diff --git a/template/build/gen-vmlinux.sh b/template/build/gen-vmlinux.sh index a51768b..e217372 100644 --- a/template/build/gen-vmlinux.sh +++ b/template/build/gen-vmlinux.sh @@ -15,5 +15,14 @@ if [ ! -r /sys/kernel/btf/vmlinux ]; then fi mkdir -p "$(dirname "$OUT")" -"$BPFTOOL" btf dump file /sys/kernel/btf/vmlinux format c >"$OUT" +# Mark the generated header as a system header so clang keeps -Wall quiet +# about it. The dump is not warning-clean: the kernel builds with tagged +# anonymous members (`struct foo;` as an unnamed struct/union member), which +# bpftool emits verbatim and clang >= 22 flags with -Wmissing-declarations. +# Nothing in it is ours to fix, and the pragma only affects diagnostics — +# CO-RE relocations and preserve_access_index are untouched. +{ + echo "#pragma clang system_header" + "$BPFTOOL" btf dump file /sys/kernel/btf/vmlinux format c +} >"$OUT" echo "generated $OUT" diff --git a/template/src/bpf/runqlat.bpf.c b/template/src/bpf/runqlat.bpf.c index 0745afd..99051d8 100644 --- a/template/src/bpf/runqlat.bpf.c +++ b/template/src/bpf/runqlat.bpf.c @@ -29,6 +29,29 @@ struct { __type(value, __u64); } runq_hist SEC(".maps"); +// floor(log2(v)) without a loop: a 6-step binary search on the bit width. +// Branch-free and a fixed instruction count, so the verifier has nothing to +// bound — the obvious `while (v >>= 1) slot++` needs `#pragma unroll`, and +// clang (22+) refuses to unroll a loop with an early exit. log2(0) == 0. +static __always_inline __u32 log2u(__u32 v) +{ + __u32 shift, r; + + r = (v > 0xFFFF) << 4; v >>= r; + shift = (v > 0xFF) << 3; v >>= shift; r |= shift; + shift = (v > 0xF) << 2; v >>= shift; r |= shift; + shift = (v > 0x3) << 1; v >>= shift; r |= shift; + r |= (v >> 1); + return r; +} + +static __always_inline __u32 log2l(__u64 v) +{ + __u32 hi = v >> 32; + + return hi ? log2u(hi) + 32 : log2u((__u32)v); +} + static __always_inline void mark_runnable(__u32 pid) { __u64 ts = bpf_ktime_get_ns(); @@ -60,17 +83,8 @@ int on_switch_lat(struct trace_event_raw_sched_switch *ctx) __u64 delta = bpf_ktime_get_ns() - *tsp; bpf_map_delete_elem(&runq_start, &pid); - // slot = floor(log2(delta_ns)) - __u32 slot = 0; - __u64 d = delta; -#pragma unroll - for (int i = 0; i < MAX_SLOTS; i++) { - if (d <= 1) { - break; - } - d >>= 1; - slot++; - } + // slot = floor(log2(delta_ns)), clamped to the last bucket + __u32 slot = log2l(delta); if (slot >= MAX_SLOTS) { slot = MAX_SLOTS - 1; }