diff --git a/bd/lfs_emubd.c b/bd/lfs_emubd.c index 458ac17a..a7517553 100644 --- a/bd/lfs_emubd.c +++ b/bd/lfs_emubd.c @@ -77,6 +77,8 @@ static lfs_emubd_block_t *lfs_emubd_mutblock( block_->rc = 1; block_->wear = 0; + block_->metastable = false; + block_->bad_bit = 0; // zero for consistency lfs_emubd_t *bd = cfg->context; @@ -312,14 +314,29 @@ int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block, const lfs_emubd_block_t *b = bd->blocks[block]; if (b) { // block bad? - if (bd->cfg->erase_cycles && b->wear >= bd->cfg->erase_cycles && - bd->cfg->badblock_behavior == LFS_EMUBD_BADBLOCK_READERROR) { - LFS_EMUBD_TRACE("lfs_emubd_read -> %d", LFS_ERR_CORRUPT); - return LFS_ERR_CORRUPT; + if (b->wear > bd->cfg->erase_cycles) { + // erroring reads? error + if (bd->cfg->badblock_behavior + == LFS_EMUBD_BADBLOCK_READERROR) { + LFS_EMUBD_TRACE("lfs_emubd_read -> %d", LFS_ERR_CORRUPT); + return LFS_ERR_CORRUPT; + } } // read data memcpy(buffer, &b->data[off], size); + + // metastable? randomly decide if our bad bit flips + if (b->metastable) { + lfs_size_t bit = b->bad_bit & 0x7fffffff; + if (bit/8 >= off + && bit/8 < off+size + && (lfs_emubd_prng(&bd->prng) & 1)) { + ((uint8_t*)buffer)[(bit/8) - off] ^= 1 << (bit%8); + } + } + + // no block yet } else { // zero for consistency memset(buffer, @@ -361,8 +378,7 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, // were we erased properly? LFS_ASSERT(bd->blocks[block]); if (bd->cfg->erase_value != -1 - && !(bd->cfg->erase_cycles - && bd->blocks[block]->wear >= bd->cfg->erase_cycles)) { + && bd->blocks[block]->wear <= bd->cfg->erase_cycles) { for (lfs_off_t i = 0; i < size; i++) { LFS_ASSERT(bd->blocks[block]->data[off+i] == bd->cfg->erase_value); } @@ -372,7 +388,7 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, if (bd->power_cycles > 0) { bd->power_cycles -= 1; if (bd->power_cycles == 0) { - // if emulating some bits, choose a random bit to flip + // emulating some bits? choose a random bit to flip if (bd->cfg->powerloss_behavior == LFS_EMUBD_POWERLOSS_SOMEBITS) { // mutate the block @@ -387,7 +403,7 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, // flip bit lfs_size_t bit = lfs_emubd_prng(&bd->prng) % (cfg->prog_size*8); - b->data[off + (bit/8)] ^= (1 << (bit%8)); + b->data[off + (bit/8)] ^= 1 << (bit%8); // mirror to disk file? if (bd->disk) { @@ -408,7 +424,7 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, } } - // if emulating most bits, prog data and choose a random bit + // emulating most bits? prog data and choose a random bit // to flip } else if (bd->cfg->powerloss_behavior == LFS_EMUBD_POWERLOSS_MOSTBITS) { @@ -427,7 +443,7 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, // flip bit lfs_size_t bit = lfs_emubd_prng(&bd->prng) % (cfg->prog_size*8); - b->data[off + (bit/8)] ^= (1 << (bit%8)); + b->data[off + (bit/8)] ^= 1 << (bit%8); // mirror to disk file? if (bd->disk) { @@ -448,7 +464,7 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, } } - // if emulating out-of-order writes, revert everything unsynced + // emulating out-of-order writes? revert everything unsynced // except for our current block } else if (bd->cfg->powerloss_behavior == LFS_EMUBD_POWERLOSS_OOO) { @@ -484,6 +500,50 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, } } } + + // emulating metastability? prog data, choose a random bad bit, + // and mark as metastable + } else if (bd->cfg->powerloss_behavior + == LFS_EMUBD_POWERLOSS_METASTABLE) { + // mutate the block + lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, + bd->blocks[block]); + if (!b) { + LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", LFS_ERR_NOMEM); + return LFS_ERR_NOMEM; + } + bd->blocks[block] = b; + + // prog data + memcpy(&b->data[off], buffer, size); + + // choose a new bad bit unless overridden + if (!(0x80000000 & b->bad_bit)) { + b->bad_bit = lfs_emubd_prng(&bd->prng) + % (cfg->block_size*8); + } + + // mark as metastable + b->metastable = true; + + // mirror to disk file? + if (bd->disk) { + off_t res1 = lseek(bd->disk->fd, + (off_t)block*cfg->block_size + (off_t)off, + SEEK_SET); + if (res1 < 0) { + int err = -errno; + LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err); + return err; + } + + ssize_t res2 = write(bd->disk->fd, &b->data[off], size); + if (res2 < 0) { + int err = -errno; + LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err); + return err; + } + } } // powerloss! @@ -533,23 +593,47 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, bd->blocks[block] = b; // block bad? - if (bd->cfg->erase_cycles && b->wear >= bd->cfg->erase_cycles) { - if (bd->cfg->badblock_behavior == - LFS_EMUBD_BADBLOCK_PROGERROR) { + if (b->wear > bd->cfg->erase_cycles) { + // erroring progs? error + if (bd->cfg->badblock_behavior + == LFS_EMUBD_BADBLOCK_PROGERROR) { LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", LFS_ERR_CORRUPT); return LFS_ERR_CORRUPT; - } else if (bd->cfg->badblock_behavior == - LFS_EMUBD_BADBLOCK_PROGNOOP || - bd->cfg->badblock_behavior == - LFS_EMUBD_BADBLOCK_ERASENOOP) { - LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", 0); - return 0; + + // noop progs? skip + } else if (bd->cfg->badblock_behavior + == LFS_EMUBD_BADBLOCK_PROGNOOP + || bd->cfg->badblock_behavior + == LFS_EMUBD_BADBLOCK_ERASENOOP) { + goto progged; + + // progs flipping bits? flip our bad bit, exactly which bit + // is chosen during erase + } else if (bd->cfg->badblock_behavior + == LFS_EMUBD_BADBLOCK_PROGFLIP) { + lfs_size_t bit = b->bad_bit & 0x7fffffff; + if (bit/8 >= off && bit/8 < off+size) { + memcpy(&b->data[off], buffer, size); + b->data[bit/8] ^= 1 << (bit%8); + goto progged; + } + + // reads flipping bits? prog as normal but mark as metastable + } else if (bd->cfg->badblock_behavior + == LFS_EMUBD_BADBLOCK_READFLIP) { + memcpy(&b->data[off], buffer, size); + b->metastable = true; + goto progged; } } // prog data memcpy(&b->data[off], buffer, size); + // clear any metastability + b->metastable = false; + +progged:; // mirror to disk file? if (bd->disk) { off_t res1 = lseek(bd->disk->fd, @@ -599,7 +683,7 @@ int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) { if (bd->power_cycles > 0) { bd->power_cycles -= 1; if (bd->power_cycles == 0) { - // if emulating some bits, choose a random bit to flip + // emulating some bits? choose a random bit to flip if (bd->cfg->powerloss_behavior == LFS_EMUBD_POWERLOSS_SOMEBITS) { // mutate the block @@ -614,7 +698,7 @@ int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) { // flip bit lfs_size_t bit = lfs_emubd_prng(&bd->prng) % (cfg->block_size*8); - b->data[(bit/8)] ^= (1 << (bit%8)); + b->data[(bit/8)] ^= 1 << (bit%8); // mirror to disk file? if (bd->disk) { @@ -636,7 +720,7 @@ int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) { } } - // if emulating most bits, erase data and choose a random bit + // emulating most bits? erase data and choose a random bit // to flip } else if (bd->cfg->powerloss_behavior == LFS_EMUBD_POWERLOSS_MOSTBITS) { @@ -657,7 +741,7 @@ int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) { // flip bit lfs_size_t bit = lfs_emubd_prng(&bd->prng) % (cfg->block_size*8); - b->data[(bit/8)] ^= (1 << (bit%8)); + b->data[(bit/8)] ^= 1 << (bit%8); // mirror to disk file? if (bd->disk) { @@ -679,7 +763,7 @@ int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) { } } - // if emulating out-of-order writes, revert everything unsynced + // emulating out-of-order writes? revert everything unsynced // except for our current block } else if (bd->cfg->powerloss_behavior == LFS_EMUBD_POWERLOSS_OOO) { @@ -712,6 +796,53 @@ int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) { } } } + + // emulating metastability? erase data, choose a random bad bit, + // and mark as metastable + } else if (bd->cfg->powerloss_behavior + == LFS_EMUBD_POWERLOSS_METASTABLE) { + // mutate the block + lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, + bd->blocks[block]); + if (!b) { + LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", LFS_ERR_NOMEM); + return LFS_ERR_NOMEM; + } + bd->blocks[block] = b; + + // emulate an erase value? + if (bd->cfg->erase_value != -1) { + memset(b->data, bd->cfg->erase_value, cfg->block_size); + } + + // choose a new bad bit unless overridden + if (!(0x80000000 & b->bad_bit)) { + b->bad_bit = lfs_emubd_prng(&bd->prng) + % (cfg->block_size*8); + } + + // mark as metastable + b->metastable = true; + + // mirror to disk file? + if (bd->disk) { + off_t res1 = lseek(bd->disk->fd, + (off_t)block*cfg->block_size, + SEEK_SET); + if (res1 < 0) { + int err = -errno; + LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err); + return err; + } + + ssize_t res2 = write(bd->disk->fd, + b->data, cfg->block_size); + if (res2 < 0) { + int err = -errno; + LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err); + return err; + } + } } // powerloss! @@ -760,21 +891,35 @@ int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) { } bd->blocks[block] = b; + // keep track of wear + if (bd->cfg->erase_cycles && b->wear <= bd->cfg->erase_cycles) { + b->wear += 1; + } + // block bad? - if (bd->cfg->erase_cycles) { - if (b && b->wear >= bd->cfg->erase_cycles) { - if (bd->cfg->badblock_behavior == - LFS_EMUBD_BADBLOCK_ERASEERROR) { - LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", LFS_ERR_CORRUPT); - return LFS_ERR_CORRUPT; - } else if (bd->cfg->badblock_behavior == - LFS_EMUBD_BADBLOCK_ERASENOOP) { - LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", 0); - return 0; + if (b->wear > bd->cfg->erase_cycles) { + // erroring erases? error + if (bd->cfg->badblock_behavior + == LFS_EMUBD_BADBLOCK_ERASEERROR) { + LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", LFS_ERR_CORRUPT); + return LFS_ERR_CORRUPT; + + // noop erases? skip + } else if (bd->cfg->badblock_behavior + == LFS_EMUBD_BADBLOCK_ERASENOOP) { + goto erased; + + // flipping bits? if we're not manually overridden, choose a + // new bad bit on erase, this makes it more likely to + // eventually cause problems + } else if (bd->cfg->badblock_behavior + == LFS_EMUBD_BADBLOCK_PROGFLIP + || bd->cfg->badblock_behavior + == LFS_EMUBD_BADBLOCK_READFLIP) { + if (!(0x80000000 & b->bad_bit)) { + b->bad_bit = lfs_emubd_prng(&bd->prng) + % (cfg->block_size*8); } - } else { - // mark wear - b->wear += 1; } } @@ -802,6 +947,10 @@ int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) { } } + // clear any metastability + b->metastable = false; + +erased:; // track erases bd->erased += cfg->block_size; if (bd->cfg->erase_sleep) { @@ -839,6 +988,17 @@ int lfs_emubd_sync(const struct lfs_config *cfg) { /// Additional extended API for driving test features /// +int lfs_emubd_seed(const struct lfs_config *cfg, uint32_t seed) { + LFS_EMUBD_TRACE("lfs_emubd_seed(%p, 0x%08"PRIx32")", + (void*)cfg, seed); + lfs_emubd_t *bd = cfg->context; + + bd->prng = seed; + + LFS_EMUBD_TRACE("lfs_emubd_seed -> %d", 0); + return 0; +} + static int lfs_emubd_rawcksum(const struct lfs_config *cfg, lfs_block_t block, uint32_t *cksum) { lfs_emubd_t *bd = cfg->context; @@ -983,6 +1143,149 @@ int lfs_emubd_setwear(const struct lfs_config *cfg, return 0; } +int lfs_emubd_markbad(const struct lfs_config *cfg, + lfs_block_t block) { + LFS_EMUBD_TRACE("lfs_emubd_markbad(%p, %"PRIu32")", + (void*)cfg, block); + lfs_emubd_t *bd = cfg->context; + + // check if block is valid + LFS_ASSERT(block < cfg->block_count); + + // mutate the block + lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, bd->blocks[block]); + if (!b) { + LFS_EMUBD_TRACE("lfs_emubd_markbad -> %d", LFS_ERR_NOMEM); + return LFS_ERR_NOMEM; + } + bd->blocks[block] = b; + + // set the wear + b->wear = -1; + + LFS_EMUBD_TRACE("lfs_emubd_markbad -> %d", 0); + return 0; +} + +int lfs_emubd_markgood(const struct lfs_config *cfg, + lfs_block_t block) { + LFS_EMUBD_TRACE("lfs_emubd_markgood(%p, %"PRIu32")", + (void*)cfg, block); + lfs_emubd_t *bd = cfg->context; + + // check if block is valid + LFS_ASSERT(block < cfg->block_count); + + // mutate the block + lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, bd->blocks[block]); + if (!b) { + LFS_EMUBD_TRACE("lfs_emubd_markgood -> %d", LFS_ERR_NOMEM); + return LFS_ERR_NOMEM; + } + bd->blocks[block] = b; + + // set the wear + b->wear = 0; + + LFS_EMUBD_TRACE("lfs_emubd_markgood -> %d", 0); + return 0; +} + +lfs_ssize_t lfs_emubd_badbit(const struct lfs_config *cfg, + lfs_block_t block) { + LFS_EMUBD_TRACE("lfs_emubd_badbit(%p, %"PRIu32")", (void*)cfg, block); + lfs_emubd_t *bd = cfg->context; + + // check if block is valid + LFS_ASSERT(block < cfg->block_count); + + // get the bad bit + lfs_size_t bad_bit; + const lfs_emubd_block_t *b = bd->blocks[block]; + if (b) { + bad_bit = 0x7fffffff & b->bad_bit; + } else { + bad_bit = 0; + } + + LFS_EMUBD_TRACE("lfs_emubd_badbit -> %"PRIi32, bad_bit); + return bad_bit; +} + +int lfs_emubd_setbadbit(const struct lfs_config *cfg, + lfs_block_t block, lfs_size_t bit) { + LFS_EMUBD_TRACE("lfs_emubd_setbadbit(%p, %"PRIu32", %"PRIu32")", + (void*)cfg, block, bit); + lfs_emubd_t *bd = cfg->context; + + // check if block is valid + LFS_ASSERT(block < cfg->block_count); + + // mutate the block + lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, bd->blocks[block]); + if (!b) { + LFS_EMUBD_TRACE("lfs_emubd_setbadbit -> %d", LFS_ERR_NOMEM); + return LFS_ERR_NOMEM; + } + bd->blocks[block] = b; + + // set the bad bit and mark as fixed + b->bad_bit = 0x80000000 | bit; + + LFS_EMUBD_TRACE("lfs_emubd_setbadbit -> %d", 0); + return 0; +} + +int lfs_emubd_randomizebadbit(const struct lfs_config *cfg, + lfs_block_t block) { + LFS_EMUBD_TRACE("lfs_emubd_randomizebadbit(%p, %"PRIu32")", + (void*)cfg, block); + lfs_emubd_t *bd = cfg->context; + + // check if block is valid + LFS_ASSERT(block < cfg->block_count); + + // mutate the block + lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, bd->blocks[block]); + if (!b) { + LFS_EMUBD_TRACE("lfs_emubd_randomizebadbit -> %d", LFS_ERR_NOMEM); + return LFS_ERR_NOMEM; + } + bd->blocks[block] = b; + + // mark the bad bit as randomized + b->bad_bit &= ~0x80000000; + + LFS_EMUBD_TRACE("lfs_emubd_randomizebadbit -> %d", 0); + return 0; +} + +int lfs_emubd_markbadbit(const struct lfs_config *cfg, + lfs_block_t block, lfs_size_t bit) { + LFS_EMUBD_TRACE("lfs_emubd_markbadbit(%p, %"PRIu32", %"PRIu32")", + (void*)cfg, block, bit); + lfs_emubd_t *bd = cfg->context; + + // check if block is valid + LFS_ASSERT(block < cfg->block_count); + + // mutate the block + lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, bd->blocks[block]); + if (!b) { + LFS_EMUBD_TRACE("lfs_emubd_markbadbit -> %d", LFS_ERR_NOMEM); + return LFS_ERR_NOMEM; + } + bd->blocks[block] = b; + + // set the wear + b->wear = -1; + // set the bad bit and mark as fixed + b->bad_bit = 0x80000000 | bit; + + LFS_EMUBD_TRACE("lfs_emubd_markbadbit -> %d", 0); + return 0; +} + lfs_emubd_spowercycles_t lfs_emubd_powercycles( const struct lfs_config *cfg) { LFS_EMUBD_TRACE("lfs_emubd_powercycles(%p)", (void*)cfg); diff --git a/bd/lfs_emubd.h b/bd/lfs_emubd.h index dfffe08a..aadb843a 100644 --- a/bd/lfs_emubd.h +++ b/bd/lfs_emubd.h @@ -31,25 +31,25 @@ extern "C" // Mode determining how "bad-blocks" behave during testing. This simulates // some real-world circumstances such as progs not sticking (prog-noop), -// a readonly disk (erase-noop), and ECC failures (read-error). -// -// Not that read-noop is not allowed. Read _must_ return a consistent (but -// may be arbitrary) value on every read. +// a readonly disk (erase-noop), ECC failures (read-error), and of course, +// random bit failures (prog-flip, read-flip) typedef enum lfs_emubd_badblock_behavior { - LFS_EMUBD_BADBLOCK_PROGERROR = 0, // Error on prog - LFS_EMUBD_BADBLOCK_ERASEERROR = 1, // Error on erase - LFS_EMUBD_BADBLOCK_READERROR = 2, // Error on read - LFS_EMUBD_BADBLOCK_PROGNOOP = 3, // Prog does nothing silently - LFS_EMUBD_BADBLOCK_ERASENOOP = 4, // Erase does nothing silently + LFS_EMUBD_BADBLOCK_PROGERROR = 0, // Error on prog + LFS_EMUBD_BADBLOCK_ERASEERROR = 1, // Error on erase + LFS_EMUBD_BADBLOCK_READERROR = 2, // Error on read + LFS_EMUBD_BADBLOCK_PROGNOOP = 3, // Prog does nothing silently + LFS_EMUBD_BADBLOCK_ERASENOOP = 4, // Erase does nothing silently + LFS_EMUBD_BADBLOCK_PROGFLIP = 5, // Prog flips a bit + LFS_EMUBD_BADBLOCK_READFLIP = 6, // Read flips a bit sometimes } lfs_emubd_badblock_behavior_t; -// Mode determining how power-loss behaves during testing. For now this -// only supports a noop behavior, leaving the data on-disk untouched. +// Mode determining how power-loss behaves during testing. typedef enum lfs_emubd_powerloss_behavior { - LFS_EMUBD_POWERLOSS_NOOP = 0, // Progs are atomic - LFS_EMUBD_POWERLOSS_SOMEBITS = 1, // One bit is progged - LFS_EMUBD_POWERLOSS_MOSTBITS = 2, // All-but-one bit is progged - LFS_EMUBD_POWERLOSS_OOO = 3, // Blocks are written out-of-order + LFS_EMUBD_POWERLOSS_NOOP = 0, // Progs are atomic + LFS_EMUBD_POWERLOSS_SOMEBITS = 1, // One bit is progged + LFS_EMUBD_POWERLOSS_MOSTBITS = 2, // All-but-one bit is progged + LFS_EMUBD_POWERLOSS_OOO = 3, // Blocks are written out-of-order + LFS_EMUBD_POWERLOSS_METASTABLE = 4, // Reads may flip a bit } lfs_emubd_powerloss_behavior_t; // Type for measuring read/program/erase operations @@ -121,6 +121,10 @@ struct lfs_emubd_config { typedef struct lfs_emubd_block { uint32_t rc; lfs_emubd_wear_t wear; + bool metastable; + // sign(bad_bit)=0 => randomized on erase + // sign(bad_bit)=1 => fixed + lfs_size_t bad_bit; uint8_t data[]; } lfs_emubd_block_t; @@ -186,6 +190,9 @@ int lfs_emubd_sync(const struct lfs_config *cfg); /// Additional extended API for driving test features /// +// Set the current prng state +int lfs_emubd_seed(const struct lfs_config *cfg, uint32_t seed); + // A checksum of a block for debugging purposes int lfs_emubd_cksum(const struct lfs_config *cfg, lfs_block_t block, uint32_t *cksum); @@ -219,6 +226,28 @@ lfs_emubd_swear_t lfs_emubd_wear(const struct lfs_config *cfg, int lfs_emubd_setwear(const struct lfs_config *cfg, lfs_block_t block, lfs_emubd_wear_t wear); +// Mark a block as bad, this is equivalent to setting wear to maximum +int lfs_emubd_markbad(const struct lfs_config *cfg, lfs_block_t block); + +// Clear any simulated wear on a given block +int lfs_emubd_markgood(const struct lfs_config *cfg, lfs_block_t block); + +// Get which bit failed, this changes on erase/power-loss unless manually set +lfs_ssize_t lfs_emubd_badbit(const struct lfs_config *cfg, + lfs_block_t block); + +// Set which bit should fail in a given block +int lfs_emubd_setbadbit(const struct lfs_config *cfg, + lfs_block_t block, lfs_size_t bit); + +// Randomize the bad bit on erase (the default) +int lfs_emubd_randomizebadbit(const struct lfs_config *cfg, + lfs_block_t block); + +// Mark a block as bad and which bit should fail +int lfs_emubd_markbadbit(const struct lfs_config *cfg, + lfs_block_t block, lfs_size_t bit); + // Get the remaining power-cycles lfs_emubd_spowercycles_t lfs_emubd_powercycles( const struct lfs_config *cfg); diff --git a/lfs.c b/lfs.c index 88613b8d..4aff7e50 100644 --- a/lfs.c +++ b/lfs.c @@ -12968,7 +12968,7 @@ static int lfsr_mountinited(lfs_t *lfs) { if (mdir->mid == -1) { // check for the magic string, all mroot should have this lfsr_data_t data; - int err = lfsr_mdir_lookup(lfs, mdir, LFSR_TAG_MAGIC, + err = lfsr_mdir_lookup(lfs, mdir, LFSR_TAG_MAGIC, &data); if (err) { if (err == LFS_ERR_NOENT) { @@ -13274,7 +13274,8 @@ static int lfsr_formatinited(lfs_t *lfs) { } int lfsr_format(lfs_t *lfs, const struct lfs_config *cfg) { - int err = lfs_init(lfs, LFS_M_RDWR, cfg); + // TODO hmmm, should lfsr_format take flags? + int err = lfs_init(lfs, LFS_M_RDWR | LFS_M_CKPROGS, cfg); if (err) { return err; } diff --git a/tests/test_badblocks.toml b/tests/test_badblocks.toml index 9aca08ef..bdca735a 100644 --- a/tests/test_badblocks.toml +++ b/tests/test_badblocks.toml @@ -19,7 +19,6 @@ after = [ # B-tree's ridiculous branching factor is great for performance, but it makes # them a bit of a pain to test, here we test them explicitly [cases.test_badblocks_single_btree_many] -defines.ERASE_CYCLES = 0xffffffff defines.BADBLOCK = -1 defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -27,6 +26,7 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', + 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -43,7 +43,7 @@ code = ''' i++) { lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK; // mark our badblock as bad - lfs_emubd_setwear(CFG, badblock, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, badblock) => 0; printf("--- badblock: 0x%x ---\n", badblock); // test creating a btree @@ -124,13 +124,12 @@ code = ''' lfs_deinit(&lfs) => 0; // reset badblock - lfs_emubd_setwear(CFG, badblock, 0) => 0; + lfs_emubd_markgood(CFG, badblock) => 0; } ''' # with dirs [cases.test_badblocks_single_dir_many] -defines.ERASE_CYCLES = 0xffffffff defines.BADBLOCK = -1 defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -138,6 +137,7 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', + 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -149,7 +149,7 @@ code = ''' i++) { lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK; // mark our badblock as bad - lfs_emubd_setwear(CFG, badblock, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, badblock) => 0; printf("--- badblock: 0x%x ---\n", badblock); // test creating directories @@ -234,13 +234,12 @@ code = ''' lfsr_unmount(&lfs) => 0; // reset badblock - lfs_emubd_setwear(CFG, badblock, 0) => 0; + lfs_emubd_markgood(CFG, badblock) => 0; } ''' # fuzz dirs [cases.test_badblocks_single_dir_fuzz] -defines.ERASE_CYCLES = 0xffffffff defines.BADBLOCK = -1 defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -248,6 +247,7 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', + 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -262,7 +262,7 @@ code = ''' i++) { lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK; // mark our badblock as bad - lfs_emubd_setwear(CFG, badblock, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, badblock) => 0; printf("--- badblock: 0x%x ---\n", badblock); // test fuzz with dirs @@ -415,13 +415,12 @@ code = ''' lfsr_unmount(&lfs) => 0; // reset badblock - lfs_emubd_setwear(CFG, badblock, 0) => 0; + lfs_emubd_markgood(CFG, badblock) => 0; } ''' # with files [cases.test_badblocks_single_file_many] -defines.ERASE_CYCLES = 0xffffffff defines.BADBLOCK = -1 defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -429,6 +428,7 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', + 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -450,7 +450,7 @@ code = ''' i++) { lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK; // mark our badblock as bad - lfs_emubd_setwear(CFG, badblock, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, badblock) => 0; printf("--- badblock: 0x%x ---\n", badblock); // test creating files @@ -519,13 +519,12 @@ code = ''' lfsr_unmount(&lfs) => 0; // reset badblock - lfs_emubd_setwear(CFG, badblock, 0) => 0; + lfs_emubd_markgood(CFG, badblock) => 0; } ''' # fuzz files [cases.test_badblocks_single_file_fuzz] -defines.ERASE_CYCLES = 0xffffffff defines.BADBLOCK = -1 defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -533,6 +532,7 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', + 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -557,7 +557,7 @@ code = ''' i++) { lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK; // mark our badblock as bad - lfs_emubd_setwear(CFG, badblock, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, badblock) => 0; printf("--- badblock: 0x%x ---\n", badblock); // test fuzz with files @@ -762,13 +762,12 @@ code = ''' lfsr_unmount(&lfs) => 0; // reset badblock - lfs_emubd_setwear(CFG, badblock, 0) => 0; + lfs_emubd_markgood(CFG, badblock) => 0; } ''' # with more complex file writes [cases.test_badblocks_single_fwrite_fuzz] -defines.ERASE_CYCLES = 0xffffffff defines.BADBLOCK = -1 defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -776,6 +775,7 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', + 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -809,7 +809,7 @@ code = ''' i++) { lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK; // mark our badblock as bad - lfs_emubd_setwear(CFG, badblock, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, badblock) => 0; printf("--- badblock: 0x%x ---\n", badblock); // test with complex file writes @@ -927,13 +927,12 @@ code = ''' lfsr_unmount(&lfs) => 0; // reset badblock - lfs_emubd_setwear(CFG, badblock, 0) => 0; + lfs_emubd_markgood(CFG, badblock) => 0; } ''' # with orphans, zombies, etc [cases.test_badblocks_single_orphanzombie_fuzz] -defines.ERASE_CYCLES = 0xffffffff defines.BADBLOCK = -1 defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -941,6 +940,7 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', + 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -965,7 +965,7 @@ code = ''' i++) { lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK; // mark our badblock as bad - lfs_emubd_setwear(CFG, badblock, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, badblock) => 0; printf("--- badblock: 0x%x ---\n", badblock); // test with orphans, zombies, etc @@ -1293,13 +1293,12 @@ code = ''' free(sim_files); lfsr_unmount(&lfs) => 0; // reset badblock - lfs_emubd_setwear(CFG, badblock, 0) => 0; + lfs_emubd_markgood(CFG, badblock) => 0; } ''' # with orphans, zombies, dirs, etc [cases.test_badblocks_single_orphanzombiedir_fuzz] -defines.ERASE_CYCLES = 0xffffffff defines.BADBLOCK = -1 defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -1307,6 +1306,7 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', + 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -1331,7 +1331,7 @@ code = ''' i++) { lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK; // mark our badblock as bad - lfs_emubd_setwear(CFG, badblock, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, badblock) => 0; printf("--- badblock: 0x%x ---\n", badblock); // test with orphans, zombies, dirs, etc @@ -1739,7 +1739,7 @@ code = ''' lfsr_unmount(&lfs) => 0; // reset badblock - lfs_emubd_setwear(CFG, badblock, 0) => 0; + lfs_emubd_markgood(CFG, badblock) => 0; } ''' @@ -1753,13 +1753,13 @@ code = ''' # B-tree's ridiculous branching factor is great for performance, but it makes # them a bit of a pain to test, here we test them explicitly [cases.test_badblocks_region_btree_many] -defines.ERASE_CYCLES = 0xffffffff defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', 'LFS_EMUBD_BADBLOCK_ERASEERROR', 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', + 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -1775,9 +1775,9 @@ code = ''' for (lfs_size_t i = 0; i < BLOCK_COUNT/2; i++) { // mark our badblock as bad if (!MIRROR) { - lfs_emubd_setwear(CFG, i, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, i) => 0; } else { - lfs_emubd_setwear(CFG, i + BLOCK_COUNT/2, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, i + BLOCK_COUNT/2) => 0; } } @@ -1861,13 +1861,13 @@ code = ''' # with dirs [cases.test_badblocks_region_dir_many] -defines.ERASE_CYCLES = 0xffffffff defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', 'LFS_EMUBD_BADBLOCK_ERASEERROR', 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', + 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -1879,11 +1879,11 @@ code = ''' // mark our badblock as bad if (!MIRROR) { if (i >= 2) { - lfs_emubd_setwear(CFG, i, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, i) => 0; } } else { if (i+BLOCK_COUNT/2 >= 2) { - lfs_emubd_setwear(CFG, i+BLOCK_COUNT/2, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, i+BLOCK_COUNT/2) => 0; } } } @@ -1972,13 +1972,13 @@ code = ''' # fuzz dirs [cases.test_badblocks_region_dir_fuzz] -defines.ERASE_CYCLES = 0xffffffff defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', 'LFS_EMUBD_BADBLOCK_ERASEERROR', 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', + 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -1993,11 +1993,11 @@ code = ''' // mark our badblock as bad if (!MIRROR) { if (i >= 2) { - lfs_emubd_setwear(CFG, i, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, i) => 0; } } else { if (i+BLOCK_COUNT/2 >= 2) { - lfs_emubd_setwear(CFG, i+BLOCK_COUNT/2, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, i+BLOCK_COUNT/2) => 0; } } } @@ -2154,13 +2154,13 @@ code = ''' # with files [cases.test_badblocks_region_file_many] -defines.ERASE_CYCLES = 0xffffffff defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', 'LFS_EMUBD_BADBLOCK_ERASEERROR', 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', + 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -2182,11 +2182,11 @@ code = ''' // mark our badblock as bad if (!MIRROR) { if (i >= 2) { - lfs_emubd_setwear(CFG, i, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, i) => 0; } } else { if (i+BLOCK_COUNT/2 >= 2) { - lfs_emubd_setwear(CFG, i+BLOCK_COUNT/2, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, i+BLOCK_COUNT/2) => 0; } } } @@ -2259,13 +2259,13 @@ code = ''' # fuzz files [cases.test_badblocks_region_file_fuzz] -defines.ERASE_CYCLES = 0xffffffff defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', 'LFS_EMUBD_BADBLOCK_ERASEERROR', 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', + 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -2290,11 +2290,11 @@ code = ''' // mark our badblock as bad if (!MIRROR) { if (i >= 2) { - lfs_emubd_setwear(CFG, i, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, i) => 0; } } else { if (i+BLOCK_COUNT/2 >= 2) { - lfs_emubd_setwear(CFG, i+BLOCK_COUNT/2, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, i+BLOCK_COUNT/2) => 0; } } } @@ -2503,13 +2503,13 @@ code = ''' # with more complex file writes [cases.test_badblocks_region_fwrite_fuzz] -defines.ERASE_CYCLES = 0xffffffff defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', 'LFS_EMUBD_BADBLOCK_ERASEERROR', 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', + 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -2543,11 +2543,11 @@ code = ''' // mark our badblock as bad if (!MIRROR) { if (i >= 2) { - lfs_emubd_setwear(CFG, i, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, i) => 0; } } else { if (i+BLOCK_COUNT/2 >= 2) { - lfs_emubd_setwear(CFG, i+BLOCK_COUNT/2, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, i+BLOCK_COUNT/2) => 0; } } } @@ -2669,13 +2669,13 @@ code = ''' # with orphans, zombies, etc [cases.test_badblocks_region_orphanzombie_fuzz] -defines.ERASE_CYCLES = 0xffffffff defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', 'LFS_EMUBD_BADBLOCK_ERASEERROR', 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', + 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -2700,11 +2700,11 @@ code = ''' // mark our badblock as bad if (!MIRROR) { if (i >= 2) { - lfs_emubd_setwear(CFG, i, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, i) => 0; } } else { if (i+BLOCK_COUNT/2 >= 2) { - lfs_emubd_setwear(CFG, i+BLOCK_COUNT/2, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, i+BLOCK_COUNT/2) => 0; } } } @@ -3036,13 +3036,13 @@ code = ''' # with orphans, zombies, dirs, etc [cases.test_badblocks_region_orphanzombiedir_fuzz] -defines.ERASE_CYCLES = 0xffffffff defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', 'LFS_EMUBD_BADBLOCK_ERASEERROR', 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', + 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -3067,11 +3067,11 @@ code = ''' // mark our badblock as bad if (!MIRROR) { if (i >= 2) { - lfs_emubd_setwear(CFG, i, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, i) => 0; } } else { if (i+BLOCK_COUNT/2 >= 2) { - lfs_emubd_setwear(CFG, i+BLOCK_COUNT/2, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, i+BLOCK_COUNT/2) => 0; } } } @@ -3488,13 +3488,13 @@ code = ''' # B-tree's ridiculous branching factor is great for performance, but it makes # them a bit of a pain to test, here we test them explicitly [cases.test_badblocks_alternating_btree_many] -defines.ERASE_CYCLES = 0xffffffff defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', 'LFS_EMUBD_BADBLOCK_ERASEERROR', 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', + 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -3510,9 +3510,9 @@ code = ''' for (lfs_size_t i = 0; i < BLOCK_COUNT/2; i++) { // mark our badblock as bad if (!MIRROR) { - lfs_emubd_setwear(CFG, 2*i+0, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, 2*i+0) => 0; } else { - lfs_emubd_setwear(CFG, 2*i+1, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, 2*i+1) => 0; } } @@ -3596,13 +3596,13 @@ code = ''' # with dirs [cases.test_badblocks_alternating_dir_many] -defines.ERASE_CYCLES = 0xffffffff defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', 'LFS_EMUBD_BADBLOCK_ERASEERROR', 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', + 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -3614,11 +3614,11 @@ code = ''' // mark our badblock as bad if (!MIRROR) { if (2*i+0 >= 2) { - lfs_emubd_setwear(CFG, 2*i+0, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, 2*i+0) => 0; } } else { if (2*i+1 >= 2) { - lfs_emubd_setwear(CFG, 2*i+1, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, 2*i+1) => 0; } } } @@ -3707,13 +3707,13 @@ code = ''' # fuzz dirs [cases.test_badblocks_alternating_dir_fuzz] -defines.ERASE_CYCLES = 0xffffffff defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', 'LFS_EMUBD_BADBLOCK_ERASEERROR', 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', + 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -3728,11 +3728,11 @@ code = ''' // mark our badblock as bad if (!MIRROR) { if (2*i+0 >= 2) { - lfs_emubd_setwear(CFG, 2*i+0, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, 2*i+0) => 0; } } else { if (2*i+1 >= 2) { - lfs_emubd_setwear(CFG, 2*i+1, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, 2*i+1) => 0; } } } @@ -3889,13 +3889,13 @@ code = ''' # with files [cases.test_badblocks_alternating_file_many] -defines.ERASE_CYCLES = 0xffffffff defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', 'LFS_EMUBD_BADBLOCK_ERASEERROR', 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', + 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -3917,11 +3917,11 @@ code = ''' // mark our badblock as bad if (!MIRROR) { if (2*i+0 >= 2) { - lfs_emubd_setwear(CFG, 2*i+0, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, 2*i+0) => 0; } } else { if (2*i+1 >= 2) { - lfs_emubd_setwear(CFG, 2*i+1, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, 2*i+1) => 0; } } } @@ -3994,13 +3994,13 @@ code = ''' # fuzz files [cases.test_badblocks_alternating_file_fuzz] -defines.ERASE_CYCLES = 0xffffffff defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', 'LFS_EMUBD_BADBLOCK_ERASEERROR', 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', + 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -4025,11 +4025,11 @@ code = ''' // mark our badblock as bad if (!MIRROR) { if (2*i+0 >= 2) { - lfs_emubd_setwear(CFG, 2*i+0, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, 2*i+0) => 0; } } else { if (2*i+1 >= 2) { - lfs_emubd_setwear(CFG, 2*i+1, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, 2*i+1) => 0; } } } @@ -4238,13 +4238,13 @@ code = ''' # with more complex file writes [cases.test_badblocks_alternating_fwrite_fuzz] -defines.ERASE_CYCLES = 0xffffffff defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', 'LFS_EMUBD_BADBLOCK_ERASEERROR', 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', + 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -4278,11 +4278,11 @@ code = ''' // mark our badblock as bad if (!MIRROR) { if (2*i+0 >= 2) { - lfs_emubd_setwear(CFG, 2*i+0, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, 2*i+0) => 0; } } else { if (2*i+1 >= 2) { - lfs_emubd_setwear(CFG, 2*i+1, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, 2*i+1) => 0; } } } @@ -4402,13 +4402,13 @@ code = ''' # with orphans, zombies, etc [cases.test_badblocks_alternating_orphanzombie_fuzz] -defines.ERASE_CYCLES = 0xffffffff defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', 'LFS_EMUBD_BADBLOCK_ERASEERROR', 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', + 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -4433,11 +4433,11 @@ code = ''' // mark our badblock as bad if (!MIRROR) { if (2*i+0 >= 2) { - lfs_emubd_setwear(CFG, 2*i+0, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, 2*i+0) => 0; } } else { if (2*i+1 >= 2) { - lfs_emubd_setwear(CFG, 2*i+1, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, 2*i+1) => 0; } } } @@ -4769,13 +4769,13 @@ code = ''' # with orphans, zombies, dirs, etc [cases.test_badblocks_alternating_orphanzombiedir_fuzz] -defines.ERASE_CYCLES = 0xffffffff defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', 'LFS_EMUBD_BADBLOCK_ERASEERROR', 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', + 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -4800,11 +4800,11 @@ code = ''' // mark our badblock as bad if (!MIRROR) { if (2*i+0 >= 2) { - lfs_emubd_setwear(CFG, 2*i+0, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, 2*i+0) => 0; } } else { if (2*i+1 >= 2) { - lfs_emubd_setwear(CFG, 2*i+1, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, 2*i+1) => 0; } } } @@ -5218,7 +5218,6 @@ code = ''' # test formatting with 0 or 1 bad, this should just error [cases.test_badblocks_mrootanchor_format] -defines.ERASE_CYCLES = 0xffffffff defines.BADBLOCKS = [0x1, 0x2, 0x3] defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -5229,10 +5228,10 @@ defines.BADBLOCK_BEHAVIOR = [ ] code = ''' if (BADBLOCKS & 0x1) { - lfs_emubd_setwear(CFG, 0, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, 0) => 0; } if (BADBLOCKS & 0x2) { - lfs_emubd_setwear(CFG, 1, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, 1) => 0; } lfs_t lfs; @@ -5241,7 +5240,6 @@ code = ''' # test blocks 0 or 1 going bad, this should just error [cases.test_badblocks_mrootanchor_wear] -defines.ERASE_CYCLES = 0xffffffff defines.BADBLOCKS = [0x1, 0x2] defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -5257,10 +5255,10 @@ code = ''' lfsr_format(&lfs, CFG) => 0; if (BADBLOCKS & 0x1) { - lfs_emubd_setwear(CFG, 0, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, 0) => 0; } if (BADBLOCKS & 0x2) { - lfs_emubd_setwear(CFG, 1, 0xffffffff) => 0; + lfs_emubd_markbad(CFG, 1) => 0; } lfsr_mount(&lfs, diff --git a/tests/test_ck.toml b/tests/test_ck.toml index 0beaa5d6..f5c44d64 100644 --- a/tests/test_ck.toml +++ b/tests/test_ck.toml @@ -104,7 +104,7 @@ code = ''' for (lfs_block_t i = 0;; i++) { // a bit hacky, but this catches infinite loops LFS_ASSERT(i < 2*BLOCK_COUNT); - + struct lfs_tinfo tinfo; int err = lfsr_traversal_read(&lfs, &t, &tinfo); assert(!err || err == LFS_ERR_CORRUPT); @@ -233,7 +233,7 @@ code = ''' for (lfs_block_t i = 0;; i++) { // a bit hacky, but this catches infinite loops LFS_ASSERT(i < 2*BLOCK_COUNT); - + struct lfs_tinfo tinfo; int err = lfsr_traversal_read(&lfs, &t, &tinfo); assert(!err || err == LFS_ERR_CORRUPT); @@ -462,3 +462,711 @@ code = ''' } done:; ''' + + + +# Some simple ckprog tests +# +# We test these much more aggressively in test_badblocks + +# test every single-bit error in block 0/1 +[cases.test_ck_ckprogs_mroot] +defines.BADBLOCK = [0, 1] +defines.BADBIT = -1 +defines.BADBLOCK_BEHAVIOR = 'LFS_EMUBD_BADBLOCK_PROGFLIP' +# this should stay inlined +defines.SIZE = 'BLOCK_SIZE/16' +code = ''' + // test all bad bits in the mroot + for (lfs_size_t i = 0; + i < ((BADBIT == -1) ? 8*BLOCK_SIZE : 1); + i++) { + lfs_size_t badbit = (BADBIT == -1) ? i : BADBIT; + + // mark our badbit as bad + lfs_emubd_markbadbit(CFG, BADBLOCK, badbit) => 0; + printf("--- badblock: 0x%x.%x, badbit: 0x%x (0x%x+%x) ---\n", + (lfs_size_t)BADBLOCK, badbit/8, badbit, badbit/8, badbit%8); + + // formatting the filesystem may already find the bit error + lfs_t lfs; + int err = lfsr_format(&lfs, CFG); + assert(!err || err == LFS_ERR_CORRUPT); + if (err == LFS_ERR_CORRUPT) { + goto corrupt; + } + lfsr_mount(&lfs, LFS_M_RDWR | LFS_M_CKPROGS, CFG) => 0; + + { + // create a file + lfsr_file_t file; + err = lfsr_file_open(&lfs, &file, "stygiomedusa", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL); + assert(!err || err == LFS_ERR_CORRUPT); + if (err == LFS_ERR_CORRUPT) { + lfsr_file_close(&lfs, &file) => 0; + goto corrupt_mounted; + } + uint32_t prng = 42; + uint8_t wbuf[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfs_ssize_t res = lfsr_file_write(&lfs, &file, wbuf, SIZE); + assert(res == SIZE || res == LFS_ERR_CORRUPT); + if (res == LFS_ERR_CORRUPT) { + goto corrupt_mounted; + } + err = lfsr_file_close(&lfs, &file); + if (err == LFS_ERR_CORRUPT) { + goto corrupt_mounted; + } + + // if we made it here without erroring we should be able to + // read our file + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, LFS_M_RDWR | LFS_M_CKPROGS, CFG) => 0; + } + + lfsr_file_open(&lfs, &file, "stygiomedusa", LFS_O_RDONLY) => 0; + uint8_t rbuf[SIZE]; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + } + } + + corrupt_mounted:; + lfsr_unmount(&lfs) => 0; + + corrupt:; + // reset badbit + lfs_emubd_markgood(CFG, BADBLOCK) => 0; + } +''' + +# test every single-bit error in a file's data block +[cases.test_ck_ckprogs_data] +defines.BADBIT = -1 +defines.BADBLOCK_BEHAVIOR = 'LFS_EMUBD_BADBLOCK_PROGFLIP' +# this should create a single block file +defines.SIZE = 'BLOCK_SIZE' +code = ''' + // first we need to figure out where the data block will actually + // end up, fortunately our block randomization is intentionally + // consistent + + // format + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR | LFS_M_CKPROGS, CFG) => 0; + + // create a file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "stygiomedusa", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + uint32_t prng = 42; + uint8_t wbuf[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // find the data block + lfsr_traversal_t t; + lfsr_traversal_open(&lfs, &t, 0) => 0; + lfs_block_t badblock; + while (true) { + struct lfs_tinfo tinfo; + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + if (tinfo.btype == LFS_BTYPE_DATA) { + badblock = tinfo.block; + break; + } + } + lfsr_traversal_close(&lfs, &t) => 0; + + lfsr_unmount(&lfs) => 0; + + // now test all bad bits in the data block + for (lfs_size_t i = 0; + i < ((BADBIT == -1) ? 8*BLOCK_SIZE : 1); + i++) { + lfs_size_t badbit = (BADBIT == -1) ? i : BADBIT; + + // mark our badbit as bad + lfs_emubd_markbadbit(CFG, badblock, badbit) => 0; + printf("--- badblock: 0x%x.%x, badbit: 0x%x (0x%x+%x) ---\n", + badblock, badbit/8, badbit, badbit/8, badbit%8); + + // format + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR | LFS_M_CKPROGS, CFG) => 0; + + { + // create a file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "stygiomedusa", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + uint32_t prng = 42; + uint8_t wbuf[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfs_ssize_t res = lfsr_file_write(&lfs, &file, wbuf, SIZE); + assert(res == SIZE || res == LFS_ERR_CORRUPT); + if (res == LFS_ERR_CORRUPT) { + lfsr_file_close(&lfs, &file) => 0; + goto corrupt_mounted; + } + int err = lfsr_file_close(&lfs, &file); + if (err == LFS_ERR_CORRUPT) { + goto corrupt_mounted; + } + + // if we made it here without erroring we should be able to + // read our file + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, LFS_M_RDWR | LFS_M_CKPROGS, CFG) => 0; + } + + lfsr_file_open(&lfs, &file, "stygiomedusa", LFS_O_RDONLY) => 0; + uint8_t rbuf[SIZE]; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + } + } + + corrupt_mounted:; + lfsr_unmount(&lfs) => 0; + + // reset badbit + lfs_emubd_markgood(CFG, badblock) => 0; + } +''' + +# test every single-bit error in a file's btree node +[cases.test_ck_ckprogs_btree] +defines.BADBIT = -1 +defines.BADBLOCK_BEHAVIOR = 'LFS_EMUBD_BADBLOCK_PROGFLIP' +# force the file to create a btree +defines.INLINE_SIZE = 0 +defines.CRYSTAL_THRESH = -1 +defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8' +defines.SIZE = '2*FRAGMENT_SIZE' +code = ''' + // first we need to figure out where the btree block will actually + // end up, fortunately our block randomization is intentionally + // consistent + + // format + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR | LFS_M_CKPROGS, CFG) => 0; + + // create a file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "stygiomedusa", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + uint32_t prng = 42; + uint8_t wbuf[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // find the btree block + lfsr_traversal_t t; + lfsr_traversal_open(&lfs, &t, 0) => 0; + lfs_block_t badblock; + while (true) { + struct lfs_tinfo tinfo; + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + if (tinfo.btype == LFS_BTYPE_BTREE) { + badblock = tinfo.block; + break; + } + } + lfsr_traversal_close(&lfs, &t) => 0; + + lfsr_unmount(&lfs) => 0; + + // now test all bad bits in the btree block + for (lfs_size_t i = 0; + i < ((BADBIT == -1) ? 8*BLOCK_SIZE : 1); + i++) { + lfs_size_t badbit = (BADBIT == -1) ? i : BADBIT; + + // mark our badbit as bad + lfs_emubd_markbadbit(CFG, badblock, badbit) => 0; + printf("--- badblock: 0x%x.%x, badbit: 0x%x (0x%x+%x) ---\n", + badblock, badbit/8, badbit, badbit/8, badbit%8); + + // format + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR | LFS_M_CKPROGS, CFG) => 0; + + { + // create a file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "stygiomedusa", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + uint32_t prng = 42; + uint8_t wbuf[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfs_ssize_t res = lfsr_file_write(&lfs, &file, wbuf, SIZE); + assert(res == SIZE || res == LFS_ERR_CORRUPT); + if (res == LFS_ERR_CORRUPT) { + lfsr_file_close(&lfs, &file) => 0; + goto corrupt_mounted; + } + int err = lfsr_file_close(&lfs, &file); + if (err == LFS_ERR_CORRUPT) { + goto corrupt_mounted; + } + + // if we made it here without erroring we should be able to + // read our file + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, LFS_M_RDWR | LFS_M_CKPROGS, CFG) => 0; + } + + lfsr_file_open(&lfs, &file, "stygiomedusa", LFS_O_RDONLY) => 0; + uint8_t rbuf[SIZE]; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + } + } + + corrupt_mounted:; + lfsr_unmount(&lfs) => 0; + + // reset badbit + lfs_emubd_markgood(CFG, badblock) => 0; + } +''' + + + +# Some simple ckread tests +# +# We test these much more aggressively in test_badblocks + +# These tests were originally intended to test all single-bit +# metastability errors with ckreads, however they quickly found that +# ckreads can't actually guarantee single-bit error-detection since +# the bit flip may alter the leb128 encoded size field and find a new, +# erronous, parity bit. +# +# For example, one bit flip: +# +# 40 0c 00 12 80 0d ff ff +# '----.----' ^--------------------. +# '- altble 0xc w0 -18 parity=1 +# +# 40 0c 80 12 80 0d ff ff +# '-------.-------' ^----------------------. +# '- altble 0xc w2304 -1664 parity=1 +# +# This doesn't make ckreads _completely_ useless, just mostly useless. +# We can still use it to check parity bits, but without a systematic +# proof. +# +# So for now these tests are sort of in stasis, limited to testing +# metastability in areas we know we can detect (revision counts, raw +# data blocks, etc). Maybe future features will make them more useful. +# + +# test every single-bit error in block 0/1 +[cases.test_ck_ckreads_mroot] +defines.BADBLOCK = [0, 1] +defines.BADBIT = -1 +defines.BADBLOCK_BEHAVIOR = [ + 'LFS_EMUBD_BADBLOCK_PROGFLIP', + 'LFS_EMUBD_BADBLOCK_READFLIP', +] +# this should stay inlined +defines.SIZE = 'BLOCK_SIZE/16' +code = ''' + // test all bad bits in the mroot + for (lfs_size_t i = 0; + // we can't detect metastable tags, so limit read-flips + // to our revision count + i < ((BADBIT == -1) ? 8*4 : 1); + i++) { + lfs_size_t badbit = (BADBIT == -1) ? i : BADBIT; + + // reset the bd prng every run for reproducibility + lfs_emubd_seed(CFG, 42) => 0; + + // mark our badbit as bad + lfs_emubd_markbadbit(CFG, BADBLOCK, badbit) => 0; + printf("--- badblock: 0x%x.%x, badbit: 0x%x (0x%x+%x) ---\n", + (lfs_size_t)BADBLOCK, badbit/8, badbit, badbit/8, badbit%8); + + // With metastability, basically any filesystem operation can + // return LFS_ERR_CORRUPT. This is ok, what we're really testing + // for is no internal/external asserts failing. + + // format + lfs_t lfs; + int err = lfsr_format(&lfs, CFG); + assert(!err || err == LFS_ERR_CORRUPT); + if (err == LFS_ERR_CORRUPT) { + goto corrupt; + } + err = lfsr_mount(&lfs, LFS_M_RDWR, CFG); + assert(!err || err == LFS_ERR_CORRUPT); + if (err == LFS_ERR_CORRUPT) { + goto corrupt; + } + + { + // create a file + lfsr_file_t file; + err = lfsr_file_open(&lfs, &file, "bathykorus", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL); + assert(!err || err == LFS_ERR_CORRUPT); + if (err == LFS_ERR_CORRUPT) { + goto corrupt_mounted; + } + uint32_t prng = 42; + uint8_t wbuf[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfs_ssize_t res = lfsr_file_write(&lfs, &file, wbuf, SIZE); + assert(res == SIZE || res == LFS_ERR_CORRUPT); + if (res == LFS_ERR_CORRUPT) { + lfsr_file_close(&lfs, &file) => 0; + goto corrupt_mounted; + } + err = lfsr_file_close(&lfs, &file); + if (err == LFS_ERR_CORRUPT) { + goto corrupt_mounted; + } + + // try to read our file + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfsr_unmount(&lfs) => 0; + err = lfsr_mount(&lfs, LFS_M_RDWR, CFG); + if (err == LFS_ERR_CORRUPT) { + goto corrupt; + } + } + + // yes reads can fail here + err = lfsr_file_open(&lfs, &file, "bathykorus", LFS_O_RDONLY); + assert(!err + || err == LFS_ERR_CORRUPT + // metastability can also cause our fs state to "rollback", + // which is not great but we can't solve this with ckreads + // alone + || err == LFS_ERR_NOENT); + if (err == LFS_ERR_CORRUPT || err == LFS_ERR_NOENT) { + goto corrupt_mounted; + } + uint8_t rbuf[SIZE]; + lfs_ssize_t res = lfsr_file_read(&lfs, &file, rbuf, SIZE); + assert(res == SIZE || res == LFS_ERR_CORRUPT); + if (res == LFS_ERR_CORRUPT) { + lfsr_file_close(&lfs, &file) => 0; + goto corrupt_mounted; + } + assert(memcmp(rbuf, wbuf, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + } + } + + corrupt_mounted:; + lfsr_unmount(&lfs) => 0; + + corrupt:; + // reset badbit + lfs_emubd_markgood(CFG, BADBLOCK) => 0; + } +''' + +# test every single-bit error in a file's data block +[cases.test_ck_ckreads_data] +defines.BADBIT = -1 +defines.BADBLOCK_BEHAVIOR = [ + 'LFS_EMUBD_BADBLOCK_PROGFLIP', + 'LFS_EMUBD_BADBLOCK_READFLIP', +] +# this should create a single block file +defines.SIZE = 'BLOCK_SIZE' +code = ''' + // first we need to figure out where the data block will actually + // end up, fortunately our block randomization is intentionally + // consistent + + // format + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + + // create a file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "bathykorus", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + uint32_t prng = 42; + uint8_t wbuf[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // find the data block + lfsr_traversal_t t; + lfsr_traversal_open(&lfs, &t, 0) => 0; + lfs_block_t badblock; + while (true) { + struct lfs_tinfo tinfo; + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + if (tinfo.btype == LFS_BTYPE_DATA) { + badblock = tinfo.block; + break; + } + } + lfsr_traversal_close(&lfs, &t) => 0; + + lfsr_unmount(&lfs) => 0; + + // now test all bad bits in the data block + for (lfs_size_t i = 0; + i < ((BADBIT == -1) ? 8*BLOCK_SIZE : 1); + i++) { + lfs_size_t badbit = (BADBIT == -1) ? i : BADBIT; + + // reset the bd prng every run for reproducibility + lfs_emubd_seed(CFG, 42) => 0; + + // mark our badbit as bad + lfs_emubd_markbadbit(CFG, badblock, badbit) => 0; + printf("--- badblock: 0x%x.%x, badbit: 0x%x (0x%x+%x) ---\n", + badblock, badbit/8, badbit, badbit/8, badbit%8); + + // With metastability, basically any filesystem operation can + // return LFS_ERR_CORRUPT. This is ok, what we're really testing + // for is no internal/external asserts failing. + + // format + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + + { + // create a file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "bathykorus", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + uint32_t prng = 42; + uint8_t wbuf[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfs_ssize_t res = lfsr_file_write(&lfs, &file, wbuf, SIZE); + assert(res == SIZE || res == LFS_ERR_CORRUPT); + if (res == LFS_ERR_CORRUPT) { + lfsr_file_close(&lfs, &file) => 0; + goto corrupt_mounted; + } + int err = lfsr_file_close(&lfs, &file); + if (err == LFS_ERR_CORRUPT) { + goto corrupt_mounted; + } + + // try to read our file + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + } + + // yes reads can fail here + err = lfsr_file_open(&lfs, &file, "bathykorus", LFS_O_RDONLY); + assert(!err + || err == LFS_ERR_CORRUPT + // metastability can also cause our fs state to "rollback", + // which is not great but we can't solve this with ckreads + // alone + || err == LFS_ERR_NOENT); + if (err == LFS_ERR_CORRUPT || err == LFS_ERR_NOENT) { + goto corrupt_mounted; + } + uint8_t rbuf[SIZE]; + lfs_ssize_t res = lfsr_file_read(&lfs, &file, rbuf, SIZE); + assert(res == SIZE || res == LFS_ERR_CORRUPT); + if (res == LFS_ERR_CORRUPT) { + lfsr_file_close(&lfs, &file) => 0; + goto corrupt_mounted; + } + assert(memcmp(rbuf, wbuf, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + } + } + + corrupt_mounted:; + lfsr_unmount(&lfs) => 0; + + // reset badbit + lfs_emubd_markgood(CFG, badblock) => 0; + } +''' + +# test every single-bit error in a file's btree node +[cases.test_ck_ckreads_btree] +defines.BADBIT = -1 +defines.BADBLOCK_BEHAVIOR = [ + 'LFS_EMUBD_BADBLOCK_PROGFLIP', + 'LFS_EMUBD_BADBLOCK_READFLIP', +] +# force the file to create a btree +defines.INLINE_SIZE = 0 +defines.CRYSTAL_THRESH = -1 +defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8' +defines.SIZE = '2*FRAGMENT_SIZE' +code = ''' + // first we need to figure out where the btree block will actually + // end up, fortunately our block randomization is intentionally + // consistent + + // format + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + + // create a file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "bathykorus", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + uint32_t prng = 42; + uint8_t wbuf[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // find the btree block + lfsr_traversal_t t; + lfsr_traversal_open(&lfs, &t, 0) => 0; + lfs_block_t badblock; + while (true) { + struct lfs_tinfo tinfo; + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + if (tinfo.btype == LFS_BTYPE_BTREE) { + badblock = tinfo.block; + break; + } + } + lfsr_traversal_close(&lfs, &t) => 0; + + lfsr_unmount(&lfs) => 0; + + // now test all bad bits in the btree block + for (lfs_size_t i = 0; + // we can't detect metastable tags, so limit read-flips + // to our revision count + i < ((BADBIT == -1) ? 8*4 : 1); + i++) { + lfs_size_t badbit = (BADBIT == -1) ? i : BADBIT; + + // reset the bd prng every run for reproducibility + lfs_emubd_seed(CFG, 42) => 0; + + // mark our badbit as bad + lfs_emubd_markbadbit(CFG, badblock, badbit) => 0; + printf("--- badblock: 0x%x.%x, badbit: 0x%x (0x%x+%x) ---\n", + badblock, badbit/8, badbit, badbit/8, badbit%8); + + // With metastability, basically any filesystem operation can + // return LFS_ERR_CORRUPT. This is ok, what we're really testing + // for is no internal/external asserts failing. + + // format + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + + { + // create a file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "bathykorus", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + uint32_t prng = 42; + uint8_t wbuf[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfs_ssize_t res = lfsr_file_write(&lfs, &file, wbuf, SIZE); + assert(res == SIZE || res == LFS_ERR_CORRUPT); + if (res == LFS_ERR_CORRUPT) { + lfsr_file_close(&lfs, &file) => 0; + goto corrupt_mounted; + } + int err = lfsr_file_close(&lfs, &file); + if (err == LFS_ERR_CORRUPT) { + goto corrupt_mounted; + } + + // try to read our file + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + } + + // yes reads can fail here + err = lfsr_file_open(&lfs, &file, "bathykorus", LFS_O_RDONLY); + assert(!err + || err == LFS_ERR_CORRUPT + // metastability can also cause our fs state to "rollback", + // which is not great but we can't solve this with ckreads + // alone + || err == LFS_ERR_NOENT); + if (err == LFS_ERR_CORRUPT || err == LFS_ERR_NOENT) { + goto corrupt_mounted; + } + uint8_t rbuf[SIZE]; + lfs_ssize_t res = lfsr_file_read(&lfs, &file, rbuf, SIZE); + assert(res == SIZE || res == LFS_ERR_CORRUPT); + if (res == LFS_ERR_CORRUPT) { + lfsr_file_close(&lfs, &file) => 0; + goto corrupt_mounted; + } + assert(memcmp(rbuf, wbuf, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + } + } + + corrupt_mounted:; + lfsr_unmount(&lfs) => 0; + + // reset badbit + lfs_emubd_markgood(CFG, badblock) => 0; + } +'''