fix(aarch64): validate the ret-dispatch resume PC and complete the inline-switch outgoing handoff (#576, #607) - #634
Open
ryanbreen wants to merge 9 commits into
Open
fix(aarch64): validate the ret-dispatch resume PC and complete the inline-switch outgoing handoff (#576, #607)#634ryanbreen wants to merge 9 commits into
ryanbreen wants to merge 9 commits into
Conversation
…C guard The K, E, and S self-tests intentionally report FAIL in this commit because the defects they cover are still present. Commits 2 and 3 turn those verdicts green by landing the production fixes. Co-Authored-By: Claude Code <noreply@anthropic.com>
… path (#576) Co-Authored-By: Claude Code <noreply@anthropic.com>
…ch fallback (#607) Complete the null-scheduler_ptr fallback outgoing handoff by normalizing the saved resume PC, requeueing a runnable outgoing thread after its context save, and clearing previous_thread. Do not add a second cooperative previous_thread publication in schedule_from_kernel. Isolated 60-second cortex-a72 SMP boots atop 68d3a5e1 measured hunk A alone at stranded=0:running_shape=0:ready_shape=0 in all three self-test profiles, while hunk B alone measured stranded=1:running_shape=0:ready_shape=1 in all three profiles, with the first Ready user strands scoring at 2037 ms, 2035 ms, and 2001 ms. Unlike the IRQ path, the cooperative marker has no DEFERRED_REQUEUE slot guaranteeing a later drain. Wake paths can therefore publish a marked thread Ready without enqueueing it, while no durable cooperative owner remains to re-examine it. Removing hunk B keeps the measured #607 fallback repair without shipping that incomplete marker lifecycle. Co-Authored-By: Claude Code <noreply@anthropic.com>
…576 tolerance Co-Authored-By: Claude Code <noreply@anthropic.com>
Ten serials plus a README recording what each one proves: leg K red at the test commit carrying esr=0x8600000d far=0x0 elr=0x0 and green at HEAD carrying the [RET_DISPATCH_REFUSED:...] census line; leg E red and green; leg S red, green, and a not-fired boot showing the leg reports FAIL rather than passing by omission; the disarmed control boot; and the two mutation boots that separate what the Rust refusal is worth from what the assembly floor is worth. The README states the two scopes this evidence does NOT cover: leg E's red is the classification being left set, not a demonstrated dispatch to zero, and leg S suppresses one opportunistic recovery path so it measures the fallback arm's own completion rather than the end-to-end field rate. Co-Authored-By: Claude Code <noreply@anthropic.com>
This was referenced Aug 22, 2026
#635) Round 1 gave the ret-based kernel dispatch a resume-PC validation, but the predicate it shared was an address-space check: anything at or above 0xFFFF_0000_0000_0000, plus the identity-mapped RAM window, with an assembly floor of #0x1000. A kernel *stack* address passes both, so the consumer stayed fail-open for every garbage kernel PC that is not zero — and that is the face the round-1 gate actually produced, 3 times byte-identically across two CPU profiles, now filed as #635: [INSTRUCTION_ABORT] FAR=0xffff000054243f00 ELR=0xffff000054243f00 ESR=0x8600000e IFSC=0xe from_el0=0 Only kernel text can hold a valid resume point, so the predicate now says that. `__kernel_text_start` / `__kernel_text_end` are already exported by arch_impl/aarch64/linker.ld, bracketing .text.vectors + .text and nothing else; exception.rs:100-118 is the existing precedent for reading a linker bound from Rust. Disassembly of the built kernel-aarch64 shows the start materialised as a PC-relative ADR and the end as ADRP+ADD, so both follow whichever alias is executing — the high-half HHDM alias on QEMU, the identity-mapped physical alias on Parallels, where the loader enters kernel_main at a physical address. A PC spelled in the counterpart alias is admitted through a second text-sized window rather than refused for being spelled differently. 4-byte alignment is required too, which is what the [PC_ALIGN] ELR=<tid> face of #633 violates. The assembly floor in aarch64_ret_to_kernel_context is raised to the same lower bound: adrp/add of __kernel_text_start into x16, compared against x1. x16 is scratch there and is what the neighbouring assembly in this file already uses; x19-x30 and SP are live and untouched. Two extra register-only instructions on the always-taken path — no lock, no allocation, no logging, in either net. The [RET_DISPATCH_REFUSED:...] census line gains an explicit :pc= field naming the refused value, so a reader no longer has to know that resume_pc == x30. No existing field is renamed or removed; the gate script and both ratchets that read this line are unaffected. Two feature-gated self-tests, built the way legs K/E/S already are (one-shot arming, a designated disposable victim kthread, a FIRED marker, and a verdict that reports FAIL when the leg never fired so it cannot pass by omission): ret_stack_pc_oracle (leg T) writes the victim's own saved SP into context.x30 at the same hook leg K uses — a value the old predicate accepts and the new one refuses, i.e. #635's face by construction. ret_floor_oracle (leg F) substitutes 0x100_0000 for resume_pc at the two call sites of aarch64_ret_to_kernel_context, inside the window between the Rust predicate and the branch, which is the only thing the assembly floor exists to cover. That constant is above the old #0x1000 bound and below __kernel_text_start, so it distinguishes the two versions of the floor. The serial-atomicity ratchet gains anchors for both new injectors and the updated emission count for take_inline_ret_dispatch_info. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t faulted `report_ret_floor`'s predicate was `armed == 1 && fired == 1`, and the kernel survives the substituted dispatch in both configurations the leg is meant to separate — so it printed PASS on both sides of its own mutation. Only the abort record discriminated, which means the leg's verdict line was decorative. It now also reads `any_fatal_postmortem_captured()`, a two-line accessor over the `FATAL_POSTMORTEM_CAPTURED` array exception.rs already maintains, and prints it as `fatal=`. Re-measured: with the assembly bound lowered back to `#0x1000` the leg reads `fatal=1 … FAIL` and the abort lands at the substituted PC four lines after the substitution; with the bound raised it reads `fatal=0 … PASS` and no abort at that PC appears. The term only covers faults before the leg reports, which is what this leg needs — the mutation's abort is immediate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… behind them Twelve serials and a Round 2 section on the README: * the four round-1 gate boots that failed and were not in the PR — three carrying #635's single self-consistent record on both CPU profiles, one carrying #613's disagreeing record pair (which also carries the EL1 [PC_ALIGN] ELR=0x4b1 record appended to #633); * leg T red and green, plus two more greens, for the kernel-stack resume PC; * leg F red and green for the assembly floor; * the round-2 disarmed control. Two disclosures are written into the README rather than left to be found: 1. One of the three leg-T green boots carries [TEST:process:kernel_stack_ownership_oracle:FAIL] with slot_free_delta = slot_alloc_delta + 1 — one more free than alloc inside that test's census window, which is what a thread allocated before the window and reaped inside it looks like, and the leg's own victim is such a thread. Same boot reads two_owner=0, zero_owner=0, drop_refused_live=0, frame_balance=0. The other two greens and the disarmed control are clean. 2. #635's abort family — ESR=0x8600000e IFSC=0xe with FAR == ELR == x30 == a kernel stack address — was observed once on the fixed tree, after that boot's tests had passed. Its register file differs from #635's field captures in a way that matters: x29 = 0x1 rather than x29 == x30, and x30 is sp + 0x38, a slot inside the faulting frame. That is an ordinary compiled epilogue reloading an overwritten saved-LR slot and returning to it — a transfer no resume-PC predicate can see. The round-2 containment is therefore not claimed to close #635. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tribute both The 25/profile gate on this branch scored 48/50 with two UNATTRIBUTED boots. Both serials and the run log are preserved, and both are attributed by field signature rather than to a catch-all: * max boot 11 — [PC_ALIGN] ELR=0x5 FAR=0x5 from_el0=0, x30=0x5, x29=0x1f, and the preceding DISPATCH_TRACE entry reads old=5. Thread id 5 is that CPU's own outgoing tid, which sharpens #633 from "a live tid" to "the outgoing tid of the immediately preceding dispatch" — the 0x4b1/0x4b5/0x4b7 captures fit the same rule. Appended to #633. * cortex-a72 boot 7 — [INSTRUCTION_ABORT] FAR=ELR=0xffff000040800000 ESR=0x8200000e IFSC=0xe from_el0=1, victim SIGSEGVs. EC=0x20 is an abort from a lower EL: a userspace thread resumed at a kernel address past __kernel_text_end. Filed as #637. Neither red is reachable by this branch's predicates, and the run shows it rather than asserting it: zero [RET_DISPATCH_REFUSED: lines and zero "WARN: bad elr" lines across all 50 boots, so neither predicate returned false once. 0x5 is below both versions of the assembly floor, so that transfer never entered aarch64_ret_to_kernel_context. The README also records that an earlier run of the same gate was discarded at boot 9 rather than reported, after a concurrent production-profile build hardlinked a non-boot_tests kernel onto the gate's one ELF path — the landmine the gate script documents at :146-153. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
Summary
Six places on aarch64 load a stored resume PC and transfer to it. Five validated the value and
redirected to
idle_loop_arm64when it failed; the sixth — the ret-based kernel dispatch fed bytake_inline_ret_dispatch_infoand consumed byaarch64_ret_to_kernel_context, the one triedfirst at both dispatch sites — did not. Round 1 gave it a validation. The gate then showed the
validation was the wrong shape: it was an address-space check, so it closed the
PC == 0face andleft the
PC == <some other kernel address>face of the same consumer fully open. Round 2 makes thepredicate a kernel-text-range check off the linker's own bounds and raises the assembly floor to
match. Along the way this PR also closes one place that leaves a thread mis-classified as
inline-saved, and completes an outgoing-thread handoff that one fallback arm left half-done.
Closes #607 — with DEVIATION D5 attached (below) and with the note that the RCA's second
commit-2 bullet, publishing
previous_threadinschedule_from_kernel, was measured harmful 3/3and is correctly absent from this PR.
Not closed here.
longer transfer to a PC outside kernel text, by two independent nets, each mutation-proven; the
FAR=0 ELR=0bucket read 0/350 in round 1 with its tolerance removed; and the empiricalconfirmation R38(c) asked for (refusals > 0 in production with zero aarch64: rare EL1 INSTRUCTION_ABORT (ELR=0, FAR=0) during process spawn on the boot_tests strict gate (~1/80 boots) #576-shape aborts) has not
been obtained — every production-profile gate reads
RET dispatch refused: 0 marker lines. Theclose wording is deliberately left to the confirm slot.
spsr=0x200023c5with DAIFI+Fset andevery resume-PC consumer clears those one instruction before transferring, so it was not reached
from a dispatch transfer. An honest comment saying that is on the issue.
"What round 2 does and does not settle".
[PC_ALIGN] ELR=<tid>face; an EL1 exemplar found in this PR's starved gate isappended to it, which widens it from the EL0-only face it was filed as.
cross-referenced on the issue.
Commits
c3b0133d2d7ce786ddd03a116b3e143dc9c75c3b972a0832a092e8a471cceff0What changed
2d7ce786.is_kernel_addris lifted out ofrestore_kernel_context_inlineinto one file-levelresume_pc_is_dispatchable, so both consumers share one authority.take_inline_ret_dispatch_inforeturnsNonewhen it fails — before any of its mutations, so adeclined dispatch leaves the thread exactly as it found it — and the dispatch falls through to the
ERET arm, where the existing redirect-to-idle already handles it. No guard relaxed, no dispatch
added, nothing freed twice: a refusal routes onto a path that already exists. It also bumps
RET_DISPATCH_REFUSALS/RET_DISPATCH_REFUSED_TIDand prints, capped at 8 emissions. It alsomirrors a floor into
aarch64_ret_to_kernel_context, matching the oneaarch64_enter_exception_framehas carried all along. And
ExecSchedCommit::applyclears the inline-schedule classification when itreplaces
t.context, through a single new authorityThread::clear_inline_schedule_state.ddd03a11. Thescheduler_ptr-null fallback arm ofinline_schedule_trampolinefinishes theoutgoing thread's transaction the way the normal arm does: normalise
elr_el1 = x30, requeue underthe same two-part condition, clear
previous_threadunconditionally.6b3e143d. The service-sequence gate's576bucket is no longer excluded from the FAILcondition.
0x0 0x0 0x8600000dgets its own field-exact, still-failing626bucket — naming, nottolerating.
[RET_DISPATCH_REFUSED:is counted per boot and per profile, reported and never gated.972a0832— round 2. The predicate stops being an address-space test:__kernel_text_start/__kernel_text_endare already exported byarch_impl/aarch64/linker.ld:57,78and bracket
.text.vectors+.textand nothing else;exception.rs:100-118is the existingprecedent for reading a linker bound from Rust. Disassembly of the built
kernel-aarch64shows thebounds materialised PC-relatively (
adrfor the start after linker relaxation,adrp+addfor theend), so the window is automatically the alias of whichever mapping is executing — the high-half HHDM
alias on QEMU, the identity-mapped physical alias on Parallels, where the loader enters
kernel_mainat a physical address. A PC spelled in the counterpart alias is admitted through a second text-sized
window rather than refused for its spelling. Live window on this build:
[0xffff000040400000, 0xffff000040600000).The assembly floor is raised to the same lower bound —
adrp x16 / add x16, :lo12: / cmp x1, x16against
__kernel_text_start, replacingcmp x1, #0x1000.x16is scratch there and is what theneighbouring assembly in the same file already uses;
x19-x30and SP are live and untouched.Both nets are register-only arithmetic — no lock, no allocation, no logging on any always-taken path.
addr_of!on anextern staticis an address computation, not a load. The census line gains anexplicit
:pc=field naming the refused value; no existing field is renamed or removed, so the gate'sgrep -cFand both ratchets that read the line are unaffected.a092e8a4. Leg F's verdict wasarmed && fired, and the kernel survives its substituted dispatchin both configurations the leg is meant to separate — so it printed PASS on both sides of its own
mutation. It now also reads
any_fatal_postmortem_captured(), a two-line accessor over the arrayexception.rsalready maintains, and prints it asfatal=.Evidence
60 s boots,
-cpu cortex-a72 -smp 4, soft-float target. Every serial is indocs/planning/teardown-unification/607-576-serials/, with a README section per round.Round 1 — legs K, E, S
c3b0133drefused=0:FAILand[FATAL_REGS] label=INSTRUCTION_ABORT cpu=2 esr=0x8600000d far=0x0 elr=0x0refused=1:refused_tid=10:PASS, no abort,[BOOT_TESTS:PASS]ExecSchedCommitclassification holevictim_tid=1212:inline_left_set=1:cleared=0:FAILinline_left_set=0:cleared=1:PASSstranded=1:FAIL(2/3 boots; the thirdfired=0:FAIL, refusing to pass by omission)stranded=0:PASS(3/3)refused=0:FAIL, no abort,[BOOT_TESTS:PASS]— the floor contains it; the Rust arm is what names the producerrefused=0:FAILand[INSTRUCTION_ABORT] FAR=0x0 ELR=0x0 ESR=0x8600000d,[BOOT_TESTS:FAIL:1]Round 2 — legs T and F
Leg T writes the designated victim's own saved
spintocontext.x30at leg K's hook: a value theround-1 predicate accepts and the round-2 predicate refuses. Leg F substitutes
resume_pc = 0x0100_0000at the two call sites ofaarch64_ret_to_kernel_context, after thepredicate has accepted the real value — the window the assembly floor exists for, and a constant
above the old
#0x1000bound and below__kernel_text_start, so the raise is the only variable.refused=0:refused_tid=0:FAILand[INSTRUCTION_ABORT] FAR=0xffff000054265f00 ELR=0xffff000054265f00 ESR=0x8600000e,spsr=0x20000305,x30 ==the same value — #635's field set from a single-field writecmp x1, #0x1000fatal=1:FAILand[FATAL_REGS] label=INSTRUCTION_ABORT cpu=3 esr=0x8600000d far=0x1000000 elr=0x1000000four lines after the substitution972a0832[RET_DISPATCH_REFUSED:tid=10:pc=0xffff000054265f00:…],refused=1:refused_tid=10:PASS, no abort — 3 bootsfatal=0:PASS, no abort at0x1000000,[BOOT_TESTS:PASS]boot_tests, neither feature)[TEST:…:FAIL:…],[BOOT_TESTS:PASS]Mutation 1 also shows
x16=0xffff000040400000in the fatal dump — the raised assembly floor loadingits bound and correctly letting a stack address through, since a stack address is above kernel text.
That is the direct evidence that the assembly floor structurally cannot catch this face and the Rust
predicate must.
Scoped honestly: mutation 1's capture reproduces the transfer and the resulting fault
(
ESR=0x8600000e,IFSC=0xe,FAR == ELR == x30,spsr=0x20000305,from_el0=0) — not theproducer's write pattern. #635's field captures additionally have
x29 == x30and six consecutivestack slots across
x19-x27; a single-field write cannot make those.Collateral, disclosed: 1 of leg T's 3 green boots also carries
[TEST:process:kernel_stack_ownership_oracle:FAIL]withslot_alloc_delta=1000:slot_free_delta=1001— one more free than alloc inside that test's census window, which is what a thread allocated before
the window and reaped inside it looks like; the leg's own victim is such a thread and its verdict line
lands inside that test. The same boot reads
two_owner=0,zero_owner=0,drop_refused_live=0,frame_balance=0. The other 2 greens and the disarmed control are clean.What round 2 does and does not settle
Does: the ret-based kernel dispatch can no longer transfer to a resume PC outside kernel text.
Two independent nets, each proven load-bearing by its own single-variable mutation, and a refusal
names the thread instead of killing the machine.
Does not: close #635. One boot on the fixed tree, after its own
[BOOT_TESTS:PASS], producedthe family again (
legF-green-floor-contained.txt:716,FAR=ELR=0xffff000054276f28 ESR=0x8600000e IFSC=0xe). Its register file is a different producer fromthe three gate captures:
x29== x30 ==the faulting PC0x1x19-x27spvs PCsp + 0x2c0sp + 0x38x30 == sp + 0x38with a garbagex29is an ordinary compiled epilogue (ldp x29, x30, [sp, #0x38])reloading an overwritten saved-LR slot and returning to it. No dispatch helper participates and no
resume-PC predicate can see it. The discriminator is written up on #635 so a later gate can attribute
a hit to the right producer instead of scoring the union.
Gates — every run, with its real verdict
Round 1, on
c9c75c3b:UNATTRIBUTED = 0UNATTRIBUTED, all three theESR=0x8600000ekernel-stack-PC face, both CPU profiles → now filed as #635, serials in-repoyeshogs,nice -n 19)UNATTRIBUTED, a disagreeing record pair = #613, pre-adjudicated → cross-referenced on the issue, serial in-reporun-aarch64-full-test.sh --rebuild --boot-tests-onlyrun-boot-parallel4 × 5sys_readlivelocks = #630, 1loopback_recv_wakereader-EOF (unfiled, aarch64-only diff, nomainbaseline)Round 2, on
71cceff0:aarch64-breenix-kernel.jsonboot_testsprofilerun-aarch64-full-test.sh --boot-tests-only --rebuildUNATTRIBUTED, both attributed belowThe two round-2 gate reds, by field signature:
[PC_ALIGN] ELR=0x5 FAR=0x5 from_el0=0 cpu=1,x30=0x5,x29=0x1f, andDISPATCH_TRACE[0] U old=5[INSTRUCTION_ABORT] FAR=0xffff000040800000 ELR=0xffff000040800000 ESR=0x8200000e IFSC=0xe from_el0=1, victim SIGSEGVsBoth serials are preserved in-repo, with the run log.
Neither is a refusal, and neither can be one. Across all 50 boots there are zero
[RET_DISPATCH_REFUSED:lines and zeroWARN: bad elr … redirecting to idlelines, so neither ofthis PR's predicates returned false even once in the run — round-2's dispatch behaviour is identical
to round-1's here, and these two reds cannot be caused by the narrowing.
0x5is below bothversions of the assembly floor, so that transfer did not come through
aarch64_ret_to_kernel_contextat all;
from_el0=1puts the other one on a path this PR does not touch. Round 1's 25/profile run was50/50 on the same script, so at these rates the two runs are consistent with the same background.
One process note, since it affects how the numbers above should be read: an earlier 25/profile run
was discarded at boot 9, not reported.
run-aarch64-service-sequence-gate.sh:146-153documentsthat a concurrent
cargo buildhardlinks a different feature set onto the onetarget/aarch64-breenix-kernel/release/kernel-aarch64path between boots, and that the gate's guardonly runs at startup; I started the production-profile boot test — which builds without
boot_tests— while that gate was mid-run. The kernel was rebuilt with--features boot_testsandthe whole gate re-run with nothing else touching the tree. The reported run is the clean one.
Open before merge
issues (aarch64: a live thread id is ERETed as a userspace PC ([PC_ALIGN] ELR=<tid>, from_el0=1) — one face across two preserved serials, EL0 resume PCs are unvalidated #633, aarch64: a userspace thread is resumed at a kernel address — [INSTRUCTION_ABORT] ESR=0x8200000e IFSC=0xe from_el0=1, FAR==ELR==0xffff000040800000, 1/50 SS-gate boots #637) and neither reachable by this PR's predicates — but the gate's own verdict is
FAILED and this PR does not ask for that to be waived.
mainbaseline. Nothing ran onmainat comparable N, so whether aarch64: ret-dispatch transfers to a kernel-STACK resume PC — [INSTRUCTION_ABORT] FAR==ELR==x30==x29, ESR=0x8600000e IFSC=0xe, 3/200 clean SS-gate boots on both CPU profiles #635's 1.5% ispre-existing or was raised by
ddd03a11's strand-to-ret-dispatch conversion is not measured. Thebinding rule:
main >= 1= pre-existing, with the rate noted on aarch64: ret-dispatch transfers to a kernel-STACK resume PC — [INSTRUCTION_ABORT] FAR==ELR==x30==x29, ESR=0x8600000e IFSC=0xe, 3/200 clean SS-gate boots on both CPU profiles #635;main == 0= caused byddd03a11, disclosed plainly here. An equal-N control onmainis the next gate slot's.finding above, attributed by register-file shape rather than by raw
ESR/IFSC, or it scores aproducer this PR never claimed.
FAIL cannot fail a gate; F6 leg S's
stranded == 0can pass vacuously; F7report_strandprints aglobal instead of its argument; F8 the census can only report
min(refusals, 8); F9RET_DISPATCH_REFUSED_TIDis a last-wins slot; F10 orphaned "tolerated" prose inrun-aarch64-arma609-arm.sh; F11PRIOR_NAMED_INSTRUCTION_ABORT_ARMS + 1is an equality, not afloor.
loopback_recv_wakereader-EOF failure from round 1's beast run is still unfiled.DEVIATION D5 — leg S suppresses one recovery path, deliberately and test-only
Under
strand_inject_live_outgoingonly, the widened arm clearsprevious_threadfor the CPU whenthe stimulus engages, because
fix_exception_cleanup_cpu_state()would otherwise opportunisticallyre-enqueue the dropped thread and mask the very transaction under test. That backstop is
opportunistic, not guaranteed — #607 was observed in the field at 1/50 despite it — so suppressing
it isolates the fallback arm's own completion. It also means leg S measures that completion, not the
end-to-end field rate. #607's close carries this disclosure.
One design bullet was measured harmful and is not here
The RCA's outgoing-handoff design had a second hunk: publish
cpu_state[cpu].previous_thread = Some(old_id)inschedule_from_kernel, symmetric with the IRQpath. Measured in isolation on top of the containment commit:
stranded=0:running_shape=0:ready_shape=0, 0/3 bootsprevious_threadpublicationstranded=1:ready_shape=1, 3/3 boots, first strand a user thread atstate=Ready,dwell_ms2037/2035/2001Unlike the IRQ path, the cooperative path has no
DEFERRED_REQUEUEslot a later scheduler entryalways drains, so while the marker is up a wake publishes the thread
Readyand declines to enqueueit, and once the trampoline clears the marker without requeueing there is no durable owner left. It is
not in this PR, and
ddd03a11's message records the measurement rather than the intention.Ratchets
Every structural check that moved got tighter, none looser.
inline_schedule_trampoline_retains_the_redundant_elr_guardpinned a bare count of one normalisation;it now derives the trampoline's arms from the source and requires every arm to carry exactly one,
so a future third arm without it trips the check.
serial_line_atomicity_structure's two-wayunlocked-writer census admits the new writers at their exact counts with the rationale inline: they
run under the scheduler lock on a dispatch path where the locked writer is unavailable, all of them
are one-shot or emission-capped rather than periodic, and
[RET_DISPATCH_REFUSED:is a gate censusand never a gate condition, so a torn line can under-count a reported number but cannot flip a
verdict. Same class as the
check_inline_save_resume_point/check_inline_eret_resume_pcentriesalready there.
🤖 Generated with Claude Code