Skip to content

lunatik: enforce per-CPU semantics at the registration points - #678

Merged
lneto merged 4 commits into
masterfrom
claude_percpu_enforcement
Aug 9, 2026
Merged

lunatik: enforce per-CPU semantics at the registration points#678
lneto merged 4 commits into
masterfrom
claude_percpu_enforcement

Conversation

@lneto

@lneto lneto commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Resolves #675: the registration points now apply the percpu policy, instead of trusting the script.

The runtime carries its instance CPU id in the state's extraspace, stored before the script is loaded so load-time registrations can read it, and lunatik.cpu() exposes it to the script (on the stub library as well: percpu scripts are usually softirq). runner.run no longer forwards its vararg tail, since a stray extra would silently claim an instance id for a plain runtime.

With the id in place, every registration a percpu instance cannot own is refused at load, with an error naming percpu, through a shared lunatik_checkpercpu helper:

  • constructors whose registration is global: device, notifier, probe, hid;
  • netfilter.register, which has no per-CPU dispatch: each instance would register its own hook and every packet would be handled once per instance. An earlier version of this branch shipped affinity at dispatch instead, each hook accepting everything not on its CPU, but that is exact only where the hook runs in softirq; on the process-context paths a migration mid-chain can handle a packet twice or not at all, so it was dropped rather than merged as a half measure. The exact form, a single hook per script dispatching to the instance of the current CPU, needs group identity in the core and builds on the percpu groups work (lunatik: percpu groups, one object owning one runtime per CPU #693).

syscall was audited and left alone (address lookup only); the eBPF attach and the per-runtime objects need nothing.

Tests, each validated by breaking the code and watching it fail: lunatik.cpu() coverage in the percpu suite (every id stamped, a plain runtime sees nil), and the refusals for device.new and netfilter.register, failing at load in a percpu instance with a clean rollback, with the same scripts running plain.

Tested on 6.8.0-136: full suite green with clean dmesg; builds clean against 5.15 and 6.8 headers.

🤖 Generated with Claude Code

@lneto
lneto force-pushed the pr/claude_percpu branch from 6dbc261 to bec8f20 Compare August 6, 2026 12:44
@lneto
lneto force-pushed the claude_percpu_enforcement branch 2 times, most recently from 5d359c5 to 0f3cefa Compare August 6, 2026 12:59
@lneto
lneto force-pushed the pr/claude_percpu branch from bec8f20 to cb402fb Compare August 6, 2026 13:04
@lneto
lneto force-pushed the claude_percpu_enforcement branch 2 times, most recently from 1a6eba8 to 6ee94ef Compare August 6, 2026 13:43
@lneto
lneto changed the base branch from pr/claude_percpu to master August 6, 2026 13:43
@lneto lneto closed this Aug 6, 2026
@lneto lneto reopened this Aug 6, 2026
@lneto
lneto force-pushed the claude_percpu_enforcement branch 3 times, most recently from 40bdd3a to e26a81e Compare August 8, 2026 10:54
@lneto
lneto force-pushed the claude_percpu_enforcement branch from e26a81e to 2786065 Compare August 9, 2026 13:43
@lneto
lneto requested a review from sneaky-potato August 9, 2026 13:50
Comment thread lunatik_core.c Outdated
int lunatik_runtime(lunatik_object_t **pruntime, const char *script, lunatik_opt_t opt)
{
return lunatik_newruntime(pruntime, NULL, script, opt);
return lunatik_newruntime(pruntime, NULL, script, opt, -1);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we reuse LUNATIK_CPU_NONE here? I suppose -1 here means no cpu instance.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. That call is the one site the sweep missed when the sentinel was named. Fixed: 6b5d4e3f

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, btw.. ;-) thanks!

@sneaky-potato

Copy link
Copy Markdown
Member

Hi @lneto the PR description talks about cpu affinity at dispatch but the code changes refuse percpu mode for netfilter. I think we should update the PR description just so that it remains correct if someone references it in future.

@lneto
lneto force-pushed the claude_percpu_enforcement branch from 6b5d4e3 to fdb5ccd Compare August 9, 2026 15:09
lneto and others added 4 commits August 9, 2026 15:11
The runner passes the instance id to lunatik.runtime(), which stores
it in the state's extraspace before the script is loaded, so load-time
registrations can read it; lunatik.cpu() exposes it to the script, on
the stub library as well since percpu scripts are usually softirq.
runner.run no longer forwards its vararg tail: with cpu as the third
argument of lunatik.runtime(), a stray extra would silently claim an
instance id for a plain runtime.

Groundwork for the registration-point enforcement (#675).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A constructor whose registration is global would be registered once
per instance by a percpu script: N chardevs, N notifier callbacks, N
kprobes on the same symbol. Refuse at load, with an error naming
percpu, through a shared helper adopted by device, notifier, probe and
hid; the eBPF attach and the per-runtime objects stay untouched (#675).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Each instance registers its own hook, so every packet is handled once
per instance, and there is no per-CPU dispatch to hand an instance the
packets of its own CPU. Refuse at load until there is one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@sneaky-potato sneaky-potato left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 💪🏻

@lneto
lneto merged commit 1982f06 into master Aug 9, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

percpu: enforce per-CPU semantics at the registration points

2 participants