FABRIC-3.md §I.2: FIRSTTOUCH + migration state machine (blk_meta_relocate_devblock)
Closes the block-subsystem punch-list item -- built exactly to §F.11's already-decided algorithm after re-verifying it against current blk_meta_t (a 2026-09-03 re-scoping note had wrongly claimed the chain fields no longer existed; they do, untouched by BMAPFMT). blk_firsttouch_claim(): one linear scan of Artemis's own device (new blk_get_first_disk_range(), correctly bounding the scan instead of the global multi-device LBN space), scattered-chain claim via prev_block/next_block/chain_length, owner_fp stamped on every member devblock, fails outright with no partial claim. blk_meta_relocate_devblock(): the real migration primitive -- bridges the existing FORTH-block-granularity blk_subsys_relocate_block() up to devblock granularity (BLK_PACK_RATIO=3, corrected mid-design), running it 3x and transferring blk_meta_t ownership fields. The "migration state machine" turned out to be just the 2 states BLK_FLAG_MIGRATING already reserved; the real design work was the trigger. Two were scoped in conversation (overflow onto Artemis; heat-based wear leveling); heat/wear-leveling is built and wired into sk_repl_idle() via blk_meta_t.write_count. Overflow is deliberately left open, precisely scoped (needs a slot-lookup-by-device-pointer call site threaded from WIREBIND) rather than guessed at. Also flagged, not fixed: BMAPFMT's owner_fp/CLAIMED and the pre-existing BAM allocator are two parallel, unreconciled accounting systems -- FIRSTTOUCH/relocate only touch the former. Verified 3-arch boot to ok> (amd64/aarch64/riscv64, each in the foreground); logs and DoE CSVs from this session's verification runs included per this repo's own audit-artifact convention. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
1d468a65b1
commit
4018fe8b04
+112
-1
@@ -306,7 +306,8 @@ enum {
|
||||
BLK_ENODEV = -4,
|
||||
BLK_ERESERVED = -5,
|
||||
BLK_EDIRTY = -6,
|
||||
BLK_ENOMEM = -7
|
||||
BLK_ENOMEM = -7,
|
||||
BLK_ENOSPC = -8 /* not enough free devblocks -- blk_firsttouch_claim() */
|
||||
};
|
||||
|
||||
/* Per-block BAM entry — one per user block in each device slot */
|
||||
@@ -374,6 +375,116 @@ uint64_t blk_subsys_epoch(void);
|
||||
*/
|
||||
int blk_subsys_relocate_block(uint32_t home_lbn, uint32_t target_lbn);
|
||||
|
||||
/* blk_get_device_range - the LBN range (start_lbn, count of 1 KiB FORTH
|
||||
* blocks) a specific already-attached device occupies (FABRIC-3.md §I.2,
|
||||
* 2026-09-04). Mirrors blk_subsys_detach_device()'s own dev-pointer slot
|
||||
* lookup, exposed publicly for the first time -- needed so a caller can
|
||||
* scope a scan/query to exactly one attached device rather than the
|
||||
* global, multi-device LBN space blk_get_total_blocks()/blk_get_meta()
|
||||
* otherwise address. Returns BLK_ENODEV if dev isn't attached.
|
||||
*/
|
||||
int blk_get_device_range(struct blkio_dev *dev, uint32_t *out_start_lbn, uint32_t *out_count);
|
||||
|
||||
/* blk_get_device_free_blocks - free vs. total 1 KiB FORTH-block count for
|
||||
* a specific already-attached device (same slot lookup as
|
||||
* blk_get_device_range()). RAM-backed slots (no on-disk vol_meta) report
|
||||
* out_total == out_free == 0, matching blk_mark_allocated()/_free()'s own
|
||||
* "!slot->raw_base" gate on touching free_blocks bookkeeping at all.
|
||||
* Returns BLK_ENODEV if dev isn't attached.
|
||||
*/
|
||||
int blk_get_device_free_blocks(struct blkio_dev *dev, uint64_t *out_free, uint64_t *out_total);
|
||||
|
||||
/* blk_get_first_disk_range - the LBN range of first_disk_slot(), i.e.
|
||||
* whichever disk-backed device blk_get_volume_meta()/blk_meta_zone_read()
|
||||
* already implicitly target -- Artemis's own disk.img in practice, since
|
||||
* it attaches at boot before any USB hotplug (the same boot-order
|
||||
* convention those two functions already rely on, not a new assumption).
|
||||
* FIRSTTOUCH (below) scans exactly this range, not the global LBN space.
|
||||
* Returns BLK_ENODEV if no disk-backed device is attached at all.
|
||||
*/
|
||||
int blk_get_first_disk_range(uint32_t *out_start_lbn, uint32_t *out_count);
|
||||
|
||||
/* blk_firsttouch_claim - FABRIC-3.md §F.11, decisions 1-3, built exactly
|
||||
* as scoped 2026-08-27 (re-verified against current blk_meta_t 2026-09-04
|
||||
* before building -- the chain fields below are real and untouched by
|
||||
* BMAPFMT's unrelated owner_fp/acl_allow/acl_ttl/flags field addition).
|
||||
*
|
||||
* One linear scan over blk_get_first_disk_range()'s devblocks (4 KiB
|
||||
* granularity -- one blk_meta_t per devblock, not per 1 KiB FORTH block),
|
||||
* via blk_get_meta(). Answers both questions the scan needs in the same
|
||||
* pass: does owner_fp already own a claim (not reused by this call --
|
||||
* every call makes a *new* claim of exactly `count` additional devblocks,
|
||||
* per §F.11's own text; an existing claim is a separate, not-yet-decided
|
||||
* question), and which devblocks are free (owner_fp all-zero, BLK_FLAG_
|
||||
* CLAIMED clear). A claim is a scattered chain, not a required contiguous
|
||||
* run -- links devblocks via blk_meta_t's prev_block/next_block/
|
||||
* chain_length fields; owner_fp is stamped onto *every* member devblock,
|
||||
* not just the head, so ownership reads locally from any member without
|
||||
* walking the chain. Fails outright (BLK_ENOSPC), no partial claim, if
|
||||
* fewer than `count` free devblocks exist.
|
||||
*
|
||||
* @param owner_fp 8-byte fingerprint (this session's convention: the
|
||||
* first 8 bytes of the claiming identity's 32-byte
|
||||
* VMIdentity.owner_pubkey -- owner_fp had no real
|
||||
* producer anywhere in this codebase before this,
|
||||
* only accessors, so this call site sets the
|
||||
* convention rather than following one).
|
||||
* @param count Devblocks to claim (whole devblocks, §F.4 decision
|
||||
* 4, already inherited, not revisited here).
|
||||
* @param out_chain_head The claim's identity to the caller: its
|
||||
* chain-head devblock number (a 1 KiB FORTH-block
|
||||
* LBN, the representative/first sub-block of that
|
||||
* devblock -- not a separate devblock-index
|
||||
* namespace; blk_get_meta()/blk_set_meta() already
|
||||
* resolve any LBN within a devblock to the same
|
||||
* shared blk_meta_t, so this is a valid handle for
|
||||
* blk_meta_relocate_devblock() below).
|
||||
* @return BLK_OK, BLK_ENODEV (no disk-backed device), BLK_EINVAL (NULL
|
||||
* arg or count == 0), or BLK_ENOSPC (not enough free devblocks).
|
||||
*/
|
||||
int blk_firsttouch_claim(const uint8_t owner_fp[8], uint32_t count, uint32_t *out_chain_head);
|
||||
|
||||
/* blk_meta_relocate_devblock - the real migration primitive (FABRIC-3.md
|
||||
* §I.2, migration state machine, 2026-09-04): moves one devblock's full
|
||||
* content and blk_meta_t ownership from home_devblock to target_devblock.
|
||||
* BLK_FLAG_MIGRATING is set on home_devblock for the duration -- the
|
||||
* state machine genuinely is just this one flag's two states (clear =
|
||||
* STABLE, set = MIGRATING), matching what BLK_FLAG_MIGRATING was already
|
||||
* reserved for.
|
||||
*
|
||||
* Devblock granularity (4 KiB) holds BLK_PACK_RATIO (3, not 4 -- the 4th
|
||||
* 1 KiB slice is the packed blk_meta_t metadata region) FORTH blocks
|
||||
* (1 KiB each); the actual content copy runs blk_subsys_relocate_block()
|
||||
* BLK_PACK_RATIO times, once per FORTH-block offset within the devblock
|
||||
* -- that function is the only existing content-relocation primitive,
|
||||
* and it operates at FORTH-block granularity, not devblock granularity,
|
||||
* so this function is the bridge between the two. On success, transfers
|
||||
* owner_fp/acl_allow/acl_ttl from home's blk_meta_t to target's, clears
|
||||
* home's (freed, BLK_FLAG_CLAIMED unset), and sets BLK_FLAG_CLAIMED on
|
||||
* target.
|
||||
*
|
||||
* NOT atomic across all BLK_PACK_RATIO FORTH-block copies -- a mid-loop
|
||||
* failure (e.g. blk_subsys_relocate_block()'s own reloc-table
|
||||
* exhaustion) leaves however many already succeeded in place, redirected, with
|
||||
* BLK_FLAG_MIGRATING still set on home_devblock and blk_meta_t ownership
|
||||
* NOT yet transferred. This is a known, documented limitation, not
|
||||
* silently accepted: true all-or-nothing atomicity across BLK_PACK_RATIO
|
||||
* separate relocation-table entries would need its own transaction log, out of
|
||||
* scope for this pass. A caller that hits a non-BLK_OK return should
|
||||
* treat home_devblock as still MIGRATING and not yet safe to reclaim.
|
||||
*
|
||||
* @return BLK_OK, BLK_EINVAL (home == target, or invalid devblock), or
|
||||
* whichever blk_subsys_relocate_block() error stopped the loop.
|
||||
*/
|
||||
int blk_meta_relocate_devblock(uint32_t home_devblock, uint32_t target_devblock);
|
||||
|
||||
/* blk_migration_idle_check - heat/wear-leveling migration trigger, meant
|
||||
* to be called once per idle tick (FABRIC-3.md §I.2, 2026-09-04). See
|
||||
* block_subsystem.c's own doc comment for the full scan/threshold
|
||||
* description and what's deliberately left open (overflow-triggered
|
||||
* migration). */
|
||||
void blk_migration_idle_check(void);
|
||||
|
||||
int blk_subsys_shutdown(void);
|
||||
|
||||
uint8_t *blk_get_buffer(uint32_t block_num, int writable);
|
||||
|
||||
Reference in New Issue
Block a user