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.
This commit is contained in:
Christopher Haster
2024-06-06 16:38:26 -05:00
parent e06964269e
commit b4af52bc72
6 changed files with 379 additions and 175 deletions
+349 -169
View File
@@ -47,51 +47,66 @@ static void lfs_emubd_decblock(lfs_emubd_block_t *block) {
static lfs_emubd_block_t *lfs_emubd_mutblock( static lfs_emubd_block_t *lfs_emubd_mutblock(
const struct lfs_config *cfg, const struct lfs_config *cfg,
lfs_emubd_block_t **block) { lfs_emubd_block_t *block) {
lfs_emubd_block_t *block_ = *block; if (block && block->rc == 1) {
if (block_ && block_->rc == 1) {
// rc == 1? can modify // rc == 1? can modify
return block_; return block;
} else if (block_) { } else if (block) {
// rc > 1? need to create a copy // 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); sizeof(lfs_emubd_block_t) + cfg->block_size);
if (!nblock) { if (!block_) {
return NULL; return NULL;
} }
memcpy(nblock, block_, memcpy(block_, block,
sizeof(lfs_emubd_block_t) + cfg->block_size); sizeof(lfs_emubd_block_t) + cfg->block_size);
nblock->rc = 1; block_->rc = 1;
lfs_emubd_decblock(block_); lfs_emubd_decblock(block);
*block = nblock; return block_;
return nblock;
} else { } else {
// no block? need to allocate // 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); sizeof(lfs_emubd_block_t) + cfg->block_size);
if (!nblock) { if (!block_) {
return NULL; return NULL;
} }
nblock->rc = 1; block_->rc = 1;
nblock->wear = 0; block_->wear = 0;
// zero for consistency // zero for consistency
lfs_emubd_t *bd = cfg->context; lfs_emubd_t *bd = cfg->context;
memset(nblock->data, memset(block_->data,
(bd->cfg->erase_value != -1) ? bd->cfg->erase_value : 0, (bd->cfg->erase_value != -1) ? bd->cfg->erase_value : 0,
cfg->block_size); cfg->block_size);
*block = nblock; return block_;
return nblock;
} }
} }
// 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 // emubd create/destroy
int lfs_emubd_createcfg(const struct lfs_config *cfg, const char *path, 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", " "%p {.erase_value=%"PRId32", .erase_cycles=%"PRIu32", "
".badblock_behavior=%"PRIu8", .power_cycles=%"PRIu32", " ".badblock_behavior=%"PRIu8", .power_cycles=%"PRIu32", "
".powerloss_behavior=%"PRIu8", .powerloss_cb=%p, " ".powerloss_behavior=%"PRIu8", .powerloss_cb=%p, "
".powerloss_data=%p})", ".powerloss_data=%p, seed=%"PRIu32"})",
(void*)cfg, cfg->context, (void*)cfg, cfg->context,
(void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
(void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, (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, path, (void*)bdcfg, bdcfg->erase_value, bdcfg->erase_cycles,
bdcfg->badblock_behavior, bdcfg->power_cycles, bdcfg->badblock_behavior, bdcfg->power_cycles,
bdcfg->powerloss_behavior, (void*)(uintptr_t)bdcfg->powerloss_cb, bdcfg->powerloss_behavior, (void*)(uintptr_t)bdcfg->powerloss_cb,
bdcfg->powerloss_data); bdcfg->powerloss_data, bdcfg->seed);
lfs_emubd_t *bd = cfg->context; lfs_emubd_t *bd = cfg->context;
bd->cfg = bdcfg; bd->cfg = bdcfg;
@@ -121,6 +136,7 @@ int lfs_emubd_createcfg(const struct lfs_config *cfg, const char *path,
bd->readed = 0; bd->readed = 0;
bd->proged = 0; bd->proged = 0;
bd->erased = 0; bd->erased = 0;
bd->prng = bd->cfg->seed;
bd->power_cycles = bd->cfg->power_cycles; bd->power_cycles = bd->cfg->power_cycles;
bd->ooo_before = NULL; bd->ooo_before = NULL;
bd->ooo_after = 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(size % cfg->prog_size == 0);
LFS_ASSERT(off+size <= cfg->block_size); 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? // 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++) { 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 // losing power?
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?
if (bd->power_cycles > 0) { if (bd->power_cycles > 0) {
bd->power_cycles -= 1; bd->power_cycles -= 1;
if (bd->power_cycles == 0) { if (bd->power_cycles == 0) {
// if we're emulating out-of-order writes, revert everything // if emulating some bits, choose a random bit to flip
// unsynced except for our current block if (bd->cfg->powerloss_behavior
if (bd->cfg->powerloss_behavior == LFS_EMUBD_POWERLOSS_OOO) { == 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++) { for (lfs_block_t i = 0; i < cfg->block_count; i++) {
lfs_emubd_decblock(bd->ooo_after[i]); lfs_emubd_decblock(bd->ooo_after[i]);
bd->ooo_after[i] = lfs_emubd_incblock(bd->blocks[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); LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", 0);
return 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 // check if erase is valid
LFS_ASSERT(block < cfg->block_count); LFS_ASSERT(block < cfg->block_count);
// get the block // losing power?
lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[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
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) { if (!b) {
LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", LFS_ERR_NOMEM); LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", LFS_ERR_NOMEM);
return LFS_ERR_NOMEM; return LFS_ERR_NOMEM;
} }
bd->blocks[block] = b;
// block bad? // block bad?
if (bd->cfg->erase_cycles) { 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 == if (bd->cfg->badblock_behavior ==
LFS_EMUBD_BADBLOCK_ERASEERROR) { LFS_EMUBD_BADBLOCK_ERASEERROR) {
LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", LFS_ERR_CORRUPT); 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); LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", 0);
return 0; return 0;
} }
@@ -792,12 +968,15 @@ int lfs_emubd_setwear(const struct lfs_config *cfg,
// check if block is valid // check if block is valid
LFS_ASSERT(block < cfg->block_count); LFS_ASSERT(block < cfg->block_count);
// set the wear // mutate the block
lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]); lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, bd->blocks[block]);
if (!b) { if (!b) {
LFS_EMUBD_TRACE("lfs_emubd_setwear -> %d", LFS_ERR_NOMEM); LFS_EMUBD_TRACE("lfs_emubd_setwear -> %d", LFS_ERR_NOMEM);
return LFS_ERR_NOMEM; return LFS_ERR_NOMEM;
} }
bd->blocks[block] = b;
// set the wear
b->wear = wear; b->wear = wear;
LFS_EMUBD_TRACE("lfs_emubd_setwear -> %d", 0); 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->readed = bd->readed;
copy->proged = bd->proged; copy->proged = bd->proged;
copy->erased = bd->erased; copy->erased = bd->erased;
copy->prng = bd->prng;
copy->power_cycles = bd->power_cycles; copy->power_cycles = bd->power_cycles;
copy->disk = bd->disk; copy->disk = bd->disk;
if (copy->disk) { if (copy->disk) {
+9 -2
View File
@@ -46,8 +46,10 @@ typedef enum lfs_emubd_badblock_behavior {
// Mode determining how power-loss behaves during testing. For now this // Mode determining how power-loss behaves during testing. For now this
// only supports a noop behavior, leaving the data on-disk untouched. // only supports a noop behavior, leaving the data on-disk untouched.
typedef enum lfs_emubd_powerloss_behavior { typedef enum lfs_emubd_powerloss_behavior {
LFS_EMUBD_POWERLOSS_NOOP = 0, // Progs are atomic LFS_EMUBD_POWERLOSS_NOOP = 0, // Progs are atomic
LFS_EMUBD_POWERLOSS_OOO = 1, // Blocks are written out-of-order 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; } lfs_emubd_powerloss_behavior_t;
// Type for measuring read/program/erase operations // Type for measuring read/program/erase operations
@@ -94,6 +96,10 @@ struct lfs_emubd_config {
// Data for power-loss callback // Data for power-loss callback
void *powerloss_data; 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 // Path to file to use as a mirror of the disk. This provides a way to view
// the current state of the block device. // the current state of the block device.
const char *disk_path; const char *disk_path;
@@ -135,6 +141,7 @@ typedef struct lfs_emubd {
lfs_emubd_io_t readed; lfs_emubd_io_t readed;
lfs_emubd_io_t proged; lfs_emubd_io_t proged;
lfs_emubd_io_t erased; lfs_emubd_io_t erased;
uint32_t prng;
lfs_emubd_powercycles_t power_cycles; lfs_emubd_powercycles_t power_cycles;
lfs_emubd_block_t **ooo_before; lfs_emubd_block_t **ooo_before;
lfs_emubd_block_t **ooo_after; lfs_emubd_block_t **ooo_after;
+4 -2
View File
@@ -128,7 +128,8 @@ void bench_permutation(size_t i, uint32_t *buffer, size_t size);
BENCH_DEFINE(ERASE_VALUE, 0xff ) \ BENCH_DEFINE(ERASE_VALUE, 0xff ) \
BENCH_DEFINE(ERASE_CYCLES, 0 ) \ BENCH_DEFINE(ERASE_CYCLES, 0 ) \
BENCH_DEFINE(BADBLOCK_BEHAVIOR, LFS_EMUBD_BADBLOCK_PROGERROR ) \ 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 // declare defines as global intmax_ts
#define BENCH_DEFINE(k, v) \ #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_value = ERASE_VALUE, \
.erase_cycles = ERASE_CYCLES, \ .erase_cycles = ERASE_CYCLES, \
.badblock_behavior = BADBLOCK_BEHAVIOR, \ .badblock_behavior = BADBLOCK_BEHAVIOR, \
.powerloss_behavior = POWERLOSS_BEHAVIOR, .powerloss_behavior = POWERLOSS_BEHAVIOR, \
.seed = EMUBD_SEED,
#endif #endif
+4 -2
View File
@@ -113,7 +113,8 @@ void test_permutation(size_t i, uint32_t *buffer, size_t size);
TEST_DEFINE(ERASE_VALUE, 0xff ) \ TEST_DEFINE(ERASE_VALUE, 0xff ) \
TEST_DEFINE(ERASE_CYCLES, 0 ) \ TEST_DEFINE(ERASE_CYCLES, 0 ) \
TEST_DEFINE(BADBLOCK_BEHAVIOR, LFS_EMUBD_BADBLOCK_PROGERROR ) \ 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 // declare defines as global intmax_ts
#define TEST_DEFINE(k, v) \ #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_value = ERASE_VALUE, \
.erase_cycles = ERASE_CYCLES, \ .erase_cycles = ERASE_CYCLES, \
.badblock_behavior = BADBLOCK_BEHAVIOR, \ .badblock_behavior = BADBLOCK_BEHAVIOR, \
.powerloss_behavior = POWERLOSS_BEHAVIOR, .powerloss_behavior = POWERLOSS_BEHAVIOR, \
.seed = EMUBD_SEED,
#endif #endif
+8
View File
@@ -19,6 +19,8 @@ after = [
[cases.test_powerloss_dir_many] [cases.test_powerloss_dir_many]
defines.POWERLOSS_BEHAVIOR = [ defines.POWERLOSS_BEHAVIOR = [
'LFS_EMUBD_POWERLOSS_NOOP', 'LFS_EMUBD_POWERLOSS_NOOP',
'LFS_EMUBD_POWERLOSS_SOMEBITS',
'LFS_EMUBD_POWERLOSS_MOSTBITS',
'LFS_EMUBD_POWERLOSS_OOO', 'LFS_EMUBD_POWERLOSS_OOO',
] ]
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
@@ -111,6 +113,8 @@ code = '''
[cases.test_powerloss_file_many] [cases.test_powerloss_file_many]
defines.POWERLOSS_BEHAVIOR = [ defines.POWERLOSS_BEHAVIOR = [
'LFS_EMUBD_POWERLOSS_NOOP', 'LFS_EMUBD_POWERLOSS_NOOP',
'LFS_EMUBD_POWERLOSS_SOMEBITS',
'LFS_EMUBD_POWERLOSS_MOSTBITS',
'LFS_EMUBD_POWERLOSS_OOO', 'LFS_EMUBD_POWERLOSS_OOO',
] ]
# inlining has a tendency to hide sync issues, so try without # inlining has a tendency to hide sync issues, so try without
@@ -205,6 +209,8 @@ code = '''
[cases.test_powerloss_file_pl_fuzz] [cases.test_powerloss_file_pl_fuzz]
defines.POWERLOSS_BEHAVIOR = [ defines.POWERLOSS_BEHAVIOR = [
'LFS_EMUBD_POWERLOSS_NOOP', 'LFS_EMUBD_POWERLOSS_NOOP',
'LFS_EMUBD_POWERLOSS_SOMEBITS',
'LFS_EMUBD_POWERLOSS_MOSTBITS',
'LFS_EMUBD_POWERLOSS_OOO', 'LFS_EMUBD_POWERLOSS_OOO',
] ]
# inlining has a tendency to hide sync issues, so try without # inlining has a tendency to hide sync issues, so try without
@@ -429,6 +435,8 @@ code = '''
[cases.test_powerloss_filedir_pl_fuzz] [cases.test_powerloss_filedir_pl_fuzz]
defines.POWERLOSS_BEHAVIOR = [ defines.POWERLOSS_BEHAVIOR = [
'LFS_EMUBD_POWERLOSS_NOOP', 'LFS_EMUBD_POWERLOSS_NOOP',
'LFS_EMUBD_POWERLOSS_SOMEBITS',
'LFS_EMUBD_POWERLOSS_MOSTBITS',
'LFS_EMUBD_POWERLOSS_OOO', 'LFS_EMUBD_POWERLOSS_OOO',
] ]
# inlining has a tendency to hide sync issues, so try without # inlining has a tendency to hide sync issues, so try without
+5
View File
@@ -16865,6 +16865,7 @@ code = '''
// create a null tag hole // create a null tag hole
rbyd = init_rbyd; rbyd = init_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_rbyd_commit(&lfs, &rbyd, -1, LFSR_ATTRS( lfsr_rbyd_commit(&lfs, &rbyd, -1, LFSR_ATTRS(
LFSR_ATTR( LFSR_ATTR(
LFSR_TAG_UATTR(0), 0, LFSR_TAG_UATTR(0), 0,
@@ -16913,6 +16914,7 @@ code = '''
// create a null tag hole // create a null tag hole
rbyd = init_rbyd; rbyd = init_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_rbyd_commit(&lfs, &rbyd, -1, LFSR_ATTRS( lfsr_rbyd_commit(&lfs, &rbyd, -1, LFSR_ATTRS(
LFSR_ATTR( LFSR_ATTR(
LFSR_TAG_UATTR(0), 0, LFSR_TAG_UATTR(0), 0,
@@ -16971,6 +16973,7 @@ code = '''
// create a null tag hole // create a null tag hole
rbyd = init_rbyd; rbyd = init_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_rbyd_commit(&lfs, &rbyd, -1, LFSR_ATTRS( lfsr_rbyd_commit(&lfs, &rbyd, -1, LFSR_ATTRS(
LFSR_ATTR( LFSR_ATTR(
LFSR_TAG_UATTR(0), 0, LFSR_TAG_UATTR(0), 0,
@@ -17028,6 +17031,7 @@ code = '''
// create a null tag hole // create a null tag hole
rbyd = init_rbyd; rbyd = init_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_rbyd_commit(&lfs, &rbyd, -1, LFSR_ATTRS( lfsr_rbyd_commit(&lfs, &rbyd, -1, LFSR_ATTRS(
LFSR_ATTR( LFSR_ATTR(
LFSR_TAG_UATTR(0), 0, LFSR_TAG_UATTR(0), 0,
@@ -17095,6 +17099,7 @@ code = '''
// create a null tag hole // create a null tag hole
rbyd = init_rbyd; rbyd = init_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_rbyd_commit(&lfs, &rbyd, -1, LFSR_ATTRS( lfsr_rbyd_commit(&lfs, &rbyd, -1, LFSR_ATTRS(
LFSR_ATTR( LFSR_ATTR(
LFSR_TAG_UATTR(0), 0, LFSR_TAG_UATTR(0), 0,