Skip to content

K3: delete the logstorm and lognest kernel threads - #586

Merged
Japabu merged 7 commits into
mainfrom
wt/toyos-nokthreads
Sep 29, 2026
Merged

Japabu merged 7 commits into
mainfrom
wt/toyos-nokthreads

Conversation

@Japabu

@Japabu Japabu commented Sep 28, 2026 •

Copy link
Copy Markdown
Collaborator

Track issues/kernel/the-kernel-still-creates-threads.md, stage K3: the
logstorm and lognest kernel threads are deleted, and the four log checks
they drove now run on producers that are not kernel threads.

What changed and why

The nesting gate runs inline in SYS_LOG_READ. log::nested::start_once
no longer calls kthread::spawn("lognest", …): it runs the body in the first
read's own syscall, between arch::cpu::enable_interrupts() and
disable_interrupts(). That is the context the kthread had: Ring 0, IF=1, not
preempted, because the Ring 0 timer only sets need_resched.
trap_dispatch's page-fault arm and deadline.rs already run in it, and
log::user::read holds no lock at the hook. Everything the gate needs is
restored unchanged:

  • the log_nest IDT gate and LOG_NEST_VECTOR;
  • aarch64 Vector::LogNest, so Hda and VirtioSound keep 2 and 3 and no
    vector moves;
  • IrqGuard::unclosed, both injection points (shard.rs and
    percpu::reserve_log_slot) and the kernel-loom shims;
  • the actuators log-nested-emit, log-nested-reserve and
    log-unbracketed-reserve;
  • log_nested_emit, log_reserve_window and log_reserve_window_negative.

nested::inject is private now, because its one outside caller was
log-shared-reservation.

The conservation law comes back as log_conservation_smp2, with a userland
producer.

  • One test-only SYS_DEBUG action, LOG_PATTERNED (21), compiled only under
    test-actuators, so no shipping kernel carries it. It calls
    log::storm::emit_patterned(0, arg).
  • test-runner's new log-storm builtin runs log_gate with one
    std::thread producer, which makes that call once per record and
    bumps an AtomicU64 after each call.
  • The producer's phases are events, not a schedule, and no guest clock
    decides any of them:
    1. It emits HANDOVER (64) records, then blocks in recv until the reader
      has taken a storm record. Only a disconnect is an error. A reader that
      never arrives hangs, and the host's 60 s CEILING reds it, as No QEMU test measures time, and audio is judged on metal only #562
      rules.
    2. The reader sends shards × 512 + 1 and then blocks until the producer
      has emitted that many more records. That puts more than a shard's worth
      into at least one shard, whichever CPUs the producer ran on. So the
      ring laps this reader's cursor on every run, and lost is never zero.
    3. The producer then emits until the reader sets stop. The reader sets it
      after the first read that takes storm records while the producer's
      counter moved across it (the counter is loaded before and after
      SYS_LOG_READ). concurrent counts only such reads after the lap.
  • The ledger, the byte-for-byte regeneration and the verdict are unchanged.
    emitted is the producer's counter, since the count now depends on the
    shard count and on when stop lands.
  • The loop ends once the producer is joined and QUIET_READS reads came back
    empty. STORM_SETTLE, STORM_RECORDS and the logstorm start/done
    parsers are deleted.
  • The guest's "raced nothing" refusal is deleted, because the producer only
    returns after a concurrent read. The host still refuses concurrent=0,
    read=0 and now lost=0.
  • The host boots the test kernel by kernel_features: TEST_KERNEL, since the
    producer needs SYS_DEBUG and arms nothing.
  • The orchestrator approved LOG_PATTERNED as a test-only SYS_DEBUG
    action. The owner delegated kernel-interface decisions to the
    orchestrator.

Why --smp 2: two CPUs keep two shards in the merge and the ledger, and
give the producer a CPU the reader is not on. The rename is carried through
tests/toyos.rs and tests/common/logread.rs. tests/test-durations drops
the old name's number, which was measured for a different producer.

Deleted and not restored:

  • the log-storm and log-shared-reservation actuators (no test ever read
    the second);
  • storm::start_once/body/STARTED;
  • watch::park_forever;
  • the boot-actuators kthread row budget;
  • Producer.shards/mark_shard/migrated=, which nothing asserted, and the
    host's a/b field parse that only migrated= used;
  • close_probe's always-empty params, now inlined.

