Repository navigation
A wake to another CPU that is busy is drained at its next preemption point and runs within a bound - #874
Conversation
|
Design acceptance, recorded by the orchestrator for the owner. He answered "Accept + extra phases": the design is accepted with K4, the cross-process spinner phases that exercise the preemption path, added to the judging measurement, beside K1–K3. A parallel design round on the scheduler's topology (#872) is being redone to the owner's bar of "the best current scheduler theory has to offer". Where that redesign touches |
… run -- --ci host, EXIT=0; --build-only, EXIT=0; the simulator and loom at the head gates/ ran in the branch worktree; final/ ran in a detached worktree at the same head (final/sim-head.txt): the policy suite EXIT=0, loom sleep/push/mailbox EXIT=0, the doorbell-kick-relaxed control EXIT=101 with its verdict, kernel-sim gate 10000 EXIT=1 on overlong_pass alone, as on main.
… within a bound A wake posted to a busy CPU waited for that CPU's next pass, which with a CPU-bound thread running was the 10 ms quantum, and nothing let an idle CPU take it: `Urgency::Normal` sent an IPI only to a SLEEPING target, the woken task sat in the mailbox until the drain, and a lone queued task was no surplus to a steal probe. PR #866's scout (`fbb958b17`) measured it on the T14 and the AMD bench: a ping-pong beside seven spinners waited 10.49 ms per wake at p99 on both, against 2.6 us when the target slept. Four changes in kernel/pure/sched/, the design accepted on PR #874: - The doorbell kicks on its 0->1 edge whether or not the target sleeps. A kick aimed at the poster's own CPU is its need_resched, never an IPI to itself (`KernelHw::kick`, and the simulator's). - At the drain, `CpuSched::place` hands a fair task that would queue behind a running fair task to an idle sibling, as it already handed RT behind RT. An idle sibling is now one SLEEPING with no kick pending: in the simulator a storm's wakes drained in one pass all landed on the one sleeping CPU, and the wakeup_storm means rose by up to half. - A fair task that stays with a key strictly below the running fair task's cuts that task's quantum to end PROTECTED_RUN_NS (1 ms) after its dispatch, or now. Strictly, because threads of one process share a key. The dispatch is a field of its own, where the design read it back off `quantum_end - QUANTUM_NS`: once cut, quantum_end says nothing about the dispatch, and a second owed wake inside the protected run would have cut to now. - One queued task is surplus. The published surplus and the probe's floor count the fair tasks other than the one the CPU still stands on, and PUSH_THRESHOLD is 1. The simulator posts as the CPU whose step it runs, so its kick self-routes as the kernel's does; it counts a kick that lands on a CPU which halted after draining what the kick was for ("late kick") apart from a policy's idle wakes; and it gains three policy cases: a busy home beside an idle CPU, an owed wake on one CPU, and a napping sibling that must not preempt its spinning one. The kick changed the schedules three negative gates stood on: `double_drop_exit_race` spawns a worker just before its teardown so the old steal port still frees an address space under a task in flight (I8), `lend_then_block`'s renewer runs past the lend's window so the old park still keeps a lapsed lend (I9), and the corpus traces are re-cut. `loom_sleep` models the IPI by the need_resched it sets, so the doorbell-kick-relaxed control reds again: with every busy target kicked, the old model's "an IPI was sent" held of every schedule. `acpi_mediated` found its off-boot-processor asker by starting threads until one landed off cpu0; a thread started from its busy CPU now goes to the idle one, which on two CPUs is cpu0 whenever the asker is not, so it asks from itself first. `interactive_mix(2, 16)` crosses invariant I5's derived bound on main as well (90 of 2,000 seeds, 196 with this change); it is recorded as an allowance at that shape and filed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
…der 1 ms `wake_onto_busy` hands a turn back and forth between two threads through park and unpark, beside a spinner on every other CPU, so each wake lands on a busy CPU and is served by the one its waker leaves idle. It times each round trip in userland on the kernel's clock and exits with the p99 in microseconds, as `cyclictest` does. The `wake_onto_busy` metal row rides the `testcases` boot and reds at a p99 of 1 ms, against the quantum a wake to a busy CPU used to wait out. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
…process spinners The wake half of `wt/toyos-scout-sys` at `fbb958b17`, ported: `scout-wake` stamps every ordinary wake in `park::deliver` with its target's class (the poster's own CPU, one that had published SLEEPING, a busy one), the switch onto the task consumes the stamp into a per-class histogram, and `SYS_DEBUG` 0x5C000001 arms and 0x5C000002 logs and resets. `scout_wake` ping-pongs beside 0, n-1, n and 2n spinners of its own process (phases 0 to 3) and, for PR #874's K4, beside n and 2n spinners in a second process (phases 4 and 5), where the pair's share is its own. The metal row `scout_wake` boots it once; `scout_smoke` boots it under QEMU first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
…host EXIT=0, guest suite EXIT=0, --build-only EXIT=0, and every simulator, loom and mutation measurement again at this head gates/ ran in the branch worktree at this head; gates/at-6e323ccf0/ is the guest suite that went red on acpi_mediated before the head that fixes it. final/ and mutations/ ran in a detached worktree at this head (final/sim-head.txt, mutations/results.txt). metal/ is the T14 request for wt/toyos-busywake-judge-base at 5beb7f63a and wt/toyos-busywake-judge-head at b031cfa6c.
a6d8b53 to
c633ee4
Compare
…etal judgement, T14 and AMD bench, base and head
Base is wt/toyos-busywake-judge-base at 5beb7f63ad0950168b3d9f5a93b6a2c9d9f41f4d (main 177e72a9e
+ the wake_onto_busy row + the scout); head is wt/toyos-busywake-judge-head at
b031cfa6c5931dec52d45231ffb6f9822af1ab0a (#874's head + the same scout), as metal/request.txt
states. Every boot's supervisor line names its tree's commit, clean.
metal/judgement/t14/{base,head}: the judge's log (EXIT=0 both), the request, and both boots'
readbacks (scout-wake, testcases). metal/judgement/amd/{base,head}: the --metal-host run's whole
log (EXIT=0 both), its log-partition save's output, and both boots' readbacks. Images are not
committed. Every file is masked: machine identifiers, IPs, MACs, and scratchpad and home paths.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
|
Metal judgement, both benches: the design goes back. K3 fails on both benches, in phases 2 and 3. K2 fails on the T14. K1 and K4 pass on both. The row's negative control is green on base on both benches, because the row never puts a wake on a busy CPU. Setup. The plan and K1–K4 are in
The whole logs, masked, are at Measured or derived. Every number below is measured, copied from a line in those logs, unless it is marked (d). A derived number has its arithmetic beside it. The scout's quantiles are bucket upper bounds, eight buckets to an octave ( K1, the purpose: passes on both benchesPhase 1, head:
K2, the quiet path: fails on the T14, passes on the AMD benchPhase 0:
K3, no idle CPU: fails on both benches, in both phases
Phase 2 is the plain failure.
Phase 3 keeps its rounds. Its p99 is the 4th worst sample of 336 on the T14 and the 5th of 500 on the AMD bench (d: 1 % of n), from one boot each. Both benches moved the same way. K4, cross-process: passes on both benches
The row, and why base is green
Each run is 20,000 rounds beside 7 spinners on 8 CPUs. Head is green, and so is base, on both benches: the negative control fails.
Verdict
The design goes back on K3. Phase 1's purpose and phases 4 and 5 hold on both benches: rounds rise from 500 to 741,789 and 803,709 in phase 1, and from 500 or fewer to 4,983 or more in phases 4 and 5. But with every CPU busy and one process, head halves phase 2's rounds on both benches and raises phase 3's p99. The row has to be restaged before it can stand as the negative control. 🤖 Generated with Claude Code |
…el that leaves the wake there reds The metal judgement found the row green on base on both benches: its pair landed on main's CPU, which no spinner held, because spawn placement rotates from a global start and the partner was the ninth spawn. No timed wake ever reached a busy CPU, so the row held nothing about the busy-wake path. The row now stages what it times and checks it before timing anything: each staged thread reads its CPU's initial APIC id (CPUID leaf 01H, which every x86-64 CPU answers, Zen 2 included) and is kept only where it was wanted, or let go and started again. The partner parks on a CPU other than the waker's and other than APIC id 0, which every device interrupt targets (MSG_DEST), so that no interrupt's pass serves its wake early; a spinner of the same program is then kept on the partner's CPU. The other CPUs are left idle. A kernel that leaves the partner's wake on the busy CPU waits out the spinner's quantum every round; one that hands it to an idle CPU pays that once. The staging thread spins while it waits, so its own CPU is never idle for a candidate, or itself, to be handed to; and the whole arrangement is staged again, up to four times, when a thread moved while it staged. A run that cannot stage, or whose spinner left the partner's CPU, refuses with exit -5. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
Found while reading PR #874's metal judgement: the kernel starts each spawn's least-loaded scan at a global counter, the simulator one past where the last spawn landed, so a spawn burst fills CPUs in a different order in the model than on metal. The judgement turned on exactly that arrangement, and a scratch scenario of the scout's phase 2 never reached it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
… 2 after the metal judgement r2/gates: --build-only EXIT=0, --ci host EXIT=0, guest suite EXIT=0 (61 of 61), at d5165c28a. r2/sim: kernel-sim and loom at 362ab397b (kernel/ identical to d5165c28a's); kernel-sim gate 10000 EXIT=1 on overlong_pass alone, as on main. r2/control: the whole change reverted onto 131970ac8, cargo test -p kernel-sim --no-fail-fast EXIT=101. r2/k3: the one-CPU trio at head and base (every woken wait 10 ms at both), and the scout's phase 2 in the simulator. r2/judge: the judge trees' QEMU smokes and T14 staging at base 4cbf3c381 and head b84bd8157. r2/metal/request.txt: the second metal request, kill lines fixed before any number. r2/scripts: the scratch scripts that ran them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
|
The orchestrator, applying the owner's standing ruling (#885: a design the manufacturers or the published state of the art back is followed, and its bench numbers are reported, never gated; a design nothing published covers is reviewed on merits, and speed decides only when it is the whole purpose):
|
…d head
r2/metal/judgement: the plan r2/metal/request.txt ran, base wt/toyos-busywake-judge-base at
4cbf3c381 and head wt/toyos-busywake-judge-head at b84bd8157, one boot per image per tree.
- t14/{base,head}: the judge log (base exit 1, head exit 0), each boot's flash log, and each
boot's readback (kernel.log, loader.log, boot.txt, verdict.txt, the image's system toml).
- amd/{base,head}: the --metal-host run log (base exit 1, head exit 0) and each boot's readback.
Disk images are left out. Every file is masked: scratchpad and home paths read <scratchpad> and
<home>, and the machine identifiers, IPs and MACs are gone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
|
Second metal judgement, both benches: the gates pass. K1 passes on both benches.
Setup. The plan is
The whole logs, masked, are at
Measured or derived. Every number below is copied from a line in those logs unless it is marked (d), which has its arithmetic beside it.
Base's red is the row, at its staged placement, and nothing else
K1, the gate: passes on both benchesPhase 1, 7 spinners:
K2, reported: holds on the T14; cannot be read on the AMD benchStaged phases 6–8: the pair alone on two CPUs, 2 s each. Each cell gives waker/partner CPU,
K3 staged, reported: holds on both benches
Natural phases 2 and 3, read beside K3: the unstaged regression again
K4, reported: holds on both benches
Verdict
The gates pass: the busy-target kick stands on K1 and on the row. What calls for investigation under the ruling:
🤖 Generated with Claude Code |
|
Review of Net lines, from
BLOCKER
NOTE
SEND BACK |
Three conflicts, all in the simulator #882 reshaped: - kernel/sim/src/hw_impl.rs: the kick keeps this branch's self-route (a kick aimed at the posting CPU is its own need_resched and no IPI) and takes main's `kicked` record of each IPI's target, pushed only where an IPI is sent. - kernel/sim/src/vm.rs: `execute` takes main's probing snapshot, actor, parked set, step trace and level counts, and wraps `execute_inner` in this branch's `posting_from` as before. - kernel/sim/corpus/old_migrate_kept_the_corpse_i14.trace: main's, with its provenance notes. Neither side's decisions replay on the merged tree; the next commit re-shrinks it. At this merge the simulator is red: main's I15 catches the steal's published count, which the next commits fix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
…t stands on would answer Review round 3 of #874 (comment 6126386560), and main's I15 after the merge. The pass published `stealable()`, which leaves out the loaded task it has just put back in the band. Two fair tasks taking turns on one CPU then publish 0 at every quantum-end pass, and that CPU has no other pass, so a CPU that goes idle after the first swap is never probed or pushed. On the merged tree I15 caught it in the lopsided cases at 2, 8 and 16 CPUs ("cpu1 has been idle for 5 rounds while cpu0 holds 1 queued fair task"). The surplus is now `fair_len()`: published state overstates by at most the one task, and never understates. With that, a probe drained in the very pass that switched away from the only queued task was consumed and declined: the re-arm policy's probe gap at two CPUs rose to 24 ms against its derived 13 ms, both re-arms of a run landing on a quantum-end pass. A probe declined only for the task this CPU stands on is now kept, and the pass asks for its next pass (`need_resched`), which, past the switch, hands that task over. `Machine::need_resched`'s contract names the second case. `idle_sibling`'s `answering(now)` term is dead behind `!kick_pending()`, and goes; `running_fair` had one caller, and is inlined. The simulator: - the three drain asserts are exact again (`best == floor`); - the loaded-task unit test now holds that the task is published, the probe kept, the next pass asked for, and the probe answered by it; - main's negative gate for a push one above the probe's threshold stages `PUSH_THRESHOLD + 1` threads for `cpus + 3` quanta, the victim that threshold leaves unannounced, outlasting I15's bound; - `the_push_keeps_an_idle_cpu_beside_work_inside_its_bound` loses its tightness half: each probe now kicks the victim, whose extra pass pushes the next sleeper, so no sampled walk comes within half of the bound (recorded in the next commit's issue); - `group_imbalance`'s doc loses "load no probe takes", false at threshold 1; - the I14 corpus trace is re-shrunk on the merged tree, its provenance notes kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
…nd, and names CPUs by x2APIC id Review round 3 of #874, BLOCKER 2 and 5. The row's partner woke onto the spinner's CPU once: the drain forwarded it to an idle CPU, where it parked, and every later wake found that CPU asleep. The one wake that met a busy CPU fell inside the discarded warm-up, so the row held the forward and nothing reddened without the busy-target kick. Run on a 4-vCPU guest from the judge tree at d5165c2's kernel, unpatched and with the kick reverted (`busy-target-unkicked`), the row read p99 2,586 us and 2,149 us: no slower without the kick. Now every round wakes a partner never woken before. `ROUNDS` partners are staged parked on one CPU, other than the waker's and the device interrupts', before a spinner is kept there; each, woken, says which CPU it ran on and parks again where it ran, which frees that CPU sooner than an exit. On a 4-vCPU guest with exiting partners the row refused, its spinner gone from the busy CPU at the end, with the kick and without it; the reading, derived and not traced, is that both idle CPUs were still busy with exits, a wake queued behind the spinner, and an idle CPU then stole the spinner. The waker spins from the first placement to the last round, so its own CPU is never idle and it is never woken. Without the busy-target kick each wake waits for the spinner's quantum to end; with it, for the kick. `apic_id()` (leaf 01H) goes: the row reads `x2apic_id()` (leaf 0BH), as `acpi_mediated` and `acpi_release` do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
…S3, the threshold-2 staging for S2 Review round 3 of #874. - a-woken-thread-waits-out-a-sibling-spinners-quantum-when-every-cpu-is-busy: the unstaged phase-2 halving and the AMD bench's phase-3 p99 from both metal judgements, and the T14's single 10 ms phase-5 wake, owned by stage S4 of #872 and exiting at its re-run of K1-K4, as the orchestrator's application of the owner's ruling (#885) orders. - a-wake-whose-waker-parks-next-is-forwarded-to-an-idle-cpu: the forward's cost on a synchronous wake, owned by S3, with spawn_cost's reading from the second judgement's readbacks. - the-work-conservation-cases-were-staged-for-a-push-threshold-of-two: the tightness half the topology suite lost, and group_imbalance's premise, owned by S2. - interactive-mix-crosses-the-fairness-bound-at-two-cpus: owned by S4, and its scratch test and output are linked where they are kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
No conflicts. #871 moves the user FP/vector save to the switch and changes the context's identity (`runs`); this branch's kernel side touches neither: `KernelHw::kick`'s self-route sets `need_resched`, which the pass still clears before it drains (`sched::driver::pass`), so the pass this branch's kept probe asks for survives into the next poll. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
… kernel moves it On a 4-vCPU guest at 34d7e18 the row refused at the head and with the busy-target kick reverted alike: its spinner had left the busy CPU by the run's end. A wake that finds no unkicked idle CPU queues behind the spinner, a quantum's end then runs the woken partner and queues the spinner, and the threshold-1 steal hands the spinner to an idle CPU (derived, not traced). That is the balance path doing its work, and the busy CPU is then idle. So before each round the waker reads where the spinner is, and when it has left, stops it and keeps a new one on the busy CPU, up to eight times before the run is refused. Each spinner owns its stop flag and its location word. On the same guest (judge nothing by its numbers): at the head 200 wakes, all run off the busy CPU, no spinner kept again, p50 103 us, p99 380 us; with the kick reverted 200 wakes, p50 12,609 us, p99 past the histogram (exit -3). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
Measured at 1dc0e39 with two marker patches, each panicking at one hand_off site when the task handed on is killed: the forward marker fires ("place forwards the corpse, rt=false"), the steal marker does not and the replay fails I14 as committed. So the re-shrunk replay still stages the path `place`'s comment says this gate stages, as a fair forward where the T14's failure was an RT boost's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
…4cb77ac1d1561dd6 - revert-old-row/: the review's busy-target-unkicked patch against the row as it stood at d5165c28a, on a 4-vCPU guest, from the judge-head tree (b84bd8157, kernel/ = d5165c28a's): unpatched, then patched, then restored. - judged/: every metal row riding the `testcases` boot, judged offline (--metal-readback) from the second judgement's readbacks, T14 and AMD bench, base (4cbf3c381) and head (b84bd8157), each from the tree that built it. - sim/: the simulator on the merged tree before the fix (sim-merged-prefix, the merge of c0cdddf4e into d5165c28a, uncommitted), with the review's published-count fix alone (sim-fix1, scratch-rearm: seed 14's re-arm trace), with the probe kept (sim-fix2), with the topology cases restaged (sim-fix3), all uncommitted trees on the way to e2808f7d0; and scratch measurements at 17c40459d (scratch-at-head: I15's stretch over 200 seeds, the re-arm gap per seed, I5's crossings over 2000 seeds). - i14/: which hand_off path the re-shrunk I14 replay takes, at 1dc0e3989.
…etch linked Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
…4d64ae630e41 - gates/: run.sh in order at a5e247249 — mutations, the simulator (all targets, policy and topology with output), loom and its doorbell-kick-relaxed control, `kernel-sim gate 10000`, `--build-only`, `--ci host`, the guest suite; each log ends in its own EXIT. - mutations/: every mutation as a checked patch, applied, run, restored; its log and results.txt. - control/: the whole change reverted onto origin/main 97f07c381 (whose kernel/pure and kernel/src/hw.rs are ab655a115's) under the head's simulator, with the PROTECTED_RUN_NS shim. - row-smoke/: the restaged wake_onto_busy row on a 4-vCPU TCG guest at a5e247249 through a scratch harness entry (smoke-harness.patch), unpatched and with busy-target-unkicked; numbers are a guest's.
…EVDF whole, MONITOR's erratum keeps the kick Round 3's review (#887, 6130446811) and the orchestrator's rulings on it (6130566783): - The TSC deadline: the twin and its clock control go. The exit reads the split again on the T14 after the change, as the MWAIT stage's does, and reports the 1 ms timer lateness with the count and with the deadline, where round 3 held it "no worse than the count's". - The busy wake's 8-spinner arm follows EEVDF whole: v6.6 curbs wakeup preemption too (63304558ba5d, RUN_TO_PARITY, on by default at v7.2), so that arm's test is a woken task running within one protected slice, 1 ms (#874's PROTECTED_RUN_NS), and its p99 against 100 us is reported. The 7-spinner arm keeps the 100 us bound as its test. - MWAIT idle: a CPU whose MONITOR loses wakes keeps the kick, as Linux keeps it. c9a4b55431e5 (v6.13, Intel's Len Brown) adds Lunar Lake to Goldmont in X86_BUG_MONITOR; at v7.2 such a CPU's MWAIT idle sets no polling flag, so each wake of it is an IPI. - APST: blockd honours NVME_QUIRK_NO_APST and NVME_QUIRK_NO_DEEPEST_PS, the drives whose APST failed, which round 2 had dropped with Linux's latency policy. No reading in the tree names the T14's drive, so whether it is one of them is unread. - fsgsbase-is-taken: CR4_FORBIDDEN is at control_regs.rs:149-153, and gsbase_locked and its user-writable-gsbase kernel, which 29e1006 cut, are named as stage C of the guest-suite track's. The track's TSC-deadline bullet drops its twin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
The busy-target kick's own-CPU arm set the poster's need_resched, so a futex_wake that woke tasks parked on the waker's CPU ran a pass before the syscall returned, placing each of them. On the T14 the 64-waiter wake_storm_cost walk read 132,867-153,899 cycles in the three boots that carried the kick against 52,910-74,328 in fifty without it, and round 3's boot crossed the row's linear bound (16 -> 64 waiters, 26,331 -> 132,867). On an 8-vCPU guest the storm syscall held about one such pass at 64 waiters and none with the kick reverted, and the pass's time went to the process's fair-share lock, which the storm's other waiters were taking as they re-parked. park::deliver now posts an ordinary wake whose target is the poster's own CPU without ringing: that CPU's own next pass drains it, as on main, and no edge stands with no pass owed to elide other CPUs' kicks to it. A boosted wake still rings and kicks. Kicker gains runs_on, which the kernel answers from its CPU id and the simulator from its poster. The wait this leaves a self-posted wake is filed for #872's S3: issues/an-ordinary-wake-posted-on-its-own-cpu-waits-for-that-cpus-next-pass.md. Two AMD-bench gaps the whole-suite run showed and nothing recorded are filed: issues/the-hard-lockup-detector-samples-no-amd-cpu.md and issues/no-watchdog-arms-on-an-amd-machine.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
sim-runs-on-never (runs_on always false) stayed green under every scenario; this holds the model's answer the core's unrung self wake reads. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
… round 4 head The head fa3a31a with park::deliver ringing and kicking a wake whose target is the poster's own CPU, as round 3 did: the metal control for wake_storm_cost. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
|
Accepted: the change to decision 1 at |
|
Review of Net lines (
Earlier BLOCKERs
The request's STORM line, from the kernel logs
BLOCKER
NOTE
SEND BACK |
No simulator verdict rests on the model's runs_on (sim-runs-on-never stayed green under every scenario), so the test guarded a line a reader checks and nothing behavioural. This restores hw_impl.rs to fa3a31a. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
Every device handler that posts a wake sets need_resched before its EOI (idt/user_dev.rs, idt/isa.rs, idt/xhci.rs, drivers/hda.rs, idt/kick.rs), so only a thread that keeps running leaves a self-posted ordinary wake waiting. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
The AMD bench's smp_roster_and_tsc_trail row is red on this branch as on main: its MADT names 16 CPUs and 8 come up. The issue is #865's own file, byte for byte, so the red is recorded whichever lands first and the second merges cleanly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
…nd 4 (ToyOS head 4118fbbeb794c52fb146d9f4916bb97b319cb06f, control 15d0885593a853a07c2e1c56efe0e6d62350a7dc) and round 3 (ToyOS head a5e247249552728692757ccfc6754d64ae630e41, control aa7b41581), whole, masked
…e4b76725cc2550a37f: --ci host, kernel-sim and --build-only, masked
|
Review of Net lines ( The merge is clean. Earlier BLOCKERs
Earlier NOTEs
The guest suite on the merge. The merge queue's own run is enough here, and I raise no BLOCKER:
BLOCKER None. NOTE
LAND |
Applies the orchestrator's rulings on round 5's review (#887 (comment)): - MWAIT idle is Intel's alone; the AMD bench keeps hlt and the kick, and its half is the orchestrator's once its _CST is read. No FFH reading. - The Goldmont-only verdict table goes. The Core Ultra 200V specification update is recorded as unread, and the exit reads it: a 2020+ Intel MONITOR or MWAIT erratum brings the verdict and its host test back with that row. - Per-CPU lines on AMD stand on ToyOS's rule over AMD's documented line (PPR 55922 CPUID_Fn00000001_EBX[15:8], 55723 section 2.6.1); "documents the cost" goes. - The T14's MONITOR/MWAIT and TSC-deadline support no longer rest on a capture taken under another OS: both exits gain a boot line printing CPUID.01H:ECX, cited once a T14 boot carries it. - The twin's "measures better" is named: 11 runs per arm, each run's median 1 ms timer lateness, the median of 11 with its spread, and overlapping spreads keep the count. - The track's comparisons against Ubuntu leave for the evidence; it states ToyOS's own readings. - The 7-spinner arm says it does not take Lozi et al.'s power-policy condition, and the 8-spinner arm says it is blocked on #874's PROTECTED_RUN_NS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
tests/toyos.rs: the testcases metal arm keeps #874's test_rs_wake_onto_busy and drops test_rs_audio_idle_suspend and its comment, which this branch deletes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
It brings #874, #896 and #887; none moves a path the record cites. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
A wake posted to a busy CPU waited for that CPU's next pass, which with a CPU-bound thread running is the 10 ms quantum, and no idle CPU could take it. This branch builds the design accepted in the acceptance comment, with K4 added to the metal judgement. It takes up
issues/a-wake-to-a-busy-cpu-waits-for-its-next-pass-and-no-idle-cpu-can-take-it.mdand does not close it: that exit also asks for the case where all eight CPUs run spinners of one process, which this design leaves to the fair band (roast 1) and whichissues/a-woken-thread-waits-out-a-sibling-spinners-quantum-when-every-cpu-is-busy.mdnow records for S4.Head
0ab013e77onmainb680a973b. Evidence is onToyOSOrg/evidencebranchpr-874:d170207holds this round's gates;4a6b534the metal readbacks and judge logs of rounds 3 and 4, whole and masked;ba98b45holds this round;c940595round 3;d63b878the second metal judgement;cfb4aa7the first;20056ccround 2;b036b48the investigation and the first whole-change control.Where this stands
wake_onto_busygate it. The second judgement passed both gates on both benches.a5e247249: the T14 passed 300 of 301, red onwake_storm_cost(judge log, exit 101); the control, the kick reverted, was red onwake_onto_busyat p99 10,001 µs on both benches (below). The AMD bench passed 287 of 301 (log, exit 101). Round 4 found the T14's red and fixed it.4118fbbeb, whose kernel this head carries unchanged but formain's x86-64 kernel leaves are global under a declared CR4.PGE, and only a kernel-half change drops them #873: the T14 passed 300 of 300; the AMD bench passed 286 of 300, red on the 14 rows tabled below and on nothing else. Every line stated before the run holds (## Metal: judged). Round 5 deleted one host test, corrected an issue, filed one and mergedmain, and owes no new metal run.Round 4: the T14's
wake_storm_costred, and the AMD bench'sRoot cause: the self kick ran a pass inside the waker's syscall
futex_wakeof 16 waiters cost the waker 26,331 cycles and one of 64 cost 132,867, past the row's linear bound of five times.Doorbell::ring.need_resched. On an 8-vCPU guest the storm syscall was instrumented (patches, scripts and logs; a guest's numbers decide nothing):need_reschedremoved it was 63,000 to 129,000 over four.post_nitself), cycles barely moved between the head and the revert.exit_to_userand places every waiter parked on the waker's CPU. From the walk's end to the syscall's exit took a mean of 704,300 cycles at the head against 19,200 reverted. In that span the waker's CPU received 0.2 kick IPIs on average and switched no task.mainthe same placements run in the waker's next pass, at its block, after the burst.fix-*.log).## Metal: judged).issues/an-ordinary-wake-posted-on-its-own-cpu-waits-for-that-cpus-next-pass.md.The AMD bench's 14 reds
Round 3's judge lines and one boot line each names are excerpted here. Round 4's AMD judge log (whole, 117,795 lines) carries the same 14 FAIL lines, row for row, and no other.
main's record of the AMD bench.mainhas no whole-suite run of the AMD bench anywhere.~/.cache/toyos/metal/, one file per machine) holds timings only, and no verdicts.ToyOSOrg/evidenceholds no whole-suite AMD run on any branch.mainrecords are its issues, from PR The metal suite targets a second bench: --metal-host, every machine's record in the host's store, the home read off the machine, and the stick read off the record #852's AMD boots, and round 3's offline judgement of the second judgement's basetestcasesboot. That boot ismainplus a scout, and it was red on 6 of its 19 rows (log).Every red names hardware or firmware the bench has and the T14 has not, or lacks. None is a scheduling outcome:
main's basehda_tone,hda_client_stallthe-amd-bench-answers-on-two-hda-controllers-and-plays-on-the-null-sink.mdacpi_server_events,acpi_tables_loaded,battery_readacpiserver-refuses-the-amd-benchs-dsdt-for-a-read-in-memory-mapped-io.mdacpi_server_deathACPI_ENABLE 0xf0toSMI_CMD 0xb2; the bench wrote0xa0to0xb0a-metal-row-names-no-hardware-and-a-bench-without-it-reds.mdclaim_reuses_its_remapping_entry,claim_refused_without_remapping8086:15fc):NotFounda-metal-row-names-no-hardware-and-a-bench-without-it-reds.md, which names bothusbd_drives_the_type_c_controllera-metal-row-names-no-hardware-and-a-bench-without-it-reds.mddomain_ends_below_the_host_bridgesan-amd-vi-machine-isolates-no-device.mdcounterspm_enable=1, HWP's; the bench declares no performance request ("no HWP")the-perf-state-declaration-refuses-hybrid-intel-and-amd-cppc.mdsmp_roster_and_tsc_trailMAX_CPUSis 8the-kernel-brings-up-at-most-eight-cpus.md, byte for byte the file open #865 adds, so the red is recorded whichever lands first and the second merges cleanlyloader_watchdog_armsno-watchdog-arms-on-an-amd-machine.mdhard_lockup_ends_a_deaf_cputhe-hard-lockup-detector-samples-no-amd-cpu.md"Not run there" means the base boot that was judged did not carry that row: it was the second judgement's filtered
testcasesimage, or another boot. Each of those rows' causes is a CPUID leaf, a PCI function, an ACPI table or a row's needle, which this branch's scheduler does not touch.Round 3: the review's findings (at
a5e247249)BLOCKER 1, the S4 issue: filed.
issues/a-woken-thread-waits-out-a-sibling-spinners-quantum-when-every-cpu-is-busy.mdcarries both judgements' phase-2 halving (500 → 250 rounds, every head wake one quantum on the waker's CPU, both benches), the AMD bench's phase-3 p99 (≤ 10,485,759 ns at base; ≤ 15,728,639 and ≤ 12,582,911 ns at head), and the T14's one 10,002,218 ns phase-5 wake. Owner: #872's S4. Exit: S4's re-run of K1–K4, as the ruling orders.BLOCKER 2, a gate that reds without the kick.
b84bd8157, whosekernel/isd5165c28a's), the row read p50 279 µs, p99 2,586 µs unpatched and p50 74 µs, p99 2,149 µs withbusy-target-unkicked(unpatched, reverted, script; both smokes EXIT=0). No slower without the kick: the review's reading holds. Metal was not asked.a5e247249(head, reverted, through a scratch harness entry): 200 wakes each, all run off the busy CPU, no spinner kept again; p50 267 µs, p99 1,398 µs at the head, p50 14,998 µs, p99 16,556 µs with the kick reverted. A guest's numbers decide nothing. On metal, round 3's control (aa7b41581, the kick reverted) was red onwake_onto_busy: p99 10,001 µs on the T14 (judge log, exit 101; kernel log) and on the AMD bench (log, exit 101), where every head since reads p99 5 to 25 µs.BLOCKER 3, the steal's published count.
main's I15 caught exactly the hole the review describes: "cpu1 has been idle for 5 rounds while cpu0 holds 1 queued fair task(s) (bound 4 rounds)", in the lopsided cases at 2, 8 and 16 CPUs (log, EXIT=101).fair_len()and answers probes fromstealable(), and the three drain asserts are exact again (best == floor). With that alone, I15 held (log). At this head the asserts hold: drained 7/7, 15/15, 63/63, 63/63 (log).need_resched), which, past the switch, hands that task over. At this head the gap is 10 ms worst over 20 seeds (log). Roast: it is a second reason for a pass to ask for a pass, and it costs one more pass where it fires; the alternative, consuming the probe, leaves the pull path's recovery to the push alone.main's topology suite at threshold 1. Two of its cases were staged forPUSH_THRESHOLD2:PUSH_THRESHOLD + 1threads forcpus + 3quanta, the victim that push leaves unannounced, outlasting I15's bound;the_push_keeps_an_idle_cpu_beside_work_inside_its_boundloses its tightness half. Each probe now kicks the victim, whose extra pass pushes the next sleeper, so over 200 seeds the worst stretch is 5 rounds against 10 on the T14's shape and 6 against 18 on the AMD bench's (log). The bound is still the derivation's worst case.group_imbalance's neighbours now hold stealable work, so it no longer stages an idle CPU whose CCX holds nothing a probe takes.issues/the-work-conservation-cases-were-staged-for-a-push-threshold-of-two.md, owned by S2.cargo test -p kernel-simat this head: EXIT=0 (log).BLOCKER 4, the rows riding the
testcasesboot, judged offline from the second judgement's readbacks with--metal-readback, each from the tree that built it, under a scratchHOMEwhose metal record is a copy, so no judging here wrote the host's record (script):4cbf3c381b84bd8157wake_onto_busyred (log)wake_onto_busy. They arehda_tone,hda_client_stall,smp_roster_and_tsc_trail(8 CPUs given, 16 in the MADT),claim_reuses_its_remapping_entryanddomain_ends_below_the_host_bridges: hardware the bench lacks,issues/a-metal-row-names-no-hardware-and-a-bench-without-it-reds.md. Base addswake_onto_busy.spawn_costmedian 1,025 → 1,029 µs (T14), 2,054 → 2,086 µs (AMD);syscall_cost651 → 648 cycles (T14), 388 → 388 (AMD);audio_tone, soundserver's worst wake latency was 212 and 600 µs at base and 223 and 427 µs at head on the T14, with 0 underruns at both;wake_storm_costper waiter: 386 → 976 ns on the T14 and 151 → 328 ns on the AMD bench. Its row asserts only the walk's linear shape, and passes. Read from the code, not traced, that is the busy-target kick's cost to a waker: a busy target the walk reaches is now sent an IPI on each 0→1 edge, where base elided it. It is reported, not gated. Round 4 traced it to the self kick, which is gone; at this kernel the T14 reads 401 ns (## Metal: judged).## Metal: judged).BLOCKER 5:
apic_id()is deleted; the row readsx2apic_id()(leaf 0BH), asacpi_mediatedandacpi_releasedo, andarch/cpu.rsismain's again.NOTEs.
interactive-mix-crosses-the-fairness-bound-at-two-cpus.mdis owned by S4, and measured again at17c40459d, whosekernel/is this head's: 197 of 2,000 runs cross, worst 254 ms against 217 ms.idle_sibling'sanswering(now)term is deleted. It was dead behind!kick_pending(), whose standing edge is what now refuses a stopped CPU after its first forward.answering's contract says so.placement-ignores-stalenessstill redsa_stopped_cpu_stops_taking_workunder--ci host.running_fairis inlined intoplace.issues/a-wake-whose-waker-parks-next-is-forwarded-to-an-idle-cpu.md, owned by S3, withspawn_cost's reading.busy_homebounds that at two hops.What changed, per decision
1. A busy target is kicked on the doorbell's edge (
mailbox.rs,Doorbell::ring).Urgency::Normalsends on the 0→1KICK_PENDINGedge whether or not the target is SLEEPING. A target already owed a pass costs nothing more. An ordinary wake posted on the CPU it goes to rings nothing (park::deliver, new this round): that CPU's own next pass drains it, as onmain. A boosted wake there still rings, and a kick aimed at the poster's own CPU sets itsneed_reschedand sends no IPI (KernelHw::kick,SimHw::kick).Kicker::runs_onanswers whose CPU the poster is: the kernel from its CPU id, the simulator from its poster.2. At the drain, a fair task that would queue behind a running fair task goes to an idle sibling (
CpuSched::place), the RT-behind-RT forward extended to the fair band.idle_siblingtakes a CPU that is SLEEPING and has no kick coming, so a burst drained in one pass spreads over the idle CPUs.3. A fair task that stays, with a key strictly below the running fair task's, cuts that task's quantum to end
PROTECTED_RUN_NS(1 ms) after the running task's dispatch, or now if it has run that long. Strictly, because threads of one process share a pot and so a key.4. One queued task is surplus (
PUSH_THRESHOLD1). The pass publishes its whole fair band, which overstates by at most the task it still stands on. It answers probes fromstealable(), the band less that task. A probe that only that task could answer is kept, and the pass asks for its next pass, which answers it past the switch (Machine::need_resched's second case).Where the build departs from the accepted design
dispatched_at), because a cut movesquantum_end.a_second_owed_wake_does_not_cut_inside_the_protected_runholds it, anddispatch-from-quantum-endreds it.idle_siblingrefuses a CPU with a kick pending.a_burst_of_wakes_forwards_one_per_idle_siblingholds it, andforward-onto-kickedreds it.Published backing, per the ruling
The forward, the threshold-1 steal and the protected preemption follow the work-conservation, stealing and preemption results #872 cites, and their bench numbers are reported. The forward's case is the hops above, not enqueue-time choice. The busy-target kick has no cited source; K1 and the row gate it.
The simulator, loom and the harness
a_wake_to_a_busy_cpu_beside_an_idle_one_runs_within_two_hops(busy_home),a_wake_owed_the_cpu_waits_out_the_protected_run_and_no_more(interactive_mix(1, 1)),a_sibling_that_naps_does_not_preempt_the_one_that_spins(sleep_spin_siblings).Vm::late_kick_wakes).double_drop_exit_race,lend_then_block,a_pass_inside_the_registration_window_is_caught, the corpus traces, and this roundmain's two topology cases above.main's per-level IPI record, which counts only kicks that send one.loom_sleepmodels the IPI by theneed_reschedit sets. Under it, thedoorbell-kick-relaxedcontrol reds.mainas well:interactive-mix-crosses-the-fairness-bound-at-two-cpus.mdthe-simulators-gate-command-waits-for-overlong-pass-to-abort.mdthe-simulator-rotates-spawn-placement-unlike-the-kernel.mdthe-kernel-brings-up-at-most-eight-cpus.md, above.wake_onto_busy, above, rides thetestcasesboot and reds at a p99 ≥ 1000 µs. That bound is a tenth of the quantum a wake left behind the spinner waits. The row is inRUST_SKIP, because its verdict is a duration.No new QEMU guest test. Every decision is reached by a
SchedPasshost test or a simulator case, and every timing claim belongs to metal.acpi_mediatedandfutex_wake_countschanged in round 1 and not since.acpi_mediatednow asks from itself first when it is already off cpu0, because a thread started from its busy CPU goes to the idle one. The ACPI fixed hardware's mediated access is only a guest's to reach.Checks
Negative control: the whole change reverted onto
main.origin/main'skernel/pureandkernel/src/hw.rswere put under this head's simulator, with a shim declaringPROTECTED_RUN_NS.cargo test -p kernel-simEXIT=101 (log, script). Six cases red:busy_home: "only 0 of 40 runs handed the sleeper to the idle CPU";The row's control is metal's, and it reds: round 3's control, the kick reverted, read
wake_onto_busyp99 10,001 µs on both benches (T14, AMD, each exit 101).Mutations at this head (results, patches and logs beside it). Each is a checked patch, applied, run and restored. Every one exits 101, red:
surplus-stealableprobe-not-kepta_probe_for_the_task_the_cpu_stands_on_is_answered_a_pass_later; sim re-armstrict-to-leq<becomes≤a_wake_that_ties…; sima_sibling_that_naps…dispatch-from-quantum-enda_second_owed_wake…no-fair-forwarda_wake_to_a_busy_cpu_is_handed…; simbusy_homeno-cuta_wake_with_a_lower_key…; sim owed wakebusy-target-unkickeddoorbell_kick_policy; sim owed wake; simbusy_homelone-task-not-surplusa_lone_queued_task_is_published…loaded-counteda_probe_for_the_task…forward-onto-kicked!kick_pendingterma_burst_of_wakes…sim-no-self-routelate-kicks-uncountedthe_two_cures…No behavioural test reaches the simulator's self-route, and none reaches the kernel's (
KernelHw::kick).Mutations of round 4's fix (patches and logs, script), each applied, run and restored:
self-wake-rungan_ordinary_wake_posted_on_its_own_cpu_rings_nothingEXIT=101; sim EXIT=0boosted-self-unrunga_boosted_wake_posted_on_its_own_cpu_still_kicksEXIT=101; sim EXIT=0every-wake-unrungbusy_home, the owed wake, the band wait, the storm, the cures)sim-runs-on-neverruns_onalways falsefa3a31a06)self-wake-rungis green in the simulator: no scenario's verdict rests on a self-posted wake's timing, and none on the model'sruns_on, a one-line accessor read at its site. The fix's control is metal's, and it reds where the line said: the T14's 64-waiter storm reading at the control is 168,679 cycles, above 74,328 (## Metal: judged).Independent oracle: the T14 and the AMD bench; and loom, the third-party model checker, for the ordering.
Gates
At
0ab013e77(r5/gates/, script, whose run log stamps the head and the tree clean before and after), onmainb680a973b:cargo run -- --ci host: EXIT=0 (log).cargo test -p kernel-sim: EXIT=0 (log).cargo run -- --build-only: EXIT=0 (log).cargo test, atfa3a31a06: EXIT=0, 62 of 62 (log). Since then the branch changed one host test and two issues and mergedmain's x86-64 kernel leaves are global under a declared CR4.PGE, and only a kernel-half change drops them #873; the guest suite was not run on the merge.loom_sleep,loom_push,loom_mailbox) were not repeated: round 4's change to them is theruns_oneach model's kicker answersfalse, and--ci hostbuilds them.Metal: judged
The orchestrator ran round 4's request on both benches; every readback image hashed to the request's sha256. Each judge is
cargo test --test toyos-build -- --metal --metal-readback <readback dir>(with--metal-host <user>@<amd-bench>on the AMD bench, and the rowwake_storm_costfor the control), from the tree that staged it: head4118fbbeb, control15d088559, the head's kernel withself-wake-rung. Exits are cargo's, read from each log's own trailer.wake_storm_costThe lines, stated before any number, against what the boots read:
mainbase; 161 ns against 196 ns on the AMD bench, and 151 ns at that base.wake_onto_busy, holds: green at the head on both benches. T14 p99 5 µs, 200 of 200 run off CPU 5, the spinner kept there again 0 times; AMD bench p99 25 µs, 200 of 200 run off CPU 6, 0 times.Round 3's run is published beside it: T14 head (300 of 301, red on
wake_storm_cost, exit 101; itstestcaseskernel log), T14 control, AMD head, AMD control. The orchestrator's boot lists arem874-r4-t14.txt,m874-r4-amd.txtandm874-r3-t14.txt; masking leaves their paths as<path>. Every published file was masked with the orchestrator'smask.shand counts 0 for the vendor, model, firmware, host and home-path names it checks.Net lines
git diff --shortstat origin/main...HEAD: 42 files, +1,704 −393.git diff --numstat):cpu.rs+376 −63, of whose additions 214 are in its test module;mailbox.rs+27 −15, 8 of them tests;park.rs+51 −2, 38 of them tests;queue.rs+5;sched/hw.rs+9 −3;kernel/src/hw.rs+10 −6.runs_on.What I am unsure of
The unrung self wake changes decision 1 as accepted: that kicked the poster’s own CPU, and this round stops it for ordinary wakes and files what it leaves. It was accepted in 6134663906.
Why the self pass grew faster than the storm on metal. The share-lock reading is the guest's; the T14's split is unread.
The kept probe answers a red the review’s fix opened. Its provisional acceptance was upheld by round 4.
What the kick to other CPUs costs a waker.
wake_storm_costper waiter reads 401 ns at the head against 386 ns atmain's judged base on the T14, and 161 against 151 ns on the AMD bench, one boot each; the T14's 64-waiter reading is inside the 50 kick-free boots' range, so one boot does not separate them. The kick's case is K1 and the row.How the head reached the unstaged trio on metal. The readbacks show the end state and not the path.
The sync wake (filed).
Whether the audio rows reach an RT wake behind running RT with only kicked sleepers.
Late kicks on the audio workload are unmeasured on metal.
busy_homemeasures only the half of its runs whose cpu1 ever published SLEEPING.From the design: C, a preemption's cost, is unmeasured, so M is a policy number.
Roast
main's 386, one boot each; round 3's 2.5× was the self kick, now gone.sleep_spin_siblings) bounds a model granularity, not a metal number.PUSH_THRESHOLDwas the parameter of an owner decision. At 1,main's topology suite lost a tightness check and a staging (filed for S2).idle_siblingis still an O(CPUs) scan, and it refuses kicked CPUs.main; an RT thread woken without a boost is among them. A device interrupt that posts such a wake setsneed_reschedbefore its EOI, so the CPU it interrupted runs that pass on the way out. Filed:issues/an-ordinary-wake-posted-on-its-own-cpu-waits-for-that-cpus-next-pass.md.🤖 Generated with Claude Code
https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy