lunatik: spawn percpu as per-CPU pinned worker threads - #686
Open
lneto wants to merge 8 commits into
Open
Conversation
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 hook registered by a percpu instance captures the instance CPU id and, at dispatch, accepts packets being processed on any other CPU, so each packet is handled by exactly one instance: per-CPU hook state with no locking, at the cost of N-1 early-outs per packet. The foreign instances return NF_ACCEPT explicitly, not the policy: their transparency is a contract of the affinity design (#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, and netfilter has affinity instead (#675). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`task->on_cpu` tells whether the task is currently executing, not where: `task()` reported 1 on every call. The CPU id comes from `task_cpu()`. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`thread.run` takes an optional cpu and pins the thread to it, which kthread_bind requires between creation and the first wake up, so the call is split out of kthread_run. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`spawn <script> percpu` starts one runtime and one thread per CPU id, each thread bound to the CPU of its instance, so a worker sees a stable `lunatik.cpu()` and owns per-CPU state without locking. Stopping by name drops every thread and runtime, and a thread that fails to start rolls back what the script already created. spawn now trims the script name before registering the thread: it was keying `env.threads` by the untrimmed name while stop looked it up trimmed, so spawning `<script>.lua` left the thread behind. Closes #681. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
lneto
force-pushed
the
claude_percpu_enforcement
branch
3 times, most recently
from
August 8, 2026 10:54
40bdd3a to
e26a81e
Compare
lneto
force-pushed
the
claude_percpu_enforcement
branch
from
August 9, 2026 13:43
e26a81e to
2786065
Compare
lneto
added a commit
that referenced
this pull request
Aug 9, 2026
#686 (per-CPU CPU-pinned kthreads for `spawn`, sleepable) reshapes the per-CPU authenticator: the handshake's keying and PBKDF2 can run inline in a per-CPU worker, so the softirq two-tier split is a fallback, not the premise. Rework the per-CPU sections and open questions to lead with the spawned worker + AF_PACKET fanout candidate, keeping EAPOL visibility and STA affinity as the open, spike-answered questions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
lneto
force-pushed
the
claude_percpu_enforcement
branch
2 times, most recently
from
August 9, 2026 15:11
fdb5ccd to
9e474bc
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Resolves #681, stacked on #678.
spawn <script> percpunow starts one runtime and one kernel thread per CPU id, each thread bound to the CPU of its instance, so a worker sees a stablelunatik.cpu()and owns per-CPU state without locking — the ksoftirqd shape, in Lua. The refusal #676 added was the placeholder for this.Two bugs surfaced on the way and are fixed first, each on its own commit:
thread:task()reportedtask->on_cpu, which tells whether the task is executing, not where: it answered 1 on every call. Withtask_cpu()it reports the CPU, which is also what lets the test prove the binding.runner.spawnkeyedenv.threadsby the untrimmed script name whilerunner.stoplooked it up trimmed, so spawning<script>.lualeft the thread behind. Fixed as part of making the keys consistent for percpu.kthread_bindmust run between creation and the first wake up, sothread.runsplitskthread_runintokthread_create+ bind +wake_up_process. The cpu argument is optional and a plain spawn takes the old path.Test: each thread records whether it is executing on the CPU its instance claims; the check asserts all of them did. Removing the bind makes it fail with "the thread of CPU 0 ran on another CPU". The percpu suite's refusal assertion became "a running percpu script is neither run nor spawned twice".
Tested on 6.8.0-136: full suite twice,
pass:94 fail:0, clean dmesg; builds clean against 5.15 and 6.8.🤖 Generated with Claude Code