Issues.

  • The K3 stage is deleted from the track, and K6 is now blocked on K2, K4
    and K5.
  • issues/diagnostics/a-shards-timestamps-run-backwards-at-seq-517.md is
    deleted. Its only evidence is [log] unbracketed: log-gate: FAILED: cpu6 seq 517 …, which is the line log_reserve_window_negative prints when it
    passes: the designed refusal. Seq 517 is 5 + 512, so the "fixed position"
    the issue describes is the burst's width.
  • The test manifests whose comments justified logread by a gate their
    boot never runs lose the comment. The grants stay, filed as
    issues/isolation/test-runner-holds-logread-where-no-log-builtin-runs.md.
  • The timing-verdicts issue no longer names STORM_SETTLE.

Orchestrator's guest runs at 32d1792

test exit
log_conservation_smp2 3/3 0
log_nested_emit 0
log_reserve_window 0
log_poll_outlives_a_close 0
mut-join-first 1
mut-join-after-handover 1
mut-no-lost 1
mut-nest-no-sti 1
Fast 0

Gates at 32d17926, this worktree

  • cargo run -- --ci host: EXIT=0 ("Host: 54 step(s), all green").
  • cargo run -- --build-only: EXIT=0.
  • cargo run -- --build-only --boot-config tests/testcases --kernel-feature boot-actuators --kernel-feature test-actuators:
    EXIT=0. This compiles test-runner and the test kernel, which the plain
    image build does not.
  • mut-join-first, mut-join-after-handover, mut-no-lost and
    mut-nest-no-sti: each passed git apply --check, applied, and built that
    test image with EXIT=0. Each was reverted with EXIT=0, leaving
    git status --porcelain empty.

High-risk checks

The log's interrupt-atomicity claim, and a new SYS_DEBUG action.

  • Negative controls: each is a checked patch that passed
    git apply --check, applied, and built the test image, then was reverted
    with git status --porcelain left empty.
    • log_reserve_window_negative is the control on the IrqGuard bracket.
    • mut-nest-no-sti deletes the enable_interrupts() line in
      log/nested.rs. It must turn log_reserve_window_negative red: with IF
      clear, the IPI pends until sysret, the burst lands after the outer
      record, and the gate passes.
    • mut-join-first joins the producer before the first read. The producer
      parks at the handover, and log_conservation_smp2 must hang to the
      host's CEILING.
    • mut-join-after-handover joins the producer right after the handover
      send, so the reader takes nothing more until the storm has ended. The
      producer never sees stop, and log_conservation_smp2 must hang to the
      CEILING.
    • mut-no-lost deletes self.lost += oldest.saturating_sub(want); from
      kernel/src/log/read.rs. The lap makes the sequence numbers show a gap
      on every run, so log_conservation_smp2 must refuse with "conservation
      failed".
  • Independent oracle: the Intel SDM's definition of RFLAGS.IF and fixed
    IPI delivery, which the unbracketed control measures by breaking it. The
    conservation law's other oracle is the text regenerated byte for byte from
    t=/i= in userland, independent of the kernel's formatter.

🤖 Generated with Claude Code

kernel/src/log/storm.rs and kernel/src/log/nested.rs existed only to
exercise four guest tests (log_conservation_smp1, log_nested_emit,
log_reserve_window, log_reserve_window_negative). Delete both files,
their kthread::spawn call sites, their five actuators and cmdline
tokens (log-storm, log-unbracketed-reserve, log-nested-emit,
log-nested-reserve, log-shared-reservation), the x86-64 log_nest IDT
gate and vector, the aarch64 LogNest vector, the userland test-runner
log-gate builtin those tests alone drove, and every test registration
and helper that served them — leaving klogd and iod as the kernel's
only remaining kthread::spawn sites.

Two real claims those tests alone checked — same-CPU interrupt
reentrancy inside emit's IF/TF-off bracket, and SYS_LOG_READ's
conservation law under concurrent multi-shard write load — are now
unverified, recorded in
issues/kernel/deleting-logstorm-and-lognest-left-two-log-claims-unverified.md
rather than left silent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6rME2DoqwjcYFStYHHY4j
@Japabu
Japabu marked this pull request as ready for review September 28, 2026 19:25
@Japabu

Japabu commented Sep 28, 2026

Copy link
Copy Markdown
Collaborator Author

Review, round 1, at 4c35a94

