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:
+349
-169
@@ -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
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user