Skip to content

SMP boot dies silently under WM traffic: the extra core-0 switch is survivable, the death is not guest-side #1261

Description

@drawmeanelephant

Claim

  • Owner: buffy (agent/buffy/1261-probes)
  • Scope: Find the mechanism behind the silent death of the SMP boot under WM traffic: with the Forensics recorder: a dying boot names its own last instant (#1278) #1281 recorder landed, instrument exception entry (before GIC consumption), spurious acks, comparator re-arms, and a console-free liveness sample; re-run the dying boot until it names its own last instant; and settle "the PPIs stopped being delivered" vs "the notes stopped being reached" as measured evidence. Then either fix it or bound it in writing.
  • Touches: kernel/src/scheduler.zig,kernel/src/timer.zig,kernel/src/exceptions.zig,kernel/src/main.zig,kernel/src/monitor.zig,kernel/src/shell.zig,kernel/src/forensics.zig,kernel/tests/scheduler_test.zig,kernel/tests/monitor_test.zig,tools/gate/specs/live-wm-pacing.spec,tools/gate/specs/live-forensics.spec
  • Depends on: claim: Forensics: a recorder that survives the guest dying, so a boot names its own last instant (#1261 unblock) #1278 (landed — the recorder this was blocked on)
  • Verification: live-forensics PASS with every probe pinned; the death's mechanism stated as observed evidence (trace + VZ verdict), not a hypothesis.
  • Status:
  • Blocked on: nothing mechanical — the recorder landed (Forensics recorder: a dying boot names its own last instant (#1278) #1281) and the probes measure clean (PPIs delivered AND recorded to the last second; VZErrorDomain code=1, so VZ aborts the VM rather than the guest faulting). What remains is diagnosis: no handler is entered on the death and VZ offers no reason beyond code=1, so the open question is which guest action makes VZ abort, and whether the abort can be turned into an observable.

⚠️ Measured 2026-09-14 — the premise of everything below is wrong

Read the measurement comment before this document. Three instrumented class-B boots overturned the causal claim in the title and the "Starvation, not a crash" line below:

  • The boot does not starve. A failing run prints 451,721 smp: secondary runs= lines and the shell prints every one (drained == staged), then leaves that drain and proceeds into script2. It is not stuck in the drain and not starved.
  • The staging storm is survivable. With script2 withheld to t=120 s the guest survives 120 s inside a 489,187-line staging storm with the nudge armed the whole time — and dies the instant the first dui command lands. The trigger is the script2 WM traffic, not the backlog and not time.
  • The nudge measures clean. 19–21 requests per 20 s, coalesced=1, all on core 0 (nudge c1=0); ticks=20 at cntpct=486,449,908 = 20.27 s, so 1 Hz exact. The extra rotation is one per second: period tick idle → shell, then ~2.4 ms later the nudge shell → user-exec.

So there are two symptoms where this card assumed one, and only one of them is fatal. The text below is kept as the record of how the premise was reached and is not to be relied on where it conflicts with that comment.

Summary

An extra preemption/context switch on core 0 during an SMP desktop boot starves the boot. The guest does not fault — it starves. This is the defect underneath #1252's reschedule nudge (815 ms → 2.5 ms input→present), and it sits in front of anything else that changes when core 0 switches: the WFE-with-tick-wake idle experiment recorded under #122 (closed; still cited by kernel/src/shell.zig:4028 as "the recorded option"), and any change to the tick period or the quantum.

It is filed as its own card because the nudge is a latency win blocked by a defect that is not about latency, and because nothing in the current design exercises a core-0 switch off the period boundary — which is why it went unnoticed.

Symptom (observed)

live-wnd5-gate2-policy — SMP, WND + NOTEPAD, dui orientation chords:

tree result
current kernel, re-measured 2026-09-14 PASS (2/2 runs)
with the nudge applied (#1255, bc89e9c) FAIL (0/4 attempts)

The failing boot does not fault: no [EXC] parking:, no tombstone, timer heartbeat ticks=N irq=N exact throughout. The WM keeps presenting on ticks while the shell stops completing dui commands, and the boot ends in VZVirtualMachine.State.error (state=3). Starvation, not a crash.

Baseline line from today's run, in order:

gate-run: host 27.0/arm64; kern.hv_support=1 hv_vmm_present=0; hv_vm_create -> 0x00000000 (HV_SUCCESS)
vgate live-wnd5-gate2-policy: PASS (2/2 runs)

Bisection (observed)

Each row is the probe, then the gate result.

probe result
baseline origin/main PASS 3/3
timer.nudge() disabled entirely (flag bookkeeping + tick(period) signature kept) PASS 2/2
comparator pulled forward, but tick returns before the rotation PASS 1/1
full rotation on a nudge FAIL 0/4
rotation skipped when the successor is the WFE-parked idle fallback FAIL 0/2
targeted handoff (claim exactly the woken slot, nobody else) FAIL 3/3
handoff suppressed when the wake is raised inside on_tick PASS 2/3 — and the win is gone (827 ms)

Read together: the comparator move is harmless; the extra switch is what harms; and the win and the harm come from the same place. The last row is the entanglement proof — suppressing the wakes raised inside the tick's own beat (wm_server.on_tick, the WM's kind-18 delivery) restores the boot and takes resched_requests from 32 to 1, removing exactly the handoffs that produced the 2.5 ms. No subset was found that keeps one and drops the other.

Two traps (observed) — both look like progress

  1. "Rotate only if the woken task is the round-robin successor" passed 6/7, but only because the target was being cleared before delivery: it was effectively a nudge with no rotation at all, and measured 807 ms. A guard that looks principled and silently disables the feature.
  2. The target's lifetime is subtle. The dominant wake (the WM's kind-18) is raised from inside the period tick's on_tick, so a rotation-based discharge cleared the handoff the very same beat had just armed — observed as handoff_attempts=1 out of nudge_served=16. Fixing it (discharge only when no nudge is pending) produced the 2.5 ms — and therefore the starvation.

Mechanism — a hypothesis, explicitly not a measurement

Best current reading: a handoff switches away from the interrupted task mid-quantum, and when the woken task blocks, round-robin resumes from its slot. The always-ready idle fallback sits at the highest slot, so it is reached before the wrap back to slot 0 (the shell), and it then parks in WFE until the next period tick — so the interrupted task waits ~1 s for what used to be its own quantum.

That is consistent with the starvation symptom, with why the WM is unaffected, and with why it needs more than one handoff to become fatal. It is not confirmed, and the probe that should have discriminated it (rotation skipped when the successor is the idle fallback) failed, which argues the story is incomplete. The 2026-09-14 measurement above argues further: the storm is survivable, so this hypothesis cannot be the whole story.

First step when this is picked up

Instrument the ring contents and current[0] across a failing boot before writing any fix. The smallest failing shape known so far is "an extra tick()-driven rotation on core 0 during a NOTEPAD boot".

Done, 2026-09-14 — see the measurement comment. The instrument was built, two defects in the instrument itself were found and fixed, and the result was not the expected one: the rotation log is clean, the storm is survivable, and the death is a silent VM-level error on the WM dui path. The blocker named at the top of this claim ("Blocked on") is what now stands between this card and its acceptance criteria.

Reproduction

git fetch origin agent/buffy/wmp3-nudge-recovered
git checkout agent/buffy/wmp3-nudge-recovered        # bc89e9c — draft PR #1255, NOT for merge
bash tools/gate/fleet.sh run live-wnd5-gate2-policy   # expect FAIL (starvation)

Contrast: the same command on current main passes. The branch is the pre-fix revision — it still writes the shared period_deadline global from every core's arm(), and lacks the targeted handoff; both are described on #1252.

Acceptance (proposed)

  • The defect reproduces on demand (stated N runs, same shape).
  • The mechanism is identified from observed evidence — ring contents and current[0] across a failing boot — not from the hypothesis above.
  • Either fixed, or bounded in writing: with the change in place, live-wnd5-gate2-policy stays PASS and live-sb6-perf-payoff (the 1 Hz determinism gate) stays PASS.
  • Then claim: WMP card 3: serve a wake by pulling the comparator forward, not by waiting for the 1 Hz tick #1252's nudge can be re-applied and its 2.5 ms re-measured.

Notes

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium severitybugSomething isn't workingclaimClaimed work (AGENTS.md): open = active; progress in comments; close when done

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions