Repository navigation
logkeeper: the console no longer waits for /log to open - #1195
Conversation
acpi_press_is_one_sci stalled on every run since #1175: its console carried nothing past the UART's hand-over. QEMU's 16550 trace of the stalled boot (diagnostic run 42036387265) shows the userland driver's only access after the kernel's last inline byte is one IER write of 0x01, its `Machine::open`; no LSR or IIR read follows, so `Line::serve` never ran and the driver was parked in `logkeeper_api::carry`. Both vCPUs were idle (`HLT=1`, IF set). logkeeper answers no carrier and grows no replay until its loop runs, and its loop ran only after `Volume::open`, whose `read_dir("/log")` waits for the LOG file server. The supervisor starts that server with storage, which waits for the root bridges' memory to be declared, for at most 30 s (`declaration::BOUND`). The press probe holds the `acpi` claim and declares nothing, so the console was silent for the whole bound, past the harness's 17 s. Before #1175 the kernel's klogd carried the console without logkeeper, so the wait never showed there. The volume now opens on a thread of its own. The loop, the carriers and the `log` port run from the start; the loop adopts the volume once the thread answers, outside a stop, and the file takes the boot whole from the replay before each round after. A stop that comes first says /log had not opened. `inspect` reports `opening` meanwhile. This was a hang, not data loss: the lines were held in the rings and the replay and carried late once storage gave up, and nothing a channel had taken was lost. The silence was bounded only by the declaration's bound, and not at all for a LOG server that takes the request and never answers. The same wait held back the panel console on metal, which reads the log through the `log` port: for the declaration and the LOG mount on a normal boot. The loop's own file writes still wait on the LOG server; recorded in issues/the-console-waits-on-logkeepers-file-writes.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of #1195 at 883809dI read the commit, the full diff against its base 6755d1d, all of Architectural assessmentThe diagnosis holds up. Before this change, logkeeper's Threading. The volume thread shares one Should the console depend on the file at all? It should not. After this PR the dependency is indirect: the loop is both the replay's only appender and the file's synchronous writer. The clean final shape makes the file a consumer of the replay with its own offset, on its own thread, like the readers and carriers. The opener thread becomes the file's writer, and the stop asks it to reach #1190. I recommend landing #1195 first. It is small, self-contained and fixes a red row that #1175 introduced. #1190 is a large draft that also spans fat32, fileserver, nvme and shell. #1190 then rebases onto it like this:
The next step after both is the writer-thread move above. #1190's own description should stop implying it settles the volume path while the loop still blocks on Net production change: about +75 lines of logkeeper code, +16 of test, and +26/-45 in issues. Proportionate. FindingsBLOCKER 1.
The fix is one line: call BLOCKER 2. "each is a thread with an offset into what the file already has" and "appends each round's lines to one Replay after the file has them" no longer hold while the file is closed. The paragraph added at main.rs:50-54 says the opposite in the next sentence. Change both to say that readers and carriers hold an offset into the replay, and that the replay follows the file once it is open. Or delete the clauses. SUGGESTION. SUGGESTION. SUGGESTION. Evidence. The runs, revisions and control are as claimed, and the control is the change reverted onto its own base. Host CI at 883809d is claimed green with a log. No metal run was made. I accept that, because the changed logic is hardware-independent and The issue close is correct. The deleted record's exit (cause named and fixed, row green) is met, its slug is cited nowhere outside SEND BACK |
…ounds `flushed` adopts first: an opener that answered while the flush's rounds ran left the file empty and had the stop say /log never opened, though LOG_OPENED was already on the console. The comments that said the replay and its readers trail the file now say so only once the file is open; the header drops the supervisor's sequencing, and the residual's record names its exit's shape and #1190 as its vehicle. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review r2 of #1195:
|
…om the replay #1190's logkeeper rounds r1 to r5 (1815b94, 7d45162, a99667b, 7011568, bc12850), rebased onto #1195's opener thread and #1196's stop mark as #1195's reviewer planned. - A refused create, append, sync or continuation is asked again (`policy::Retry`, 1 s doubling to 64 s). `Volume` keeps the file's bytes (`size`, `durable`) apart from the replay's offsets (`from`, `through`, `durable_through`). `catch_up` opens the part again, cut to its last sync, or rebuilds it whole where it is shorter, and gives it what it is owed from the replay (`Hub::owed(at, max)` → `store::Owed`), a part's worth a round, through the ordinary part-sized writes and continuations. A gap the replay let go of is said in the file only. - The stop (`Volume::finish`) catches up within `STOP_CATCH_UP`, half the supervisor's flush bound, syncs what it wrote, and says any shortfall in the file and on the console; a refusal there leaves the part lost and the count from the last sync. - #1195: its opener thread stays; `adopt` only takes the volume, whose file stands at the replay's start, so the next round's catch-up gives it the boot so far; #1195's own catch-up loop, `Hub::owed(&mut u64)` and `serve::Owed` go. A volume still opening at the stop is waited for within the same `STOP_CATCH_UP`, then given the boot by `finish`, or said not to have opened. - #1196: the stop mark is written only over a part that is open, since a refused volume's file is not the boot's whole log. Host tests in `store.rs` (27 in all): the bookkeeping through an unsynced write, a replay gap, a refused continuation and a part cut behind its back; a refused first create; a stop with and without time; a stop whose reopen is refused after an unsynced write. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Problem
acpi_press_is_one_scistalled on every run since #1175 (three of three, main included). Its console carried nothing after the UART's hand-over: no supervisor line, no program line.Cause, read off the guest
A diagnostic run of the row on main's code, plus QEMU's 16550 trace and QMP on the stall, showed:
0x01, itsMachine::open. No LSR or IIR read followed, soLine::servenever ran. The driver was parked before its loop, inlogkeeper_api::carry, waiting for logkeeper's answer.HLT=1,RFL=0x246, CR3 the kernel's), twice, 2 s apart.The code explains why carry was never answered. logkeeper runs
Volume::open→read_dir("/log")before its loop, its carriers and itslogport. A request on a role's port waits for that role's server. The supervisor starts the LOG file server only with storage, and storage waits for the root bridges' memory to be declared, for at mostdeclaration::BOUND= 30 s. The press probe holds theacpiclaim and declares nothing, so every console channel stood silent for the bound, past the harness's 17 s. The other acpicase rows looked fine only because their boots end within a second and the stop's replay covers the gap. Before #1175 the kernel's klogd carried the console without logkeeper.Severity: hang, not data loss. Nothing a channel took was lost: the lines waited in the rings and the replay. By the code, they would reach the console once the bound expired and the supervisor closed LOG's ports. That was not observed: the harness gives up at 17 s. For a LOG server that takes the request and never answers, the wait had no bound at all.
Change
logkeeper opens the volume on a thread of its own:
logport run from the start.Hub::owed), before each round after. A gap the replay let go of is said in the file./loghad not opened.inspectreportsvolume.state = openingmeanwhile.The closed record's durable rule now lives in logkeeper's module header: nothing waits for the volume to open.
Residual, recorded rather than fixed: the loop's own file writes and syncs still block on the LOG server. A server that is alive but wedged would silence the console the same way, with no failure bound. It is read from the code, not reproduced:
issues/the-console-waits-on-logkeepers-file-writes.md.Overlap with #1190 (draft,
dataloss-followup). #1190 reworks the same volume code: the file keeps its own offset into the replay and is asked again after a refusal. It still opens the volume before the loop, so it does not fix this stall. Whichever lands second should adopt the other's mechanism: #1190'scaught_upsubsumes this branch'sadoptcatch-up loop, and this branch's opener thread is what #1190's first open needs. Both add aHub::owed.Metal
The T14 has no 16550 (its loopback reads
0xFF), so the UART driver exits and the UART path does not exist there. The same wait did apply to the panel console, which reads the log through logkeeper'slogport, served only after the volume opened. On a normal boot that hold-back lasts for the declaration and the LOG mount, about a second bydeclaration::BOUND's note. A LOG server that hangs would make it indefinite. This is inferred from the code; no metal run was made. No metal row isolates the wait, and the press row needs QEMU's power button.Checks
All guest runs are
guest-rows.yml, x86-64 KVM. Evidence is onpr-1195, with the runner's home and hostname masked.-trace serial_*and QMP on the stall (patch, never for landing)acpi_press_is_one_scithe_log_lands_on_the_boot_stick,nvme_disk_bootscargo run -- --ci host(macOS aarch64)a_late_file_is_owed_the_boot_whole_and_what_was_let_goNegative control: the change reverted onto its base. The row stalled three of three without it: 42010705757 and 42016111039 at #1187's head 0383550, and 42025888487 at main b27dd3e. 0383550's tree differs from this branch's base 6755d1d only in
issues/and instruction files. The row at 6755d1d itself, this branch's base without the change, stalls the same way: STALL at 17 s, the console ending atBoot: complete(42049438240).Review round 1 at 1131a5a (
flushedadopts a volume that opened during the stop's rounds; comments corrected):acpi_press_is_one_sciandthe_log_lands_on_the_boot_stick, 2 passed (42068104021).cargo test -p logkeeper23 passed. Earlier results stand for what this round did not touch: the round changed onlyflushed's start, comments and the record.No aarch64 guest run: the change is userland logkeeper code, which is architecture-independent and touches no shared kernel code.
🤖 Generated with Claude Code