filesys: route packets by unit; consolidate per-unit state into FilesysUnit#172
Merged
Merged
Conversation
The handler reads its unit from the startup packet's DeviceNode once and passes it in D2 on every trap. The host routes by that unit directly, dropping the MsgPort->unit map and the "first packet on a port" startup heuristic. The MsgPort is still passed (A1) and stamped into the dn_Task/dol_Task/fl_Task fields DOS uses to reach the handler, but is no longer a lookup key. This also lines up with a future per-unit register interface, where the unit is implicit in which register is written. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
FilesysHle held several parallel per-unit maps (volumes, device_nodes) plus locks/files keyed globally but scanned by unit. Replace them with a Vec<FilesysUnit> indexed by unit, each owning its MountSpec, handler MsgPort, DeviceNode/volume addresses, and its own files and locks. Now that packets route by explicit unit, every lookup goes straight to the owning unit instead of a map keyed by unit or a linear scan. The handler MsgPort is captured into the unit at the startup packet, so alloc_lock/build_volume_node read it from there and no longer take it as an argument. The board-wide lock pool (free_slots, pool_next) and the open-file cookie counter stay on FilesysHle, since they are shared across units. Expansion init still rebuilds from a fresh default, preserving only each unit's MountSpec. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
handle_packet and its helpers (resolve, lock_path, fill_fib, dir_listing, alloc_lock, build_volume_node) are now methods on FilesysUnit, operating on their own unit's state. FilesysHle::handle_packet shrinks to a dispatcher: bounds-check the unit, then delegate. board_base is passed into the unit methods, so FilesysUnit needs no back-reference to the owner. Each unit owns a LockPool: a fixed, non-overlapping slice of the board-window FileLock pool. The slices are sized by the board's *maximum* unit count and sit at stable offsets, so a unit's slice -- and the lock addresses handed out from it -- never move when units are added or removed at runtime (the eventual mount/eject-on-the-fly goal). The open-file cookie counter likewise moves per-unit. LockRec drops its now-redundant unit field, since every lock lives in its owning unit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
16 was excessive: real storage controllers carry fewer (SCSI's 7 targets plus host, IDE two, floppies four). Halving the maximum also doubles each unit's fixed board-window lock-pool slice to ~170 locks. The guest handler no longer re-clamps the mount count against MOUNT_MAX_COUNT: the host writes that count into the mount table and already bounds it (board_image asserts, config rejects over-limit configs), so the check was guarding against a state the host cannot produce. MOUNT_MAX_COUNT is now host-only; the guest board header drops its duplicate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
d759375 to
0b31fe2
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
This PR refactors the HOSTFS/filesys packet routing and state management to be explicitly unit-indexed end-to-end (guest passes unit in D2; host dispatches by unit), while consolidating all per-mount runtime state into a FilesysUnit structure and introducing a per-unit lock-pool slice for stable guest lock addresses.
Changes:
- Route filesys packets by mount unit (D2) instead of
MsgPort -> unitinference. - Consolidate per-unit state into
FilesysUnitand move packet handling/helpers onto the unit. - Cap host mounts at 8 units and remove the guest-side mount-count clamp.
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
src/filesys.rs |
Implements unit-based dispatch, introduces FilesysUnit + per-unit LockPool, and updates mount max to 8. |
guest/services/handler.c |
Caches mount unit from the startup packet’s DeviceNode and passes it in D2 on every packet trap; removes guest clamp. |
guest/services/copperline_board.h |
Removes the guest-visible MOUNT_MAX_COUNT define now that the host bounds mount count. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
LinuxJedi
approved these changes
Jul 13, 2026
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.
Follow-up refactor on top of the merged HOSTFS filesys work (#159). No
behavior change intended; a manual boot with four mounts still works.
What changed
Route packets by explicit mount unit, not MsgPort address. The guest
handler reads its unit from the startup packet's DeviceNode once and passes
it in D2 on every trap. The host routes by that unit directly, dropping the
MsgPort -> unitmap and the "first packet on a port" startup heuristic.The MsgPort is still passed (A1) and stamped into the
dn_Task/dol_Task/fl_Taskfields DOS uses to reach the handler, but is no longer a lookupkey. This also lines up with a possible future per-unit register interface,
where the unit is implicit in which register is written.
Consolidate per-unit state into a
FilesysUnitstruct. Several parallelper-unit maps (volumes, device_nodes) plus globally-keyed locks/files become
a
Vec<FilesysUnit>indexed by unit, each owning itsMountSpec, handlerMsgPort, DeviceNode/volume addresses, files, and locks. Every lookup now goes
straight to the owning unit.
Move packet handling onto
FilesysUnit; per-unitLockPool.handle_packetand its helpers become methods onFilesysUnit;FilesysHle::handle_packetshrinks to a dispatcher (bounds-check the unit,delegate). Each unit owns a
LockPool: a fixed, non-overlapping slice of theboard-window FileLock pool, sized by the board's maximum unit count and at
stable offsets, so a unit's lock addresses never move when units are
added/removed at runtime (the eventual mount/eject-on-the-fly goal).
Cap host mounts at 8 units, drop the guest-side clamp. 16 was excessive
(real controllers carry fewer). The guest no longer re-clamps the count:
the host writes and bounds it (
board_imageasserts, config rejectsover-limit), so
MOUNT_MAX_COUNTis now host-only.Testing
cargo test --lib filesys(6/6),cargo clippy --all-targetsclean🤖 Generated with Claude Code