__sink(9)

πŸ“¦ version 7.3.1, in nixpkgs unstable

✨ __sink(9) ✨

okay bestie, welcome to the Kernel API Manual entry for __sink β€” and no, it does not mean what you think it means, it's not about your dishes, it's about hiding stuff from the compiler 😌

πŸ’– NAME πŸ’–

__sink β€” hide @expr's value from the compiler and the BPF verifier 🫢

πŸ¦‹ SYNOPSIS πŸ¦‹

__sink (expr );

πŸŽ€ ARGUMENTS πŸŽ€

  • expr β€” the expression whose value should be opacified πŸ’… (yes, "opacified", that's the actual word, we love a vocabulary moment)

🌸 DESCRIPTION 🌸

So here's the tea: this thing is a no-op at runtime. literally does nothing when the program actually runs. but the empty inline assembly with a read-write constraint ("+g") is doing main character energy work at compile/verify time, and it has exactly two effects:

  1. Compiler: it treats expr as both read and written, which prevents dead-code elimination and keeps expr (plus any side effects that produced it) alive. so it can't get ghosted 🩷
  2. BPF verifier: it forgets the precise value/range of expr β€” it "makes it imprecise". The verifier normally tracks exact ranges for every register and stack slot. Which is useful, sure, but precision means each distinct value creates a separate verifier state. And inside loops? that's where it gets ugly β€” state explosion πŸ’€ every iteration carries different precise values, so states never merge and the verifier ends up exploring every single iteration individually. not slay.

🧚 example β€” preventing loop state explosion

u32 nr_intersects = 0, nr_covered = 0;
__sink(nr_intersects);
__sink(nr_covered);
bpf_for(i, 0, nr_nodes) {
    if (intersects(cpumask, node_mask[i]))
        nr_intersects++;
    if (covers(cpumask, node_mask[i]))
        nr_covered++;
}

Without __sink, the verifier tracks every possible (nr_intersects, nr_covered) pair across iterations, and you get hit with "BPF program is too large" 😭 With __sink, the values become unknown scalars, so all iterations collapse into one reusable state. iconic behaviour, honestly πŸ‘‘

πŸ“ example β€” keeping a reference alive

struct task_struct *t = bpf_task_acquire(task);
__sink(t);

Follows the convention from BPF selftests (bpf_misc.h) ✨

🌈 SEE ALSO 🌈


πŸ’œ and that's the whole page, bestie πŸ’œ this lil' gem lives at ./tools/sched_ext/include/scx and it's dated October 2026 β€” go forth and sink responsibly, mwah πŸ«ΆπŸ’…

πŸ“¦ where this page comes from