Gate: CI host SUCCESS on run 36472126504 at head 4c35a94; the other host run on the same head was SKIPPED, a duplicate. The orchestrator's guest runs at 4c35a94: Fast EXIT=0, --weekly log_poll_outlives_a_close EXIT=0. The branch adds no test and targets no hardware. git merge-tree --write-tree origin/main 4c35a94b exits 0 against e3a1cdc. The change is ready for review.

Net: +51 / -1565. Production (kernel/, kernel-loom/) is +3 / -389. Tests (tests/, userland/test-runner/, the src/build.rs test) are +6 / -1194. Issues are +45 / -2.

BLOCKER

  • kernel/src/log/nested.rs:46 (origin/main) — The only guest check of emit's IF/TF bracket is deleted, together with its only negative control, even though a replacement needs no kernel thread. The replacement:
    • Replace kthread::spawn("lognest", body, 0) with an inline call in the existing SYS_LOG_READ hook: crate::arch::cpu::enable_interrupts(); body(); crate::arch::cpu::disable_interrupts();.
    • body then returns instead of calling park_forever.
    • The inline context is the kthread's: Ring 0, IF=1, not preempted. The ring-0 timer only sets need_resched, and sched/kthread.rs's header says a Ring 0 loop reaches no preemption point. The kernel already runs a syscall in this state in trap_dispatch's page-fault arm (idt/mod.rs:365) and in deadline.rs:282. log::user::read holds no lock at the hook.
    • Keep idt/log_nest.rs, LOG_NEST_VECTOR, aarch64 Vector::LogNest, IrqGuard::unclosed, both injection points (shard.rs, percpu::reserve_log_slot), the kernel-loom shims, and emit_patterned/checksum/payload_byte.
    • Keep the actuators log-nested-emit, log-nested-reserve and log-unbracketed-reserve, the nest half of log_gate.rs, and log_nested_emit, log_reserve_window and log_reserve_window_negative unchanged.
    • Mutation the implementer runs: delete the new enable_interrupts() line. log_reserve_window_negative must then go red: the IPI pends until sysret, so the burst lands after the outer record and the gate passes.
    • This belongs in this PR; zero ABI change.
  • kernel/src/log/storm.rs:49 (origin/main) — log_conservation_smp1 is deleted, even though a userland producer can carry it. The replacement:
    • Add one SYS_DEBUG action under test-actuators (syscall/dispatch.rs's DA:: match, one toyos_abi::syscall::debug_action constant). It calls log::storm::emit_patterned(a2, a3) and returns 0.
    • log-gate spawns one std::thread producer that makes that call for 0..1024 and bumps a shared AtomicU64 after each call. The reader keeps its ledger, its byte-for-byte regeneration and its verdict. The host conservation() stays.
    • Drop logstorm start/done and their parsers: the gate knows the shape it started. Drop the log-storm actuator and storm::start_once/body/STARTED.
    • Measure concurrent from the producer's counter (records read while it was below 1024), not from batch order. Per the kthread header, the deleted Ring 0 storm reached no preemption point on --smp 1. The batch-order count is non-zero whenever the storm's records span two batches, even ones read after it ended.
    • Mutation: join the producer before the first read. concurrent == 0 must then red.
    • The new debug action is a test-only ABI addition and needs the owner's word per CLAUDE.md. If that word is no, the fallback without ABI is a thread spawning /system/bin/echo children: each exit commits syscalls:/memory:/census/exit: records. That fallback keeps the ledger and loses byte regeneration, and the issue must then say so. This belongs in this PR.
  • issues/kernel/the-kernel-still-creates-threads.md:28 — K3 is marked done while its own condition is unmet. The condition is "deleted if the log gate does not need kernel-context producers", and this branch deletes the gate's only producers. K3 closes only when the two replacements above land.

NOTE

  • kernel/src/arch/aarch64/trap.rs:163 — Deleting LogNest = 1 silently renumbers Hda 2→0 and VirtioSound 3→1. Restoring LogNest per the first BLOCKER fixes it; a deletion must not renumber.
  • kernel/src/arch/{x86_64,aarch64}/mod.rs percpu_fetch_add + actuator.rs log-shared-reservation — No test on origin/main ever reads this actuator (git grep log-shared-reservation origin/main -- tests userland src is empty). Its deletion stands inside the restoration.
  • tests/testcases/system.toml:26, partclaimcase:32, blockdcase:25, doomcase:35, doommusiccase, logrotatecase, metalcase, netcase, sshdcase — These justify test-runner's logread by "the log gate". The branch deleted that gate and missed these citations; restoration makes them true again. Only testcases runs a log builtin at all, so the grant in the other manifests is authority nothing uses. File that as an issue; it is outside this fence.
  • issues/diagnostics/a-shards-timestamps-run-backwards-at-seq-517.md — Not accounted for by the PR. Its only evidence is the [log] unbracketed: line, which is log_reserve_window_negative's designed refusal: the deleted host doc derives seq 517 = 5 + 512. Deleting the control would leave that defect with no instrument; restoring it keeps one. The orchestrator should judge whether the issue is a defect at all.
  • issues/kernel/a-spawn-of-echo-was-refused-with-an-error-nothing-names.md, issues/build/log-reserve-window-negative-times-out-beside-other-guests.md — Their exit conditions name log_gate.rs's spawn path and log_reserve_window_negative, and both go under deletion. Restoration keeps them measurable.
  • userland/test-runner/src/log_gate.rs (origin/main) Producer.shards, mark_shard, migrated= — Its own doc says nothing asserts it. Delete it while the gate is restored.
  • tests/common/logread.rs close_probe(.., params) — The parameter has one value, &[]. Inline it.

REMOVE

  • issues/kernel/deleting-logstorm-and-lognest-left-two-log-claims-unverified.md — Delete the whole file once the checks return. It is also false as written:
    • "multi-shard": the only conservation test was --smp 1, one shard.
    • "outside K3's scope (delete only)": K3's own condition says otherwise.
    • The quote attributed to nested.rs's header is from tests/common/logread.rs.
    • "per the owner's ruling…" is narration.
  • issues/kernel/the-kernel-still-creates-threads.md:28-31 — The K3 bullet is rewritten instead of deleted.
  • userland/test-runner/src/log_close.rs:20-21 — Rewritten prose. Revert it; log-gate returns.
  • tests/common/logread.rs:1 — The module doc is rewritten. Revert it with the restoration.
  • PR body — Cut these lines: "(~62 lines)", "(~143 lines)", "caught by clippy, not by eye", "a pricing hint, read leniently — not required for correctness", "kernel/src line count", the "Guest runs to queue" and "Test plan" sections, and the paragraph "What was checked by nothing else… outside K3's scope".

SEND BACK

Japabu and others added 4 commits September 28, 2026 22:52
Round 1 deleted the logstorm and lognest kernel threads together with
the four guest tests they drove. The threads stay deleted; the checks
come back, with producers that are not kernel threads.

The nesting gate (log_nested_emit, log_reserve_window and the negative
control log_reserve_window_negative) is restored unchanged except for
where its body runs: log::nested::start_once now runs it inline in the
first SYS_LOG_READ, between enable_interrupts() and
disable_interrupts(). That is the kthread's context: Ring 0, IF=1, not
preempted (the Ring 0 timer only sets need_resched), as the page-fault
arm and deadline.rs already run. The log_nest gate, LOG_NEST_VECTOR,
aarch64 Vector::LogNest (so Hda and VirtioSound keep 2 and 3),
IrqGuard::unclosed, both injection points, the kernel-loom shims and
the three actuators return with it. nested::inject is private now: its
only outside caller was log-shared-reservation.

The conservation law returns as log_conservation_smp2. Its producer is
a std::thread of test-runner's new `log-storm` builtin, calling a new
test-only SYS_DEBUG action, LOG_PATTERNED (21, under test-actuators),
1024 times; each call emits one `logstorm t=0 i=<n>` record through
log::storm::emit_patterned, which the reader regenerates byte for
byte. `concurrent` counts storm records taken by a read after which
the producer's own counter was still below 1024, not batch order; the
loop ends once that counter is 1024 and QUIET_READS reads were empty,
so STORM_SETTLE and the logstorm start/done parsers go. --smp 2 rather
than 1: a Ring 3 producer on the reader's only CPU interleaves only at
10 ms quantum ends, and one whose 1024 calls fit in one quantum would
make every run vacuous.

Deleted and not restored: the log-storm and log-shared-reservation
actuators (the second was read by no test), storm::start_once/body,
watch::park_forever, the boot-actuators kthread row budget, the
Producer.shards/migrated= ledger nothing asserted, the host's a/b field
parse that only migrated= used, and close_probe's always-empty params.
test-durations drops log_conservation_smp1's number, measured for a
different producer; the renamed test is unpriced until measured.

issues/diagnostics/a-shards-timestamps-run-backwards-at-seq-517.md is
deleted: its only evidence is the line `[log] unbracketed: log-gate:
FAILED: cpu6 seq 517 ...`, which is log_reserve_window_negative's own
eprintln on a pass — the designed refusal. 517 is 5 + 512: the burst is
512 records reserved ahead of the interrupted one on a shard holding
four boot records, and one more boot record moved it to 518, which is
the "fixed position" the issue read as a defect. The sched_stress red
it names shared a run with that line and nothing more.

issues/kernel/deleting-logstorm-and-lognest-left-two-log-claims-unverified.md
is deleted: both claims are checked again. The K3 stage is deleted
from the-kernel-still-creates-threads.md: the logstorm and lognest
threads are gone and the checks moved to syscall-context producers.
K6 is blocked on K2, K4 and K5.

Eight test manifests justified test-runner's logread by the log gate,
which none of those boots runs; the comments go and the grants are
filed as issues/isolation/test-runner-holds-logread-where-no-log-builtin-runs.md.
The echo-spawn and negative-control-timeout issues' exits name the
restored sites.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts, every hunk of each side accounted for:
- tests/doomcase/system.toml: main deletes the case; this branch only
  removed its logread comment, so the deletion stands, and the logread
  issue no longer lists it (seven manifests, not eight).
- tests/doommusiccase/system.toml: main's shortened namespace comment,
  without the logread comment this branch removed.
- tests/common/logread.rs: this branch's STORM_GATE beside main's
  one-line CEILING comment.
- userland/test-runner/src/log_gate.rs: main deleted the guest's 30 s
  CEILING ("no QEMU test measures time"); it goes here too, with its
  elapsed check and the Instant/Duration imports. The host's 60 s
  ceiling is what reds a gate that never finishes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lands inside it on every run

At 8910046 the gate's non-vacuity check ("concurrent", storm records
taken by a read while the producer had not finished) held only when the
scheduler happened to interleave the reader and the producer. One TCG run
at --smp 2 had the producer finish all 1024 calls before the reader
reached a storm record, and the gate refused: a timing verdict, which
main's #562 rules out of QEMU tests.

