Artemis Milestone 2h: hot-detach -- 2h complete
blk_subsys_detach_device() (block_subsystem.c) walks the device chain, refuses removal of anything but the current tail (a mid-chain removal would corrupt every later slot's start_lbn -- this architecture's own doc already argues USB stays last specifically to avoid that), unlinks, shrinks total_user_lbn, closes and frees the slot. Discards rather than flushes dirty state -- the device is physically gone by the time this runs (PORTSC disconnect only). Trigger wiring mirrors the attach path: bot_msc_attached (set only once attach actually succeeds) gates a new bot_msc_detach_pending flag set at PORTSC disconnect (not Disable Slot completion, which is conditionally skipped and would miss concurrent connect/disconnect pairs), consumed in sk_repl_idle(). Advisor flagged the real hazard ahead of time: block_words.c's VM block window (blk_vm_lbn[]/blk_vm_cbuf[]) can go stale across a detach then a same-LBN re-attach, and suggested a pointer-identity re-check in blk_vm_load() as a minimal fix. That fix was implemented, then directly falsified by its own designed-for-this test: attach a blank device, read a block (populating the cache), detach, re-attach a device with distinct content at the identical LBN, read again -- served stale content from the first device. Root cause, confirmed live: glibc's allocator hands free(slot) straight back to the very next same-size calloc(), so the "fresh" and stale pointers were bitwise identical despite being two different devices. Fixed properly with a monotonic blk_subsys_epoch() counter (bumped on every attach/detach) checked by a new blk_vm_check_epoch() helper at the one choke point (blk_vm_find(), plus blk_vm_flush_all() which reads the same arrays directly) that covers every path touching the window cache -- unfooled by address reuse. Verified live with a new disk/usb-thumbdrive-test2.img fixture (distinct content from the existing blank test image): attach A, read (cache hit populated), detach, re-attach B at the same LBN, read again -- correctly ran a fresh device read and returned B's real content, not A's stale cached zeros. The failing pointer-comparison attempt's own capture log kept as evidence, not deleted. All three architectures re-verified clean. FABRIC-2.md Section X 2h marked complete -- enumeration through hot-detach all live and verified; only WRITE(10) (2g's own still-open item) remains unimplemented in the driver, not blocking anything here. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
3b085dd875
commit
af267a52a6
@@ -225,6 +225,26 @@ int blk_subsys_init(VM *vm, uint8_t *ram_base, size_t ram_size);
|
||||
|
||||
int blk_subsys_attach_device(struct blkio_dev *dev);
|
||||
|
||||
/* Milestone 2h hot-detach. Refuses (BLK_EINVAL) unless dev's slot is the
|
||||
* current chain tail -- see this function's own doc comment in
|
||||
* block_subsystem.c for why. Discards any dirty cache/BAM/vol_meta state
|
||||
* rather than attempting to flush it (the device is already physically
|
||||
* gone by the time this is called). Returns BLK_ENODEV if dev isn't
|
||||
* attached, BLK_EINVAL if dev is NULL or not the chain tail.
|
||||
*/
|
||||
int blk_subsys_detach_device(struct blkio_dev *dev);
|
||||
|
||||
/* Monotonic counter, bumped on every attach/detach (Milestone 2h). A raw
|
||||
* pointer comparison against a blk_get_buffer() result cannot reliably
|
||||
* detect a same-address device swap (glibc's allocator can hand back the
|
||||
* exact address just free()'d by a detach to the very next attach's
|
||||
* calloc()) -- callers that cache a blk_get_buffer() result across calls
|
||||
* (block_words.c's VM block window) must instead compare this epoch
|
||||
* against the value they last observed, invalidating their whole cache on
|
||||
* any change rather than trusting a stored pointer's identity.
|
||||
*/
|
||||
uint64_t blk_subsys_epoch(void);
|
||||
|
||||
int blk_subsys_shutdown(void);
|
||||
|
||||
uint8_t *blk_get_buffer(uint32_t block_num, int writable);
|
||||
|
||||
@@ -243,6 +243,26 @@ typedef struct {
|
||||
* xhci_poll_events()" constraint) then blk_subsys_attach_device(). */
|
||||
uint8_t bot_msc_attach_pending;
|
||||
uint32_t bot_msc_attach_slot_id;
|
||||
/* Set by sk_repl_idle() once blk_subsys_attach_device() actually
|
||||
* succeeds (not by the SET_CONFIGURATION handler itself -- attach can
|
||||
* still fail, e.g. a bad capacity query, in which case there is
|
||||
* nothing to detach later). Read by the PORTSC disconnect handler
|
||||
* below to decide whether this disconnect needs a block-subsystem
|
||||
* detach at all -- a device that never successfully attached (or that
|
||||
* was already detached) produces no spurious detach flag. */
|
||||
uint8_t bot_msc_attached;
|
||||
/* Set by the PORTSC disconnect handler (see xhci_poll_events()'s own
|
||||
* disconnect handling) only when bot_msc_attached is set -- same
|
||||
* flag+consume-in-sk_repl_idle() shape as bot_msc_attach_pending,
|
||||
* chosen deliberately over hooking the Disable Slot completion:
|
||||
* disconnect is the unambiguous signal, while Disable Slot is only
|
||||
* even issued when connect_state == XHCI_CONN_IDLE (see the "command
|
||||
* ring busy" skip path) and would silently miss a detach otherwise.
|
||||
* No xhci_bot_wait_for_idle() call is needed for detach itself (no
|
||||
* device round-trip -- it's local block_subsystem.c bookkeeping), but
|
||||
* consuming it in sk_repl_idle() anyway matches the attach path's own
|
||||
* shape and keeps xhci.c decoupled from block_subsystem.c. */
|
||||
uint8_t bot_msc_detach_pending;
|
||||
|
||||
/* Deferred chaining: a doorbell ring (new control transfer) must
|
||||
* never happen synchronously from inside xhci_poll_events()'s event-
|
||||
|
||||
@@ -460,6 +460,8 @@ typedef struct VM
|
||||
uint8_t *blk_vm_cbuf[BLK_VM_SLOTS]; /* C buffer pointer from blk_get_buffer */
|
||||
uint8_t blk_vm_dirty[BLK_VM_SLOTS]; /* 1 = vm->memory modified since last sync */
|
||||
int blk_vm_next; /* Round-robin eviction cursor */
|
||||
uint64_t blk_vm_epoch; /* blk_subsys_epoch() as of last cache validation --
|
||||
* see block_words.c's blk_vm_find() */
|
||||
|
||||
/** @name Physics Hot-Words Cache
|
||||
* @{
|
||||
|
||||
Reference in New Issue
Block a user