From b4af52bc72b6a8cb34506a5baa71cdcb87c15304 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Thu, 6 Jun 2024 16:38:26 -0500 Subject: [PATCH] Implemented SOMEBITS/MOSTBITS emubd powerloss behavior These emulate powerloss behavior where only some of the bits being progged are actually progged if there is a powerloss. This behavior was the original motivation for our ecksums/fcrcs, so it's good to have this tested. As a simplification, these only test the extremes: - LFS_EMUBD_POWERLOSS_SOMEBITS => one bit progged - LFS_EMUBD_POWERLOSS_MOSTBITS => all-but-one bit progged Also they flips bits instead of preserving exact partial prog behavior, but this is allowed (progs can have any intermediate value), has the same effect as partial progs, and should encourage failed progs. This required a number of tweaks in emubd: moved powerloss before prog, moved mutate after powerloss, etc, but these shouldn't affect other powerloss behaviors. Handling powerloss after prog was only to avoid power_cycles=1 being useless, it's not strictly required. Good news is testing so far suggests our ecksum design is sound. --- bd/lfs_emubd.c | 518 +++++++++++++++++++++++++------------- bd/lfs_emubd.h | 11 +- runners/bench_runner.h | 6 +- runners/test_runner.h | 6 +- tests/test_powerloss.toml | 8 + tests/test_rbyd.toml | 5 + 6 files changed, 379 insertions(+), 175 deletions(-) diff --git a/bd/lfs_emubd.c b/bd/lfs_emubd.c index f14fb5a4..458ac17a 100644 --- a/bd/lfs_emubd.c +++ b/bd/lfs_emubd.c @@ -47,51 +47,66 @@ static void lfs_emubd_decblock(lfs_emubd_block_t *block) { static lfs_emubd_block_t *lfs_emubd_mutblock( const struct lfs_config *cfg, - lfs_emubd_block_t **block) { - lfs_emubd_block_t *block_ = *block; - if (block_ && block_->rc == 1) { + lfs_emubd_block_t *block) { + if (block && block->rc == 1) { // rc == 1? can modify - return block_; + return block; - } else if (block_) { + } else if (block) { // rc > 1? need to create a copy - lfs_emubd_block_t *nblock = malloc( + lfs_emubd_block_t *block_ = malloc( sizeof(lfs_emubd_block_t) + cfg->block_size); - if (!nblock) { + if (!block_) { return NULL; } - memcpy(nblock, block_, + memcpy(block_, block, sizeof(lfs_emubd_block_t) + cfg->block_size); - nblock->rc = 1; + block_->rc = 1; - lfs_emubd_decblock(block_); - *block = nblock; - return nblock; + lfs_emubd_decblock(block); + return block_; } else { // no block? need to allocate - lfs_emubd_block_t *nblock = malloc( + lfs_emubd_block_t *block_ = malloc( sizeof(lfs_emubd_block_t) + cfg->block_size); - if (!nblock) { + if (!block_) { return NULL; } - nblock->rc = 1; - nblock->wear = 0; + block_->rc = 1; + block_->wear = 0; // zero for consistency lfs_emubd_t *bd = cfg->context; - memset(nblock->data, + memset(block_->data, (bd->cfg->erase_value != -1) ? bd->cfg->erase_value : 0, cfg->block_size); - *block = nblock; - return nblock; + return block_; } } +// prng used for some emulation things +static uint32_t lfs_emubd_prng(uint32_t *state) { + // A simple xorshift32 generator, easily reproducible. Keep in mind + // determinism is much more important than actual randomness here. + uint32_t x = *state; + // must be non-zero, use uintmax here so that seed=0 is different + // from seed=1 and seed=range(0,n) makes a bit more sense + if (x == 0) { + x = -1; + } + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + *state = x; + return x; +} + + // emubd create/destroy int lfs_emubd_createcfg(const struct lfs_config *cfg, const char *path, @@ -104,7 +119,7 @@ int lfs_emubd_createcfg(const struct lfs_config *cfg, const char *path, "%p {.erase_value=%"PRId32", .erase_cycles=%"PRIu32", " ".badblock_behavior=%"PRIu8", .power_cycles=%"PRIu32", " ".powerloss_behavior=%"PRIu8", .powerloss_cb=%p, " - ".powerloss_data=%p})", + ".powerloss_data=%p, seed=%"PRIu32"})", (void*)cfg, cfg->context, (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, @@ -112,7 +127,7 @@ int lfs_emubd_createcfg(const struct lfs_config *cfg, const char *path, path, (void*)bdcfg, bdcfg->erase_value, bdcfg->erase_cycles, bdcfg->badblock_behavior, bdcfg->power_cycles, bdcfg->powerloss_behavior, (void*)(uintptr_t)bdcfg->powerloss_cb, - bdcfg->powerloss_data); + bdcfg->powerloss_data, bdcfg->seed); lfs_emubd_t *bd = cfg->context; bd->cfg = bdcfg; @@ -121,6 +136,7 @@ int lfs_emubd_createcfg(const struct lfs_config *cfg, const char *path, bd->readed = 0; bd->proged = 0; bd->erased = 0; + bd->prng = bd->cfg->seed; bd->power_cycles = bd->cfg->power_cycles; bd->ooo_before = NULL; bd->ooo_after = NULL; @@ -342,78 +358,100 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, LFS_ASSERT(size % cfg->prog_size == 0); LFS_ASSERT(off+size <= cfg->block_size); - // get 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; - } - - // block bad? - if (bd->cfg->erase_cycles && b->wear >= bd->cfg->erase_cycles) { - 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; - } - } - // were we erased properly? - if (bd->cfg->erase_value != -1) { + LFS_ASSERT(bd->blocks[block]); + if (bd->cfg->erase_value != -1 + && !(bd->cfg->erase_cycles + && bd->blocks[block]->wear >= bd->cfg->erase_cycles)) { for (lfs_off_t i = 0; i < size; i++) { - LFS_ASSERT(b->data[off+i] == bd->cfg->erase_value); + LFS_ASSERT(bd->blocks[block]->data[off+i] == bd->cfg->erase_value); } } - // prog data - memcpy(&b->data[off], buffer, size); - - // 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, buffer, size); - if (res2 < 0) { - int err = -errno; - LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err); - return err; - } - } - - // track progs - bd->proged += size; - if (bd->cfg->prog_sleep) { - int err = nanosleep(&(struct timespec){ - .tv_sec=bd->cfg->prog_sleep/1000000000, - .tv_nsec=bd->cfg->prog_sleep%1000000000}, - NULL); - if (err) { - err = -errno; - LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err); - return err; - } - } - - // lose power? + // losing power? if (bd->power_cycles > 0) { bd->power_cycles -= 1; if (bd->power_cycles == 0) { - // if we're emulating out-of-order writes, revert everything - // unsynced except for our current block - if (bd->cfg->powerloss_behavior == LFS_EMUBD_POWERLOSS_OOO) { + // if emulating some bits, choose a random bit to flip + if (bd->cfg->powerloss_behavior + == LFS_EMUBD_POWERLOSS_SOMEBITS) { + // 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; + + // flip bit + lfs_size_t bit = lfs_emubd_prng(&bd->prng) + % (cfg->prog_size*8); + b->data[off + (bit/8)] ^= (1 << (bit%8)); + + // 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; + } + } + + // if emulating most bits, prog data and choose a random bit + // to flip + } else if (bd->cfg->powerloss_behavior + == LFS_EMUBD_POWERLOSS_MOSTBITS) { + // 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); + + // flip bit + lfs_size_t bit = lfs_emubd_prng(&bd->prng) + % (cfg->prog_size*8); + b->data[off + (bit/8)] ^= (1 << (bit%8)); + + // 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; + } + } + + // if emulating out-of-order writes, revert everything unsynced + // except for our current block + } else if (bd->cfg->powerloss_behavior + == LFS_EMUBD_POWERLOSS_OOO) { for (lfs_block_t i = 0; i < cfg->block_count; i++) { lfs_emubd_decblock(bd->ooo_after[i]); bd->ooo_after[i] = lfs_emubd_incblock(bd->blocks[i]); @@ -486,6 +524,65 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, } } + // 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; + + // block bad? + if (bd->cfg->erase_cycles && b->wear >= bd->cfg->erase_cycles) { + 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; + } + } + + // prog data + memcpy(&b->data[off], buffer, size); + + // 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; + } + } + + // track progs + bd->proged += size; + if (bd->cfg->prog_sleep) { + int err = nanosleep(&(struct timespec){ + .tv_sec=bd->cfg->prog_sleep/1000000000, + .tv_nsec=bd->cfg->prog_sleep%1000000000}, + NULL); + if (err) { + err = -errno; + LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err); + return err; + } + } + LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", 0); return 0; } @@ -498,16 +595,174 @@ int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) { // check if erase is valid LFS_ASSERT(block < cfg->block_count); - // get the block - lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]); + // losing power? + if (bd->power_cycles > 0) { + bd->power_cycles -= 1; + if (bd->power_cycles == 0) { + // if emulating some bits, choose a random bit to flip + if (bd->cfg->powerloss_behavior + == LFS_EMUBD_POWERLOSS_SOMEBITS) { + // 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; + + // flip bit + lfs_size_t bit = lfs_emubd_prng(&bd->prng) + % (cfg->block_size*8); + b->data[(bit/8)] ^= (1 << (bit%8)); + + // 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; + } + } + + // if emulating most bits, erase data and choose a random bit + // to flip + } else if (bd->cfg->powerloss_behavior + == LFS_EMUBD_POWERLOSS_MOSTBITS) { + // 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); + } + + // flip bit + lfs_size_t bit = lfs_emubd_prng(&bd->prng) + % (cfg->block_size*8); + b->data[(bit/8)] ^= (1 << (bit%8)); + + // 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; + } + } + + // if emulating out-of-order writes, revert everything unsynced + // except for our current block + } else if (bd->cfg->powerloss_behavior + == LFS_EMUBD_POWERLOSS_OOO) { + for (lfs_block_t i = 0; i < cfg->block_count; i++) { + if (i != block && bd->blocks[i] != bd->ooo_before[i]) { + lfs_emubd_decblock(bd->blocks[i]); + bd->blocks[i] = lfs_emubd_incblock(bd->ooo_before[i]); + + // mirror to disk file? + if (bd->disk) { + off_t res1 = lseek(bd->disk->fd, + (off_t)i*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, + (bd->blocks[i]) + ? bd->blocks[i]->data + : bd->disk->scratch, + cfg->block_size); + if (res2 < 0) { + int err = -errno; + LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err); + return err; + } + } + } + } + } + + // powerloss! + bd->cfg->powerloss_cb(bd->cfg->powerloss_data); + + // oh, continuing? undo out-of-order write emulation + if (bd->cfg->powerloss_behavior == LFS_EMUBD_POWERLOSS_OOO) { + for (lfs_block_t i = 0; i < cfg->block_count; i++) { + if (bd->blocks[i] != bd->ooo_after[i]) { + lfs_emubd_decblock(bd->blocks[i]); + bd->blocks[i] = lfs_emubd_incblock(bd->ooo_after[i]); + + // mirror to disk file? + if (bd->disk) { + off_t res1 = lseek(bd->disk->fd, + (off_t)i*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, + (bd->blocks[i]) + ? bd->blocks[i]->data + : bd->disk->scratch, + cfg->block_size); + if (res2 < 0) { + int err = -errno; + LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err); + return err; + } + } + } + } + } + } + } + + // 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; // block bad? if (bd->cfg->erase_cycles) { - if (b->wear >= 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); @@ -561,85 +816,6 @@ int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) { } } - // lose power? - if (bd->power_cycles > 0) { - bd->power_cycles -= 1; - if (bd->power_cycles == 0) { - // if we're emulating out-of-order writes, revert everything - // unsynced except for our current block - if (bd->cfg->powerloss_behavior == LFS_EMUBD_POWERLOSS_OOO) { - for (lfs_block_t i = 0; i < cfg->block_count; i++) { - lfs_emubd_decblock(bd->ooo_after[i]); - bd->ooo_after[i] = lfs_emubd_incblock(bd->blocks[i]); - - if (i != block && bd->blocks[i] != bd->ooo_before[i]) { - lfs_emubd_decblock(bd->blocks[i]); - bd->blocks[i] = lfs_emubd_incblock(bd->ooo_before[i]); - - // mirror to disk file? - if (bd->disk) { - off_t res1 = lseek(bd->disk->fd, - (off_t)i*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, - (bd->blocks[i]) - ? bd->blocks[i]->data - : bd->disk->scratch, - cfg->block_size); - if (res2 < 0) { - int err = -errno; - LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err); - return err; - } - } - } - } - } - - // powerloss! - bd->cfg->powerloss_cb(bd->cfg->powerloss_data); - - // oh, continuing? undo out-of-order write emulation - if (bd->cfg->powerloss_behavior == LFS_EMUBD_POWERLOSS_OOO) { - for (lfs_block_t i = 0; i < cfg->block_count; i++) { - if (bd->blocks[i] != bd->ooo_after[i]) { - lfs_emubd_decblock(bd->blocks[i]); - bd->blocks[i] = lfs_emubd_incblock(bd->ooo_after[i]); - - // mirror to disk file? - if (bd->disk) { - off_t res1 = lseek(bd->disk->fd, - (off_t)i*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, - (bd->blocks[i]) - ? bd->blocks[i]->data - : bd->disk->scratch, - cfg->block_size); - if (res2 < 0) { - int err = -errno; - LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err); - return err; - } - } - } - } - } - } - } - LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", 0); return 0; } @@ -792,12 +968,15 @@ int lfs_emubd_setwear(const struct lfs_config *cfg, // check if block is valid LFS_ASSERT(block < cfg->block_count); - // set the wear - lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]); + // mutate the block + lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, bd->blocks[block]); if (!b) { LFS_EMUBD_TRACE("lfs_emubd_setwear -> %d", LFS_ERR_NOMEM); return LFS_ERR_NOMEM; } + bd->blocks[block] = b; + + // set the wear b->wear = wear; LFS_EMUBD_TRACE("lfs_emubd_setwear -> %d", 0); @@ -866,6 +1045,7 @@ int lfs_emubd_copy(const struct lfs_config *cfg, lfs_emubd_t *copy) { copy->readed = bd->readed; copy->proged = bd->proged; copy->erased = bd->erased; + copy->prng = bd->prng; copy->power_cycles = bd->power_cycles; copy->disk = bd->disk; if (copy->disk) { diff --git a/bd/lfs_emubd.h b/bd/lfs_emubd.h index 06b892c2..dfffe08a 100644 --- a/bd/lfs_emubd.h +++ b/bd/lfs_emubd.h @@ -46,8 +46,10 @@ typedef enum lfs_emubd_badblock_behavior { // Mode determining how power-loss behaves during testing. For now this // only supports a noop behavior, leaving the data on-disk untouched. typedef enum lfs_emubd_powerloss_behavior { - LFS_EMUBD_POWERLOSS_NOOP = 0, // Progs are atomic - LFS_EMUBD_POWERLOSS_OOO = 1, // 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_behavior_t; // Type for measuring read/program/erase operations @@ -94,6 +96,10 @@ struct lfs_emubd_config { // Data for power-loss callback void *powerloss_data; + // Seed for prng, which may be used for emulating failed progs. This does + // not affect normal operation. + uint32_t seed; + // Path to file to use as a mirror of the disk. This provides a way to view // the current state of the block device. const char *disk_path; @@ -135,6 +141,7 @@ typedef struct lfs_emubd { lfs_emubd_io_t readed; lfs_emubd_io_t proged; lfs_emubd_io_t erased; + uint32_t prng; lfs_emubd_powercycles_t power_cycles; lfs_emubd_block_t **ooo_before; lfs_emubd_block_t **ooo_after; diff --git a/runners/bench_runner.h b/runners/bench_runner.h index 2c1f1036..5c7a30ac 100644 --- a/runners/bench_runner.h +++ b/runners/bench_runner.h @@ -128,7 +128,8 @@ void bench_permutation(size_t i, uint32_t *buffer, size_t size); BENCH_DEFINE(ERASE_VALUE, 0xff ) \ BENCH_DEFINE(ERASE_CYCLES, 0 ) \ BENCH_DEFINE(BADBLOCK_BEHAVIOR, LFS_EMUBD_BADBLOCK_PROGERROR ) \ - BENCH_DEFINE(POWERLOSS_BEHAVIOR, LFS_EMUBD_POWERLOSS_NOOP ) + BENCH_DEFINE(POWERLOSS_BEHAVIOR, LFS_EMUBD_POWERLOSS_NOOP ) \ + BENCH_DEFINE(EMUBD_SEED, 0 ) // declare defines as global intmax_ts #define BENCH_DEFINE(k, v) \ @@ -158,7 +159,8 @@ void bench_permutation(size_t i, uint32_t *buffer, size_t size); .erase_value = ERASE_VALUE, \ .erase_cycles = ERASE_CYCLES, \ .badblock_behavior = BADBLOCK_BEHAVIOR, \ - .powerloss_behavior = POWERLOSS_BEHAVIOR, + .powerloss_behavior = POWERLOSS_BEHAVIOR, \ + .seed = EMUBD_SEED, #endif diff --git a/runners/test_runner.h b/runners/test_runner.h index 77246c80..b24abdd3 100644 --- a/runners/test_runner.h +++ b/runners/test_runner.h @@ -113,7 +113,8 @@ void test_permutation(size_t i, uint32_t *buffer, size_t size); TEST_DEFINE(ERASE_VALUE, 0xff ) \ TEST_DEFINE(ERASE_CYCLES, 0 ) \ TEST_DEFINE(BADBLOCK_BEHAVIOR, LFS_EMUBD_BADBLOCK_PROGERROR ) \ - TEST_DEFINE(POWERLOSS_BEHAVIOR, LFS_EMUBD_POWERLOSS_NOOP ) + TEST_DEFINE(POWERLOSS_BEHAVIOR, LFS_EMUBD_POWERLOSS_NOOP ) \ + TEST_DEFINE(EMUBD_SEED, 0 ) // declare defines as global intmax_ts #define TEST_DEFINE(k, v) \ @@ -143,7 +144,8 @@ void test_permutation(size_t i, uint32_t *buffer, size_t size); .erase_value = ERASE_VALUE, \ .erase_cycles = ERASE_CYCLES, \ .badblock_behavior = BADBLOCK_BEHAVIOR, \ - .powerloss_behavior = POWERLOSS_BEHAVIOR, + .powerloss_behavior = POWERLOSS_BEHAVIOR, \ + .seed = EMUBD_SEED, #endif diff --git a/tests/test_powerloss.toml b/tests/test_powerloss.toml index b788a62b..dd1a8d2a 100644 --- a/tests/test_powerloss.toml +++ b/tests/test_powerloss.toml @@ -19,6 +19,8 @@ after = [ [cases.test_powerloss_dir_many] defines.POWERLOSS_BEHAVIOR = [ 'LFS_EMUBD_POWERLOSS_NOOP', + 'LFS_EMUBD_POWERLOSS_SOMEBITS', + 'LFS_EMUBD_POWERLOSS_MOSTBITS', 'LFS_EMUBD_POWERLOSS_OOO', ] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] @@ -111,6 +113,8 @@ code = ''' [cases.test_powerloss_file_many] defines.POWERLOSS_BEHAVIOR = [ 'LFS_EMUBD_POWERLOSS_NOOP', + 'LFS_EMUBD_POWERLOSS_SOMEBITS', + 'LFS_EMUBD_POWERLOSS_MOSTBITS', 'LFS_EMUBD_POWERLOSS_OOO', ] # inlining has a tendency to hide sync issues, so try without @@ -205,6 +209,8 @@ code = ''' [cases.test_powerloss_file_pl_fuzz] defines.POWERLOSS_BEHAVIOR = [ 'LFS_EMUBD_POWERLOSS_NOOP', + 'LFS_EMUBD_POWERLOSS_SOMEBITS', + 'LFS_EMUBD_POWERLOSS_MOSTBITS', 'LFS_EMUBD_POWERLOSS_OOO', ] # inlining has a tendency to hide sync issues, so try without @@ -429,6 +435,8 @@ code = ''' [cases.test_powerloss_filedir_pl_fuzz] defines.POWERLOSS_BEHAVIOR = [ 'LFS_EMUBD_POWERLOSS_NOOP', + 'LFS_EMUBD_POWERLOSS_SOMEBITS', + 'LFS_EMUBD_POWERLOSS_MOSTBITS', 'LFS_EMUBD_POWERLOSS_OOO', ] # inlining has a tendency to hide sync issues, so try without diff --git a/tests/test_rbyd.toml b/tests/test_rbyd.toml index d526a3c0..51c3cd11 100644 --- a/tests/test_rbyd.toml +++ b/tests/test_rbyd.toml @@ -16865,6 +16865,7 @@ code = ''' // create a null tag hole rbyd = init_rbyd; + lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, -1, LFSR_ATTRS( LFSR_ATTR( LFSR_TAG_UATTR(0), 0, @@ -16913,6 +16914,7 @@ code = ''' // create a null tag hole rbyd = init_rbyd; + lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, -1, LFSR_ATTRS( LFSR_ATTR( LFSR_TAG_UATTR(0), 0, @@ -16971,6 +16973,7 @@ code = ''' // create a null tag hole rbyd = init_rbyd; + lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, -1, LFSR_ATTRS( LFSR_ATTR( LFSR_TAG_UATTR(0), 0, @@ -17028,6 +17031,7 @@ code = ''' // create a null tag hole rbyd = init_rbyd; + lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, -1, LFSR_ATTRS( LFSR_ATTR( LFSR_TAG_UATTR(0), 0, @@ -17095,6 +17099,7 @@ code = ''' // create a null tag hole rbyd = init_rbyd; + lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, -1, LFSR_ATTRS( LFSR_ATTR( LFSR_TAG_UATTR(0), 0,