The producer now stops after HANDOVER (64) records and waits on a
channel until the reader has taken a storm record, then emits the rest.
The reader sends only after it has loaded the producer's counter for
that read, so that read counts as concurrent on every run: 64 is below
the target, and the producer cannot move until the send. The wait is
bounded by HANDOVER_WAIT (30 s, inside the host's 60 s) and fails
loudly, naming the reader that never arrived.

Controls, deterministic both:
- join the producer before the first read: the producer times out at
  the handover and the gate reports it;
- the same with the handover deleted: the producer finishes before any
  read and the concurrent check refuses, as at 8910046.

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

Japabu commented Sep 29, 2026

Copy link
Copy Markdown
Collaborator Author

Review, round 3, at daca039

Gate: mergeable MERGEABLE. CI host is SUCCESS on run 36504484626, whose headSha is daca039. The orchestrator's guest runs at daca039 are in orch-runs/586r3-*.log: log_conservation_smp2 EXIT=0 3 of 3; log_nested_emit, log_reserve_window (both arms) and log_poll_outlives_a_close EXIT=0; Fast 228/228 EXIT=0; mut-nest-no-sti, mut-join-first and mut-join-first-no-handover EXIT=1.

Net: +251 / -570. Production (kernel/, toyos-abi/) is +23 / -113. Tests (tests/, userland/test-runner/, src/build.rs) are +196 / -402. Issues are +32 / -55.

No kernel thread: git grep kthread::spawn -- kernel finds only iod.rs:15 and log/console.rs:66, which are K5 and K4. MAX_KERNEL_TASKS is 2 in every build.

Earlier blockers

  • R1 nested.rs inline in SYS_LOG_READ: CLOSED. 586r3-log_reserve_window passes both arms, log_nested_emit passes, and mut-nest-no-sti is red with "the bracket was removed and the log gate passed anyway".
  • R1 conservation law with a userland producer: CLOSED as briefed. mut-join-first is red, but only through the 30 s bound that the first BLOCKER below removes.
  • R1 K3 marked done with its condition unmet: CLOSED. Both checks are back.
  • The orchestrator's round-2 "raced nothing" red: CLOSED by the handover. Green 3 of 3, and mut-join-first-no-handover is red with the same refusal.
  • handle_lifetime: 5 of 5 green at daca039 and 5 of 5 at main 8a8fe27. Accepted as not this branch's.

BLOCKER

  • userland/test-runner/src/log_gate.rs:94,292 — recv_timeout(HANDOVER_WAIT) is an in-guest deadline that decides a verdict, and No QEMU test measures time, and audio is judged on metal only #562 ruled those out. No QEMU test measures time, and audio is judged on metal only #562's words: "a QEMU test measures no time … recv in place of recv_timeout". No other recv_timeout remains under userland/test-runner or tests/toyos-rust-tests on origin/main. The bound also contradicts the row comment at tests/toyos.rs:1080 ("not one of them reads a clock").
    • Fix: use taken.recv(). Map RecvError to the existing "the reader ended before it took a storm record". Delete HANDOVER_WAIT and RecvTimeoutError.
    • A reader that never arrives then hangs, and the host's 60 s CEILING reds it. That is No QEMU test measures time, and audio is judged on metal only #562's only clock.
    • Re-measure mut-join-first: it must now go red as a stall at the ceiling.
  • userland/test-runner/src/log_gate.rs:218-220, tests/common/logread.rs:83 — concurrent is at least 1 by construction. The handover read counts even when the producer is parked in recv.
    • Measured: 586r3-log_conservation_smp2-2 has concurrent=64, which is HANDOVER and also BATCH. Only the handover batch counted.
    • Mutation that stays green: right after a successful handover.send(()), add join(producer.take())?;. The reader then takes nothing until the storm has ended. log_conservation_smp2 must turn red and does not.
    • Fix, option 1: count a read as concurrent only if produced advanced across it. Load the counter before tail.read and after it.
    • Fix, option 2: delete concurrent, both of its refusals, and the claim that the reader "runs beside it".
  • userland/test-runner/src/log_gate.rs:521 (STORM_RECORDS), log_gate.rs:12-15 — Half the conservation law is not exercised on 2 of 3 runs. The ledger's lost half is exercised only when the ring laps this cursor. 586r3-log_conservation_smp2-1 and -3 report lost=0; -2 reports lost=449.
    • Mutation: delete self.lost += oldest.saturating_sub(want); at kernel/src/log/read.rs:85. The test stays green on every run that does not lap.
    • Fix: make the lap an event as well. After the handover, the reader waits on a second channel until the producer has emitted shards × SHARD_RECORDS + 1 more records, and only then reads again. The host asserts lost > 0.
    • The read.rs:85 mutation must then go red on every run.

NOTE

  • toyos-abi/src/syscall.rs:860 — Round 1 made LOG_PATTERNED depend on the owner's word, as a test-only ABI addition. The PR records no such word; the orchestrator should confirm it.

REMOVE

  • kernel/src/syscall/dispatch.rs:605-606 — The comment describes its caller. It is not a contract.
  • kernel/src/log/user.rs:49 — "rather than on a kernel thread" describes a past implementation.
  • userland/test-runner/src/log_gate.rs:21-22 ("so a read lands inside the storm on every run") and tests/common/logread.rs:100-101 ("runs beside it rather than only between its quanta") — Nothing checks either claim; see the second BLOCKER.
  • userland/test-runner/src/log_gate.rs:92-93 — The HANDOVER_WAIT doc goes with the first BLOCKER.
  • issues/build/timing-verdicts-ruled-off-qemu-have-no-metal-arm.md:76 — "STORM_SETTLE and" names a constant this branch deletes.
  • issues/isolation/test-runner-holds-logread-where-no-log-builtin-runs.md:17-19 — "Their comments gave the log gate as the reason … the comments are gone and the grants are not" narrates this change.
  • issues/kernel/a-spawn-of-echo-was-refused-with-an-error-nothing-names.md:42-43 and issues/build/log-reserve-window-negative-times-out-beside-other-guests.md:26-27 — The exit lines were rewritten although the sites they name were restored. Revert them to main's text.
  • PR body — Cut:
    • the "Round 3" section: its history and the handle_lifetime investigation belong in the commit message and the tracker;
    • "No guest has run this head.", which is false;
    • both bullets under "What I am unsure of", which the measurements above answer.

SEND BACK

Japabu and others added 2 commits September 29, 2026 05:28
…st clock decides a verdict

Round 3's review of #586 found three holes in the storm gate.

- The producer's 30 s `recv_timeout` at the handover was an in-guest
  deadline deciding a verdict, which #562 rules out. It is `recv` now:
  only a disconnect is an error, and the host's `CEILING` reds a reader
  that never arrives.
- `concurrent` was at least one batch by construction: the handover read
  counted even while the producer was parked. A read now counts only if
  the producer's counter moved across it, and only after the lap. The
  producer emits until such a read has happened and the reader sets
  `stop`, so the overlap is waited on rather than sampled. The guest's
  "raced nothing" refusal could no longer fire and is deleted; the host
  still refuses `concurrent=0`.
- `lost` was zero on two of three runs, so read.rs's `lost +=` was
  measured by nothing. After the handover the reader now blocks until the
  producer has emitted `shards * 512 + 1` more records, which puts more
  than a shard's worth into one shard whichever CPUs the producer ran on,
  and the host asserts `lost > 0`. `STORM_RECORDS` goes: the emitted count
  is the producer's counter.

REMOVEs: the `LOG_PATTERNED` arm's comment, the "rather than on a kernel
thread" clause in `log::user::read`, the unchecked "runs beside it" and
"a read lands inside the storm" claims, `STORM_SETTLE` in the
timing-verdicts issue, and the narration in the logread-grants issue.
The echo-spawn and negative-control-timeout issues are back to main's
text.

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

Japabu commented Sep 29, 2026

Copy link
Copy Markdown
Collaborator Author

Review, round 4, at 32d1792

Gate: mergeable MERGEABLE. CI host is SUCCESS on run 36519584415, whose headSha is 32d1792. The orchestrator's guest runs at 32d1792 are in orch-runs/586r4-*.log.

Net: +260 / -577. Production (kernel/, toyos-abi/) is +21 / -113. Tests (tests/, userland/, src/) are +212 / -411. Issues are +27 / -53. git grep kthread::spawn -- kernel finds only iod.rs:15 and log/console.rs:66, which are K5 and K4.

Earlier blockers

  • R3-1, the 30 s recv_timeout: CLOSED. git grep recv_timeout -- userland/test-runner finds nothing. The producer blocks in taken.recv(). 586r4-mut-join-first is red as "timed out after 300s", which is the host ceiling.
  • R3-2, concurrent was at least 1 by construction: CLOSED. The producer runs until a read after the lap takes storm records while its counter moves. 586r4-mut-join-after-handover (the round-3 mutation) is now red as a hang at the 300 s ceiling, with 6,351,956 console lines. The three green runs each report concurrent=64.
  • R3-3, lost was usually 0: CLOSED.
    • The green runs report lost=2593, lost=27058 and lost=3177, and the host now refuses lost == 0.
    • 586r4-mut-no-lost is red on the law itself: "conservation failed: the sequence numbers say 4377 record(s) were never read and the kernel counted 0". It is not red on a liveness or vacuity check.
    • The arithmetic holds against shard.rs:135. With oldest_readable = head - 512, shards × 512 + 1 records emitted while the reader is parked put at least 513 on one shard. That shard's oldest is then above any cursor the reader held.

BLOCKER

None.

NOTE

  • userland/test-runner/src/log_gate.rs:214,226,235-236,241 — Every ? that leaves gate after the lap leaves stop unset. The producer then emits forever inside test-runner, so a torn-record refusal floods the console after its verdict. mut-join-after-handover shows the rate: 6.3M lines. Set stop on every exit from gate, for example with a drop guard.
  • tests/common/logread.rs:89 — The host's concurrent == 0 clause is unreachable. The guest cannot print log-gate: OK with concurrent at 0, because the producer only stops once it is positive. Delete the clause, or keep it knowingly as a duplicate.
  • userland/test-runner/src/log_gate.rs:229 — Nothing measured goes red on moved. The patch - if after_lap && moved && took > 0 { / + if after_lap && took > 0 { stays green at --smp 2. The liveness hang carries the concurrency claim, and moved only reports it.
  • userland/test-runner/src/log_gate.rs:91 — This is a third hand copy of the kernel's 512, after logread.rs:219 BURST. A kernel change would red loudly through lost == 0, so this is not a blocker.

REMOVE

  • PR body, "## Guest runs" — It reports daca039 and says "The orchestrator re-runs them at this head". Both are stale.
  • PR body, "The guest's 30 s CEILING is deleted, as main's No QEMU test measures time, and audio is judged on metal only #562 deleted it." — This is history of the branch.
  • PR body, "What I am unsure of" — It speculates about a one-CPU schedule that no test boots.

LAND AFTER NAMED CHANGES

@Japabu
Japabu added this pull request to the merge queue Sep 29, 2026
Merged via the queue into main with commit 2945662 Sep 29, 2026
1 check passed
@Japabu
Japabu deleted the wt/toyos-nokthreads branch September 29, 2026 06:58
Japabu added a commit that referenced this pull request Sep 29, 2026
One conflict, kernel/src/arch/aarch64/boot.rs, both hunks kept: main's
(#583) new `report_counter_origin`, empty on AArch64 because no register
says where the generic timer counts from, which main.rs's
`report_power_on` calls on both architectures; and this branch's
`timer()`, its doc and its body (the EL1 virtual timer, logged, stopped
until the scheduler arms it) in place of main's `owed!` stub.

#583's KernelArgs layout word needs nothing on the AArch64 side: the
loader writes it in the portable bootloader/src/main.rs, the kernel
refuses a foreign one in the portable `kernel_main`, which AArch64's
`_start` reaches, and that `_start` reads its four fields by
`offset_of!`, so the layout moves under it by construction.

Auto-merged, each checked against the branch's own hunk: actuator.rs
(main's layout actuator beside this branch's irq-storm and timer-floor),
main.rs (main's layout refusal and power-on report beside this branch's
`mod hw` and headless GOP), x86_64/boot.rs (main's UTC `clock` and
`report_counter_origin` beside this branch's irq-storm/timer-floor
refusal), aarch64/mod.rs (#586 drops log-shared-reservation's window
from `percpu_fetch_add`; this branch's percpu.rs still calls
`log::nested::reserve_window`, which main keeps), clock.rs,
sched/kthread.rs, src/build.rs, src/sourcegate.rs, tests/toyos.rs.
kernel/src/hw.rs is this branch's alone: main touched neither it nor
either architecture's hw.rs. The rust gitlink takes main's 9c3eea44.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Japabu added a commit that referenced this pull request Sep 29, 2026
Brings #583, #586, #593, #597, #555, #600, #610 and #611. Every conflicted
hunk, and where it went:

- rust: 1471893e39c, which merges main's pin 9c3eea441d8 into this branch's
  62fa74d7a50 with no conflict (main's three bootstrap commits and this
  branch's six std files do not meet). Pushed to ToyOSOrg/rust wt-toyos-fsd.
- kernel/src/actuator.rs: #583 deletes `rtc-zone-east`, and that deletion
  stands. This branch's doc for `leak-rollback-selftest` stands, since the
  FAT reopen control went with the kernel's FAT adapter.
- kernel/src/fat32_adapter.rs (modify/delete): the deletion stands. #583's
  hunk made FAT stamp UTC (`clock::utc_secs`) with the refusal reason at the
  site. FAT stamping is fsd's on this branch, so it is carried there:
  `local_secs`, which recovered a zone through `toyos_wallclock::resolve`
  (deleted by #583) and cited logd's `wall.rs` (deleted by #583), becomes
  `utc_secs`, `clock_epoch()` alone, with main's reason. fsd no longer
  depends on toyos-wallclock; userland/Cargo.lock drops the edge.
- kernel/src/sched/kthread.rs: #586 deleted `lognest` and `log-storm`, and
  this branch deleted `iod`, so klogd is the one kernel thread in every
  build: MAX_KERNEL_TASKS = 1, with no feature split.
- issues/kernel/the-kernel-still-creates-threads.md: #586 met K3 and deleted
  it; this branch meets K5 and deletes it. K6 is blocked on K2 and K4.
- userland/logd/src/main.rs: #583's `boot_secs` rename, beside this branch's
  `Published::new()`, which takes no argument here. The `owed` and
  `retrying_since` fields stay deleted (this branch).
- userland/logd/src/serve.rs: this branch's `serving` flag, with #583's
  `boot_secs`.
- tests/test-durations: #586 deleted `log_conservation_smp1` and this branch
  deleted `log_backing_read_error`; neither row stays.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U6SVYFkdvV2t38KzNrESxs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant