Repository navigation
Supervisor times PCI claims: the declaration in the device set, and storage and homes before the rows that need them - #1164
Conversation
…ge and homes after it By the owner's ruling of 1–2 Nov 2026, the kernel no longer waits at claim time for the root bridges' declaration; the supervisor starts what needs it once the device set says it is held. Kernel and ABI: - `toyos_abi::devset::Header` carries the declaration's state (`Pending`, `Held`, `Withheld(Refused | Ended)`), bytes 1..3, refused by name when malformed. `declare` and `withhold` post the set's watch; the snapshot reads the generation before the state, so a change between is posted again. - A PCI claim made while nothing is answered is refused at once, `ClaimError::Undeclared`, `WouldBlock` to userland. Deleted with the wait: `DECLARATION`, `DECLARATION_DEADLINE`, `answered()`'s park and `aperture::Withheld::Unanswered`. Supervisor (`toyos_supervisor::declaration`, host-tested): - Where nothing can declare (no started `acpi` holder, and no started row that may mint the claim with the ACPI server built in), nothing waits: every row starts at once, storage first, no PCI driver, said once. - Else first: `logkeeper`, the `acpi` claim's holder, and where none is started a row that may mint the claim and hand it on (`device`), as a test boot's runner does. A program's output waits in its ring for logkeeper. - On the declaration held: the disk drivers, the block service, the file servers, then the session's home and every state directory, then every other row, so no row starts before its `HOME` exists; then every `drives` row. It says how long after its first rows the declaration came. - On the declaration withheld, or none by a 30 s bound: said once by name, storage and the PCI drivers are given up for the boot, the unstarted storage rows' ports are closed so a file request is refused rather than left waiting, and every other row starts with no storage. Restored by it: - `acpi_server_death` (T14) with its judge and its job, `acpi_release`, which now kills the server only once it has declared: on that boot the job makes the first `acpi` claim, and the log reaches the stick through the storage the declaration starts. acpicase gets its storage and console rows back. - The held-function guest arm, as `acpi_mediated`'s `held` arm on a boot whose first job's server declared: `acpi_holder_is_refused_a_held_functions_configuration`. The undeclared row is now `a_claim_before_the_declaration_is_refused_at_once`: a job's claim of each `edu` refused `WouldBlock` while the set reads `Pending`, and the supervisor's bound said. The build check is `every_config_that_claims_a_pci_function_has_a_declarer`: a started holder, or the server built in for a started `device` row to hand the claim. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review r1 of #1164 at
|
…of it, storage one state
- `toyos_supervisor::declaration::plan` decides which rows start first,
which are storage and which follow, over `services[1..]`: SYSTEM, started
before the manifest was read, is in no list, so a withheld or overdue boot
never closes its ports and the held path never starts it again. The
`child.is_none()` filter goes. Host-tested, with a control that lets the
plan include index 0 (it fails).
- The row predicates (`declares`, `is_block`, `is_disk_driver`,
`is_storage`) move there beside it. Whether a declarer exists is a manifest
fact, a row holding the `acpi` claim started or not, rather than the name
`acpiserver`; acpicase's acpiserver row declares the claim its job hands
it, and the build check reads the same fact.
- `Storage` is `Awaiting`, `Up { drivers }` or `Never`: a boot nothing can
declare on is `Up { drivers: false }`. `storage_started` goes.
- The startup sequence is written once, at `storage_up`; the module header
says what the module decides, and logkeeper is said to use no `HOME`
rather than to keep none.
- `acpi.rs`'s module doc and `device_claim`'s say a PCI claim before the
declaration is `WouldBlock`.
- `declaration()` matches the answer under the lock instead of cloning its
windows; the supervisor's line is one constant in the harness; a held
declaration read at the bound is host-tested; `acpi_release`'s header
says it claims off the boot processor where one of its tries runs there;
the death issue names #1164 as its holder.
Correction to `79473b788`'s message: the undeclared row asserts the
supervisor's word that nothing on that boot can declare, not its bound.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Judged off the T14's boot of acpicase at #1164's 79473b7: the job's claim asked `ACPI_ENABLE` from cpu2, the server declared the root bridges' memory before it was killed, the supervisor read the declaration 79 ms after its first rows and started storage, `ACPI_DISABLE` was written at the server's death, and the log reached the stick. The records that pointed at it say the row runs again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Review r1, answered at Required
Optional, taken
Optional, not taken
🤖 Generated with Claude Code |
Review r2 of #1164 at
|
… a row is started once by construction
- `storage_up` waits on the supervisor's file worker for the homes, the
state directories and the grants, and a service that ended meanwhile went
through `ended()` to `drive()`. `Storage` was already `Up`, so that reading
started the other `drives` rows before their homes existed. `Storage` now
has the window as its own state, `Starting { drivers }`: a service that
ends there is started again, but `drives` rows wait for `finish()`, which
`storage_up` calls once every other row is started. Storage's own disk
drivers start the window on `declared()`. Host-tested with a service ending
inside the window; a control that lets `drivers()` answer inside it fails.
- `start_service` asserts the row was never started instead of skipping one
silently: since round 1's plan, no caller passes one.
- `Plan` derives `Default` for the supervisor's construction;
`Storage::Up`'s doc and `acpi_release`'s header say what they mean.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Review r2, answered at Required
Optional, all taken
Checks
Metal
🤖 Generated with Claude Code |
Review r3 of #1164 at bd2a67e (focused on r2's required items)Architecture. A Item 1: re-entrance of
|
…tarts again `bd2a67e01` held every `drives` row back while storage and the homes are started, disk drivers among them. The window's own steps, the session's home, the state directories and the grants, go through the block service to a disk driver, so one that ended there was not restarted until `finish()`: the boot waited its 30 s and lost its homes. `Storage::may_start` now decides each row: every one once storage is up on a declaration, and inside the window a disk driver alone. `drive()` starts what it allows. `only_a_disk_driver_starts_inside_storages_window` holds both directions, and on a boot nothing can declare on, neither. Two controls fail it: disk drivers held back in the window, and every row let in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Re-review blocker, answered at Change:
Test:
Checks: 🤖 Generated with Claude Code |
|
Orchestrator verification of the r3 blocker fix at
Verdict: blocker resolved. I verified it directly rather than through a fourth reviewer pass. |
…before the declaration Main's start plan (#1164) put every row that neither keeps the log, holds the acpi claim nor may mint it after the declaration, storage and the homes, and the UART's driver among them. The kernel lets go of the UART at its hand-over, before userland runs, so a machine whose only console is its UART said nothing from userland's start until storage was up, and nothing at all where storage hung. The driver needs neither the declaration nor a home. The plan starts first the rows that carry the log, by what they hold: the log's origins (logkeeper) and the console's input (the UART's driver), in place of logkeeper's name. A host test pins the UART's driver in `first`; the plan's old rule reds it. The refused-stop comment and record now say only the lines the UART had already taken repeat, and the record's exit paragraph is wrapped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Status: draft. On
main6dccc4956(#934), head12e913653; it answers reviews r1 and r2 and the re-review's blocker. Metal at0ade582c4judges green on both benches,acpi_server_deathamong them.bd2a67e01and12e913653change only the restart-during-startup window and an assert, which no metal row reaches.Problem
#934 made every PCI claim wait in the kernel, for up to 30 s, for the root bridges' declaration. The supervisor started the disk drivers right after the
acpiholder, so on a boot whose first holder is a job, every claim waited out the deadline and the boot had no storage. Two tests were lost to that:acpi_server_death(T14), whose log reaches the stick only through storage;acpiholder.The owner ruled (1–2 Nov 2026) that claims are the supervisor's to time: the kernel's claim-time wait goes, and the supervisor starts drivers off the device set once the declaration is in it.
What this changes
Kernel and ABI
toyos_abi::devset::Headercarries the declaration's state:Pending,Held, orWithheld(Refused | Ended). It is bytes 1..3, refused by name when malformed (host-tested).declareandwithholdpost the set's watch. The snapshot reads the generation before the state, so a change in between is posted again.ClaimError::Undeclared,WouldBlockin userland.DECLARATION,DECLARATION_DEADLINE,answered()'s park, andaperture::Withheld::Unanswered.Supervisor: startup ordered by what each row needs.
toyos_supervisor::declaration::plandecides it, host-tested;main.rsfollows it, and the sequence is written once, atstorage_up. The order comes from manifest facts already stated, with no new flag.SYSTEM: the plan covers
services[1..], so SYSTEM, running since before the manifest, is in none of its lists.Storage's state:
StorageisAwaiting,Starting { drivers },Up { drivers }orNever.Startingisstorage_up's window. Inside it, a service that ends is started again, and so is a disk driver, since the homes the window makes go through it; no otherdrivesrow starts until the homes and every other row exist (Storage::may_start). So "no row starts before itsHOMEexists" holds for those restarts too, and the window cannot stall on its own disk. It is host-tested both ways, with a control for each. Every declared row'sHOMEis its state directory or the session's, on DATA, so a row starts only after itsHOMEexists. The exceptions are the rows the declaration comes through, and the log.Nothing to wait for: where nothing can declare, every row starts at once, storage first, with no PCI driver, said once. "Nothing can declare" means no started
acpiholder, and no started row that may mint the claim (device) to hand it to a row the manifest declares holding it. acpicase's acpiserver row now declares that claim. These are the virt images,undeclaredcaseanddiag.First:
logkeeper, theacpiclaim's holder, and, where no holder is started, a row that may mint the claim and hand it on. A program's output waits in its ring untillogkeeperreads it.On the declaration held:
drivesrows.The supervisor says how long after its first rows the declaration came.
On the declaration withheld, or none by a 30 s bound: said once by name. Storage and the PCI drivers are given up for the boot.
logkeeperon a withheld boot before this design closed them. SYSTEM is in no list, so it keeps its ports.Restored
acpi_server_death(T14 metal), with its judge.acpi_release, now kills the server only once it has declared.acpi_mediated'sheldarm:acpi_holder_is_refused_a_held_functions_configuration.acpi_release's server declares, so storage and the virtio-serial driver start.HeldFunctionby address and through ECAM.Tests and checks
a_claim_waits_out_an_undeclared_machinebecomesa_claim_before_the_declaration_is_refused_at_once. Its new job,pci_undeclared, readsPendingin the set and has eacheduclaim refusedWouldBlock, and the supervisor says nothing can declare.every_config_that_claims_a_pci_function_has_a_declarer: a started holder, or the server built in for a starteddevicerow to hand the claim.Profile::HeadlessNoConsolePortstays for acpicase's probe rows, whose holders declare nothing.Why the guest rows need QEMU
The device-set header and the kernel's refusal, the supervisor's ordering on a real boot, the held flag fed to the ACPI fence, and a probe holding the fixed hardware are a booted machine's. The decisions themselves are host-tested in
toyos-abi,toyos-pci,toyos-supervisorandtoyos-userbound. No bench boots without its ACPI server or can be made to withhold.Checks
Evidence: ToyOSOrg/evidence
pr-1164at31b14e6d5for the latest commit, and ateaa5aef54, with rounds 1 and 2 beside it insupervised-claims-r1/and-r2/.cargo test -p toyos-supervisor12e913653declarationtests (log)f96bbd779,12e913653cargo run -- --ci host12e913653cargo run -- --build-only, x86-64 and aarch64f96bbd779qemu-remote.sh)bd2a67e01, which the latest commit changes only in the window's restart rulevirt_reboot(4 rows) andvirt_first_entry, locallybd2a67e0179473b788's WIPacpi_press_is_one_sci,acpi_lock_given_back_on_one_cpu,acpi_mediated_access,acpi_supply_outlives_holder,acpi_holder_is_refused_a_held_functions_configuration,a_claim_before_the_declaration_is_refused_at_once,a_row_reaches_its_own_state_and_no_other,a_function_arrives_and_leaves,a_drives_row_holds_one_process_per_function,the_log_lands_on_the_boot_stickandsession_lifecycle. Between them they cover the withheld, overdue, held, no-declarer and ordinary paths.hosted_clang_hello, which builds LLVM for hours.Metal
0ade582c4(T14, AMD)acpi_server_death,acpi_tables_loaded,usbd_drives_the_type_c_controller: 3/3amdvi_usbd_drives_its_own_entry: 1/179473b7880ade582c4: the job askedACPI_ENABLEfrom cpu2, the supervisor read the declaration 79 ms after its first rows,ACPI_DISABLEwas written at the server's death, and the log reached the stick.bd2a67e01: it touches no path these rows read.Risks and open points
acpiholder never declares (issues/an-x86-machine-with-no-acpi-row-hands-over-no-pci-claim.md) now boots with no storage after 30 s, said once. Before, it had storage services and no disks.logkeeper, the holder, and adevicerunner on a boot whose holder is a job) find their state directory once storage is up. Each is one the boot needs before storage can come.🤖 Generated with Claude Code