Add per-slot CRC + campaign crash-resume (DOE-RESUME-COUNT)
FABRIC-3.md §XXXV.12: closes two real gaps found by walking the disk-to- PDF pipeline end to end. (1) doe_trial_slot_t had no integrity check of its own -- 64 slots share one devblock via a read-modify-write, so a crash mid-write to any slot could silently corrupt its siblings with nothing to catch it. Added an 8-byte CRC (same discipline the control header already had), computed/verified the same way. (2) The campaign itself had no crash-resume -- a reboot always restarted the whole 18-trial loop from index 0 even with good persisted data already on disk. Since the trial matrix is fully deterministic from (seed, n-reps), resuming needs no persisted "which trials ran" state, just a count: DOE-RESUME-COUNT (new kernel primitive, same pattern as DOE-PERSIST-TRIAL) walks the ring counting CRC-valid records for a given ISA; MU-EXEC-CAMPAIGN's signature changes to accept a start-idx and seeds MU-RUN-ID/the DO loop from it. Each RUN-*-CAMPAIGN entry word now calls DOE-RESUME-COUNT before launching. Both block-namespace-checked to fit existing headroom before committing to the design -- no new blocks needed. Real bug caught by the build, not shipped: an implicit compiler-inserted alignment byte before slot_crc silently broke the hand-computed trailing pad size (doe_trial_slot_size_check failed to compile). Fixed with an explicit alignment field, matching log_slot_t's own zero-implicit- padding discipline. extract_doe_region.py now verifies both CRCs with the REAL CRC-64/XZ algorithm from block_subsystem.c's compute_crc64() (ported and verified bit-for-bit against a native C reference this session), replacing §XXXV.11's incorrect guessed implementation. Verified: clean compile on all 3 ISAs (amd64/aarch64/riscv64 -- amd64 verified via isolated build only, not boot, since the real per-ISA campaign is currently live on the only QEMU instance this project's own rule allows), both capsules pass mkcapsule --lint, CRC64 bit-exact via native reference. Live boot-verification explicitly deferred until that campaign finishes or is interrupted -- not claimed as tested when it wasn't. Found (not missed) a real compatibility consequence: the running campaign's data is in the old pre-CRC format, so it reads as "corrupt" under the new extractor and DOE-RESUME-COUNT will resume it from 0, not mid-campaign -- no data loss, cheap redundant re-run of ~1-2 trials. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BWpNjdwPtFLuVLaAq44L9K
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
c535252907
commit
51940f49d0
@@ -117,7 +117,15 @@ typedef char doe_region_ctrl_size_check[(sizeof(doe_region_ctrl_t) == 4096) ? 1
|
||||
* doe_trial_slot_t - one trial summary record, exactly DOE_SLOT_SIZE bytes.
|
||||
* Field order: uint64_t pair first (natural 8-byte alignment at offsets
|
||||
* 0/8), then uint32_t fields (4-byte aligned from offset 16 on), then the
|
||||
* ISA tag, then trailing pad -- same alignment discipline log_slot_t uses.
|
||||
* ISA tag, then an EXPLICIT alignment field (_align_pad) so slot_crc lands
|
||||
* on an offset the compiler needs no implicit padding to reach -- same
|
||||
* "every field lands on its natural alignment with zero compiler-inserted
|
||||
* padding" discipline log_slot_t's own doc comment states. Leaving this to
|
||||
* an implicit trailing _pad array (as an earlier draft did) put slot_crc
|
||||
* right after an 8-byte char array at a non-8-aligned offset, so the
|
||||
* compiler silently inserted its own padding there instead and the
|
||||
* hand-computed trailing pad size no longer matched sizeof() -- caught by
|
||||
* this file's own static size-check failing to compile.
|
||||
*===========================================================================*/
|
||||
|
||||
#define DOE_SLOT_ISA_MAX 8u /* NUL-padded, e.g. "amd64", "aarch64", "riscv64" */
|
||||
@@ -133,7 +141,12 @@ typedef struct {
|
||||
uint32_t fail_count;
|
||||
uint32_t fleet_conserved; /* vm_physics_conserved() at trial end, 0/1 */
|
||||
char isa[DOE_SLOT_ISA_MAX];
|
||||
uint8_t _pad[DOE_SLOT_SIZE - (8 + 8 + 4 + 4 + 4 + 4 + 4 + 4 + 4 + DOE_SLOT_ISA_MAX)];
|
||||
uint32_t _align_pad; /* explicit -- brings the next field to offset 56 (8-aligned) */
|
||||
uint64_t slot_crc; /* covers every field above this one (§XXXV.12) --
|
||||
* a devblock holds DOE_SLOTS_PER_DEVBLOCK=64 slots via
|
||||
* one read-modify-write, so a crash mid-write to ANY
|
||||
* slot in a devblock can corrupt its siblings; this is
|
||||
* how a reader tells a genuine record from corruption. */
|
||||
} doe_trial_slot_t;
|
||||
|
||||
typedef char doe_trial_slot_size_check[(sizeof(doe_trial_slot_t) == DOE_SLOT_SIZE) ? 1 : -1];
|
||||
@@ -165,6 +178,21 @@ int doe_region_append(uint32_t run_id, uint32_t cfg, uint32_t rep, uint32_t nw,
|
||||
uint64_t switch_count_cumulative,
|
||||
const char *isa, uint32_t isa_len);
|
||||
|
||||
/*
|
||||
* doe_region_count_for_isa - Count CRC-valid, live records in the ring
|
||||
* whose isa tag matches the given string (§XXXV.12, campaign crash-resume).
|
||||
* A record that fails its slot_crc check does not count -- the caller
|
||||
* (DOE-RESUME-COUNT) treats a corrupted slot as "not actually done," so
|
||||
* that trial safely re-runs rather than being silently skipped. Returns 0
|
||||
* if the ring has never been initialized (matching doe_region_append()'s
|
||||
* own "blank == not yet initialized" discipline), never negative.
|
||||
*
|
||||
* @param isa ISA tag to match, e.g. "amd64".
|
||||
* @param isa_len Length of isa (truncated to DOE_SLOT_ISA_MAX-1).
|
||||
* @return Count of matching, CRC-valid records (0 or more).
|
||||
*/
|
||||
uint32_t doe_region_count_for_isa(const char *isa, uint32_t isa_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user