(Re)implemented bad prog/erase recovery
This (re)implements the heavy-hitting tests in test_badblocks that rakes
filesystem operations over various types of prog/erase failures:
- test_badblocks_[one|region|alternating]_btree - force tall B-trees
- test_badblocks_[one|region|alternating]_dirs - large mtree
- test_badblocks_[one|region|alternating]_files - mixed mtree + files
- test_badblocks_[one|region|alternating]_fwrite_fuzz - complex files
- test_badblocks_[one|region|alternating]_orphanzombiedir_fuzz - complex
- test_badblocks_mrootanchor - uh, format fails, cheap test though
Where:
- test_badblocks_one_* - runs with every possible bad block
- test_badblocks_region_* - runs with a large region of bad blocks
- test_badblocks_alternating_* - runs with alternating bad blocks, this
one is rough for block pair allocations
This required quite a bit of rewiring of internal block allocations. I
knew this would eventually need to be (re)implemented, but the jump from
infallible to fallible progs everywhere was still quite involved:
- lfs_alloc no longer returns LFS_ERR_CORRUPT if erase fails, instead it
will keep searching for a block where an erase "sticks" or return
LFS_ERR_NOENT. This simplifies above layers.
This actually turned out to be required since the lookahead traversal
can also return LFS_ERR_CORRUPT... which needs to be treated as a hard
error and bail.
- In lfsr_btree_commit_ all inner-node compactions needed alloc loops.
This really complements B-tree's copy-on-write behavior, but does make
lfsr_btree_commit_ a bit of a goto soup...
- Same for lfsr_btree_commit/lfsr_bshrub_commit, but fortunately there
are nice and self-contained.
- lfsr_mdir_alloc__/lfsr_mdir_swap__ needed a bit of an overhaul to be
able to handle bad progs. lfsr_mdir_alloc__ now takes a bool `all`
parameter to know if it should allocate one or two of the mdir blocks.
You could argue it's simpler/cheaper to always allocate two blocks at
a time, but this could lead to premature filesystem death on
unfortunate bad block patterns. test_badblocks_alternating_*
specifically tests for this. Note we still allocate both on
relocation, but only on the first commit attempt.
This also rearranges things to move the overcompacting logic out of
lfsr_mdir_swap__ and into lfsr_mdir_commit_, since we only want to
overcompact after trying to program all possible free blocks.
- lfsr_file_flush_ now needs to rewrite the entire block of data if a
prog fails, even if appending an existing data block.
Humorously, this was really easy, since we already align everything to
any existing blocks as a part of our crystallization algorithm. Almost
too easy... (no new code! only a couple gotos! scary!)
Note some of these may be transformable into simpler while loops, but I
decided to avoid this and prefer explicit `relocate` gotos because: 1.
in some functions these end up deeply nested in existing loops and I was
already bitten by a shadowed continue, 2. the "good" path does not loop,
with a loop you need an easy to miss break and the intention is less
clear, and 3. consistency is good.
We are _not_ testing read errors yet. This is because we no longer read
back progs and the relaxed rcache/pcache alignment requirements make
this a bit difficult to (re)implement. User feedback also suggests we
may want to make this optional... So need to think on how to address
this.
Some other notes:
- Our low-level bd wrappers, lfsr_bd_*__, now log bad ops via LFS_DEBUG.
- Overcompaction is now an LFS_WARN.
- The pcache is now correctly dropped if we error during flush.
- I noticed lfsr_btree_alloc double allocated for new B-trees, it
doesn't now, maybe change this function?
- Our B-tree tests all stop on LFS_ERR_NOSPC, but this isn't guaranteed
since our filesystem isn't in a valid state. We should make sure none
of our B-tree tests actually rely on this...
Honestly, considering how much new logic was introduced, this really did
not impact code cost as much as I thought it would. Probably thanks to
the underlying data structures being built to easily discard blocks in
the first place:
code stack
before: 33474 2640
after: 33618 (+0.4%) 2648 (+0.3%)
This commit is contained in:
@@ -48,6 +48,8 @@ static int lfsr_bd_read__(lfs_t *lfs, lfs_block_t block, lfs_size_t off,
|
||||
int err = lfs->cfg->read(lfs->cfg, block, off, buffer, size);
|
||||
LFS_ASSERT(err <= 0);
|
||||
if (err) {
|
||||
LFS_DEBUG("Bad read 0x%"PRIx32".%"PRIx32" %"PRIu32" (%d)",
|
||||
block, off, size, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -67,6 +69,8 @@ static int lfsr_bd_prog__(lfs_t *lfs, lfs_block_t block, lfs_size_t off,
|
||||
int err = lfs->cfg->prog(lfs->cfg, block, off, buffer, size);
|
||||
LFS_ASSERT(err <= 0);
|
||||
if (err) {
|
||||
LFS_DEBUG("Bad prog 0x%"PRIx32".%"PRIx32" %"PRIu32" (%d)",
|
||||
block, off, size, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -81,6 +85,8 @@ static int lfsr_bd_erase__(lfs_t *lfs, lfs_block_t block) {
|
||||
int err = lfs->cfg->erase(lfs->cfg, block);
|
||||
LFS_ASSERT(err <= 0);
|
||||
if (err) {
|
||||
LFS_DEBUG("Bad erase 0x%"PRIx32" (%d)",
|
||||
block, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -92,6 +98,7 @@ static int lfsr_bd_sync__(lfs_t *lfs) {
|
||||
int err = lfs->cfg->sync(lfs->cfg);
|
||||
LFS_ASSERT(err <= 0);
|
||||
if (err) {
|
||||
LFS_DEBUG("Bad sync (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -304,6 +311,9 @@ static int lfsr_bd_flush(lfs_t *lfs, uint32_t *cksum_) {
|
||||
lfs->pcache.off, lfs->pcache.buffer, aligned_size,
|
||||
cksum_);
|
||||
if (err) {
|
||||
// if an error occurs we really don't want to flush our
|
||||
// cache again
|
||||
lfsr_bd_droppcache(lfs);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -4027,7 +4037,11 @@ static int lfsr_data_readbtree(lfs_t *lfs, lfsr_data_t *data,
|
||||
// core btree operations
|
||||
|
||||
static int lfsr_btree_alloc(lfs_t *lfs, lfsr_btree_t *btree) {
|
||||
return lfsr_rbyd_alloc(lfs, btree);
|
||||
(void)lfs;
|
||||
// TODO do we need this function then?
|
||||
// allocate lazily
|
||||
*btree = (lfsr_btree_t){.trunk=0, .weight=0};
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lfsr_btree_lookupnext_(lfs_t *lfs, const lfsr_btree_t *btree,
|
||||
@@ -4286,8 +4300,7 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
int err = lfsr_rbyd_appendattrs(lfs, &rbyd_, rid, -1, -1,
|
||||
attrs, attr_count);
|
||||
if (err) {
|
||||
// TODO wait should we also move if there is corruption here?
|
||||
if (err == LFS_ERR_RANGE) {
|
||||
if (err == LFS_ERR_RANGE || err == LFS_ERR_CORRUPT) {
|
||||
goto compact;
|
||||
}
|
||||
return err;
|
||||
@@ -4295,54 +4308,13 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
|
||||
err = lfsr_rbyd_appendcksum(lfs, &rbyd_);
|
||||
if (err) {
|
||||
if (err == LFS_ERR_RANGE) {
|
||||
if (err == LFS_ERR_RANGE || err == LFS_ERR_CORRUPT) {
|
||||
goto compact;
|
||||
}
|
||||
// TODO wait should we also move if there is corruption here?
|
||||
return err;
|
||||
}
|
||||
|
||||
finalize:;
|
||||
// done?
|
||||
if (!lfsr_rbyd_trunk(&parent)) {
|
||||
LFS_ASSERT(bid == 0);
|
||||
*btree = rbyd_;
|
||||
*attr_count_ = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// is our parent the root and is the root degenerate?
|
||||
if (rbyd.weight == btree->weight) {
|
||||
// collapse the root, decreasing the height of the tree
|
||||
*btree = rbyd_;
|
||||
*attr_count_ = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// prepare commit to parent, tail recursing upwards
|
||||
//
|
||||
// note that since we defer merges to compaction time, we can
|
||||
// end up removing an rbyd here
|
||||
attr_count = 0;
|
||||
bid -= pid - (rbyd.weight-1);
|
||||
if (rbyd_.weight == 0) {
|
||||
scratch->attrs[attr_count++] = LFSR_ATTR(
|
||||
LFSR_TAG_RM, -rbyd.weight, LFSR_DATA_NULL());
|
||||
} else {
|
||||
scratch->attrs[attr_count++] = LFSR_ATTR(
|
||||
LFSR_TAG_BRANCH, 0,
|
||||
LFSR_DATA_BRANCH_(&rbyd_, scratch->buf));
|
||||
if (rbyd_.weight != rbyd.weight) {
|
||||
scratch->attrs[attr_count++] = LFSR_ATTR(
|
||||
LFSR_TAG_GROW, -rbyd.weight + rbyd_.weight,
|
||||
LFSR_DATA_NULL());
|
||||
}
|
||||
}
|
||||
attrs = scratch->attrs;
|
||||
|
||||
rbyd = parent;
|
||||
rid = pid;
|
||||
continue;
|
||||
goto recurse;
|
||||
|
||||
compact:;
|
||||
// estimate our compacted size
|
||||
@@ -4471,6 +4443,7 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
}
|
||||
}
|
||||
|
||||
compact_relocate:;
|
||||
// allocate a new rbyd
|
||||
err = lfsr_rbyd_alloc(lfs, &rbyd_);
|
||||
if (err) {
|
||||
@@ -4481,6 +4454,10 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
err = lfsr_rbyd_compact(lfs, &rbyd_, &rbyd, -1, -1);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
if (err == LFS_ERR_CORRUPT) {
|
||||
goto compact_relocate;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -4490,6 +4467,10 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
attrs, attr_count);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
if (err == LFS_ERR_CORRUPT) {
|
||||
goto compact_relocate;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -4497,32 +4478,35 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
err = lfsr_rbyd_appendcksum(lfs, &rbyd_);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
if (err == LFS_ERR_CORRUPT) {
|
||||
goto compact_relocate;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
goto finalize;
|
||||
goto recurse;
|
||||
|
||||
split:;
|
||||
// we should have something to split here
|
||||
LFS_ASSERT(split_rid > 0
|
||||
&& split_rid < (lfsr_srid_t)rbyd.weight);
|
||||
|
||||
split_relocate_l:;
|
||||
// allocate a new rbyd
|
||||
err = lfsr_rbyd_alloc(lfs, &rbyd_);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// allocate a sibling
|
||||
err = lfsr_rbyd_alloc(lfs, &sibling);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// copy over tags < split_rid
|
||||
err = lfsr_rbyd_compact(lfs, &rbyd_, &rbyd, -1, split_rid);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
if (err == LFS_ERR_CORRUPT) {
|
||||
goto split_relocate_l;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -4534,6 +4518,10 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
attrs, attr_count);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
if (err == LFS_ERR_CORRUPT) {
|
||||
goto split_relocate_l;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -4541,6 +4529,17 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
err = lfsr_rbyd_appendcksum(lfs, &rbyd_);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
if (err == LFS_ERR_CORRUPT) {
|
||||
goto split_relocate_l;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
split_relocate_r:;
|
||||
// allocate a sibling
|
||||
err = lfsr_rbyd_alloc(lfs, &sibling);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -4548,6 +4547,10 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
err = lfsr_rbyd_compact(lfs, &sibling, &rbyd, split_rid, -1);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
if (err == LFS_ERR_CORRUPT) {
|
||||
goto split_relocate_r;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -4559,6 +4562,10 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
attrs, attr_count);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
if (err == LFS_ERR_CORRUPT) {
|
||||
goto split_relocate_r;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -4566,6 +4573,10 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
err = lfsr_rbyd_appendcksum(lfs, &sibling);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
if (err == LFS_ERR_CORRUPT) {
|
||||
goto split_relocate_r;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -4575,7 +4586,7 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
if (rbyd_.weight == 0) {
|
||||
rbyd_ = sibling;
|
||||
}
|
||||
goto finalize;
|
||||
goto recurse;
|
||||
}
|
||||
|
||||
// lookup first name in sibling to use as the split name
|
||||
@@ -4642,6 +4653,7 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
continue;
|
||||
|
||||
merge:;
|
||||
merge_relocate:;
|
||||
// allocate a new rbyd
|
||||
err = lfsr_rbyd_alloc(lfs, &rbyd_);
|
||||
if (err) {
|
||||
@@ -4652,18 +4664,30 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
err = lfsr_rbyd_appendcompactrbyd(lfs, &rbyd_, &rbyd, -1, -1);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
if (err == LFS_ERR_CORRUPT) {
|
||||
goto merge_relocate;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
err = lfsr_rbyd_appendcompactrbyd(lfs, &rbyd_, &sibling, -1, -1);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
if (err == LFS_ERR_CORRUPT) {
|
||||
goto merge_relocate;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
err = lfsr_rbyd_appendcompaction(lfs, &rbyd_, 0);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
if (err == LFS_ERR_CORRUPT) {
|
||||
goto merge_relocate;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -4672,6 +4696,11 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
err = lfsr_rbyd_appendattrs(lfs, &rbyd_, rid, -1, -1,
|
||||
attrs, attr_count);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
if (err == LFS_ERR_CORRUPT) {
|
||||
goto merge_relocate;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -4679,6 +4708,10 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
err = lfsr_rbyd_appendcksum(lfs, &rbyd_);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
if (err == LFS_ERR_CORRUPT) {
|
||||
goto merge_relocate;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -4711,6 +4744,48 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
rbyd = parent;
|
||||
rid = pid + sibling.weight;
|
||||
continue;
|
||||
|
||||
recurse:;
|
||||
// done?
|
||||
if (!lfsr_rbyd_trunk(&parent)) {
|
||||
LFS_ASSERT(bid == 0);
|
||||
*btree = rbyd_;
|
||||
*attr_count_ = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// is our parent the root and is the root degenerate?
|
||||
if (rbyd.weight == btree->weight) {
|
||||
// collapse the root, decreasing the height of the tree
|
||||
*btree = rbyd_;
|
||||
*attr_count_ = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// prepare commit to parent, tail recursing upwards
|
||||
//
|
||||
// note that since we defer merges to compaction time, we can
|
||||
// end up removing an rbyd here
|
||||
attr_count = 0;
|
||||
bid -= pid - (rbyd.weight-1);
|
||||
if (rbyd_.weight == 0) {
|
||||
scratch->attrs[attr_count++] = LFSR_ATTR(
|
||||
LFSR_TAG_RM, -rbyd.weight, LFSR_DATA_NULL());
|
||||
} else {
|
||||
scratch->attrs[attr_count++] = LFSR_ATTR(
|
||||
LFSR_TAG_BRANCH, 0,
|
||||
LFSR_DATA_BRANCH_(&rbyd_, scratch->buf));
|
||||
if (rbyd_.weight != rbyd.weight) {
|
||||
scratch->attrs[attr_count++] = LFSR_ATTR(
|
||||
LFSR_TAG_GROW, -rbyd.weight + rbyd_.weight,
|
||||
LFSR_DATA_NULL());
|
||||
}
|
||||
}
|
||||
attrs = scratch->attrs;
|
||||
|
||||
rbyd = parent;
|
||||
rid = pid;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4729,6 +4804,7 @@ static int lfsr_btree_commit(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
if (err == LFS_ERR_RANGE) {
|
||||
LFS_ASSERT(attr_count > 0);
|
||||
|
||||
relocate:;
|
||||
lfsr_rbyd_t rbyd;
|
||||
err = lfsr_rbyd_alloc(lfs, &rbyd);
|
||||
if (err) {
|
||||
@@ -4738,6 +4814,10 @@ static int lfsr_btree_commit(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
err = lfsr_rbyd_commit(lfs, &rbyd, bid, attrs, attr_count);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
if (err == LFS_ERR_CORRUPT) {
|
||||
goto relocate;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -5865,23 +5945,19 @@ static int lfsr_mtree_seek(lfs_t *lfs, const lfsr_mtree_t *mtree,
|
||||
// making lfsr_mdir_commit quite involved and a bit of a mess.
|
||||
|
||||
// low-level mdir operations needed by lfsr_mdir_commit
|
||||
static int lfsr_mdir_alloc__(lfs_t *lfs, lfsr_mdir_t *mdir, lfsr_smid_t mid) {
|
||||
static int lfsr_mdir_alloc__(lfs_t *lfs, lfsr_mdir_t *mdir,
|
||||
lfsr_smid_t mid, bool all) {
|
||||
// assign the mid
|
||||
mdir->mid = mid;
|
||||
|
||||
// allocate two blocks
|
||||
for (int i = 0; i < 2; i++) {
|
||||
int err = lfs_alloc(lfs, &mdir->rbyd.blocks[i], false);
|
||||
if (all) {
|
||||
// allocate one block without an erase
|
||||
int err = lfs_alloc(lfs, &mdir->rbyd.blocks[1], false);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
mdir->rbyd.weight = 0;
|
||||
mdir->rbyd.trunk = 0;
|
||||
mdir->rbyd.eoff = 0;
|
||||
mdir->rbyd.cksum = 0;
|
||||
|
||||
// read the new revision count
|
||||
//
|
||||
// we use whatever is on-disk to avoid needing to rewrite the
|
||||
@@ -5894,19 +5970,27 @@ static int lfsr_mdir_alloc__(lfs_t *lfs, lfsr_mdir_t *mdir, lfsr_smid_t mid) {
|
||||
}
|
||||
// note we allow corrupt errors here, as long as they are consistent
|
||||
rev = (err != LFS_ERR_CORRUPT) ? lfs_fromle32_(&rev) : 0;
|
||||
|
||||
// reset recycle bits in revision count and increment
|
||||
rev = lfsr_rev_init(lfs, rev);
|
||||
|
||||
// erase, preparing for compact
|
||||
err = lfsr_bd_erase(lfs, mdir->rbyd.blocks[0]);
|
||||
relocate:;
|
||||
// allocate another block with an erase
|
||||
err = lfs_alloc(lfs, &mdir->rbyd.blocks[0], true);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
mdir->rbyd.weight = 0;
|
||||
mdir->rbyd.trunk = 0;
|
||||
mdir->rbyd.eoff = 0;
|
||||
mdir->rbyd.cksum = 0;
|
||||
|
||||
// write our revision count
|
||||
err = lfsr_rbyd_appendrev(lfs, &mdir->rbyd, rev);
|
||||
if (err) {
|
||||
// bad prog? try another block
|
||||
if (err == LFS_ERR_CORRUPT) {
|
||||
goto relocate;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -5927,21 +6011,12 @@ static int lfsr_mdir_swap__(lfs_t *lfs, lfsr_mdir_t *mdir_,
|
||||
}
|
||||
// note we allow corrupt errors here, as long as they are consistent
|
||||
rev = (err != LFS_ERR_CORRUPT) ? lfs_fromle32_(&rev) : 0;
|
||||
// increment our revision count
|
||||
rev = lfsr_rev_inc(lfs, rev);
|
||||
|
||||
// decide if we need to relocate
|
||||
if (!force && lfsr_rev_needsrelocation(lfs, rev)) {
|
||||
// alloc a new mdir
|
||||
err = lfsr_mdir_alloc__(lfs, mdir_, mdir->mid);
|
||||
if (err != LFS_ERR_NOSPC) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// no more blocks? wear-leveling falls apart here, but
|
||||
// we can not relocate
|
||||
LFS_WARN("Overcompacting mdir %"PRId32" "
|
||||
"0x{%"PRIx32",%"PRIx32"}",
|
||||
mdir->mid >> lfs->mdir_bits,
|
||||
mdir->rbyd.blocks[0], mdir->rbyd.blocks[1]);
|
||||
return LFS_ERR_NOSPC;
|
||||
}
|
||||
|
||||
// swap our blocks
|
||||
@@ -5959,7 +6034,7 @@ static int lfsr_mdir_swap__(lfs_t *lfs, lfsr_mdir_t *mdir_,
|
||||
}
|
||||
|
||||
// increment our revision count and write it to our rbyd
|
||||
err = lfsr_rbyd_appendrev(lfs, &mdir_->rbyd, lfsr_rev_inc(lfs, rev));
|
||||
err = lfsr_rbyd_appendrev(lfs, &mdir_->rbyd, rev);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
@@ -6505,7 +6580,7 @@ static int lfsr_mdir_commit_(lfs_t *lfs, lfsr_mdir_t *mdir,
|
||||
int err = lfsr_mdir_commit__(lfs, mdir, start_rid, end_rid,
|
||||
mid, attrs, attr_count);
|
||||
if (err) {
|
||||
if (err == LFS_ERR_RANGE) {
|
||||
if (err == LFS_ERR_RANGE || err == LFS_ERR_CORRUPT) {
|
||||
goto compact;
|
||||
}
|
||||
return err;
|
||||
@@ -6531,14 +6606,46 @@ compact:;
|
||||
// swap blocks, increment revision count
|
||||
lfsr_mdir_t mdir_;
|
||||
err = lfsr_mdir_swap__(lfs, &mdir_, mdir, false);
|
||||
if (err && err != LFS_ERR_NOSPC
|
||||
&& err != LFS_ERR_CORRUPT) {
|
||||
return err;
|
||||
}
|
||||
|
||||
bool overcompactable = (err != LFS_ERR_CORRUPT);
|
||||
bool all = true;
|
||||
relocate:;
|
||||
// relocate? bad prog? ok, try allocating a new mdir
|
||||
if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) {
|
||||
err = lfsr_mdir_alloc__(lfs, &mdir_, mdir->mid, all);
|
||||
if (err && !(err == LFS_ERR_NOSPC && overcompactable)) {
|
||||
return err;
|
||||
}
|
||||
all = false;
|
||||
|
||||
// no more blocks? wear-leveling falls apart here, but we can try
|
||||
// without relocating
|
||||
if (err == LFS_ERR_NOSPC) {
|
||||
LFS_WARN("Overcompacting mdir %"PRId32" "
|
||||
"0x{%"PRIx32",%"PRIx32"}",
|
||||
mdir->mid >> lfs->mdir_bits,
|
||||
mdir->rbyd.blocks[0], mdir->rbyd.blocks[1]);
|
||||
overcompactable = false;
|
||||
|
||||
err = lfsr_mdir_swap__(lfs, &mdir_, mdir, true);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// compact our mdir
|
||||
err = lfsr_mdir_compact__(lfs, &mdir_, mdir, start_rid, end_rid);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
if (err == LFS_ERR_CORRUPT) {
|
||||
goto relocate;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -6553,6 +6660,10 @@ compact:;
|
||||
mid, attrs, attr_count);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
if (err == LFS_ERR_CORRUPT) {
|
||||
goto relocate;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -6670,8 +6781,7 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
|
||||
lfsr_srid_t split_rid;
|
||||
int err = lfsr_mdir_commit_(lfs, &mdir_[0], -1, -1, &split_rid,
|
||||
mdir->mid, attrs, attr_count);
|
||||
if (err
|
||||
&& err != LFS_ERR_RANGE
|
||||
if (err && err != LFS_ERR_RANGE
|
||||
&& err != LFS_ERR_NOENT) {
|
||||
goto failed;
|
||||
}
|
||||
@@ -6707,12 +6817,15 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
|
||||
// shrubs are staged correctly
|
||||
bool left = lfsr_mid_rid(lfs, mdir->mid) < split_rid;
|
||||
|
||||
bool all = true;
|
||||
split_relocate:;
|
||||
// alloc and compact into new mdirs
|
||||
err = lfsr_mdir_alloc__(lfs, &mdir_[i^left],
|
||||
lfs_smax32(mdir->mid, 0));
|
||||
lfs_smax32(mdir->mid, 0), all);
|
||||
if (err) {
|
||||
goto failed;
|
||||
}
|
||||
all = false;
|
||||
|
||||
err = lfsr_mdir_compact__(lfs, &mdir_[i^left],
|
||||
mdir,
|
||||
@@ -6720,6 +6833,10 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
|
||||
((i^left) == 0) ? split_rid : -1);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
if (err == LFS_ERR_CORRUPT) {
|
||||
goto split_relocate;
|
||||
}
|
||||
goto failed;
|
||||
}
|
||||
|
||||
@@ -6729,6 +6846,10 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
|
||||
mdir->mid, attrs, attr_count);
|
||||
if (err && err != LFS_ERR_NOENT) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
if (err == LFS_ERR_CORRUPT) {
|
||||
goto split_relocate;
|
||||
}
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
@@ -6758,7 +6879,7 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
|
||||
"0x{%"PRIx32",%"PRIx32"}",
|
||||
mdir_[1].mid >> lfs->mdir_bits,
|
||||
mdir_[1].rbyd.blocks[0], mdir_[1].rbyd.blocks[1]);
|
||||
goto drop;
|
||||
goto dropped;
|
||||
|
||||
// one sibling reduced to zero
|
||||
} else if (mdir_[0].rbyd.weight == 0) {
|
||||
@@ -6767,7 +6888,7 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
|
||||
mdir_[0].mid >> lfs->mdir_bits,
|
||||
mdir_[0].rbyd.blocks[0], mdir_[0].rbyd.blocks[1]);
|
||||
mdir_[0].rbyd = mdir_[1].rbyd;
|
||||
goto relocate;
|
||||
goto relocated;
|
||||
|
||||
// other sibling reduced to zero
|
||||
} else if (mdir_[1].rbyd.weight == 0) {
|
||||
@@ -6775,7 +6896,7 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
|
||||
"0x{%"PRIx32",%"PRIx32"}",
|
||||
mdir_[1].mid >> lfs->mdir_bits,
|
||||
mdir_[1].rbyd.blocks[0], mdir_[1].rbyd.blocks[1]);
|
||||
goto relocate;
|
||||
goto relocated;
|
||||
}
|
||||
|
||||
// no siblings reduced to zero, update our mtree
|
||||
@@ -6859,7 +6980,7 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
|
||||
goto failed;
|
||||
}
|
||||
|
||||
drop:;
|
||||
dropped:;
|
||||
mdelta = -(1 << lfs->mdir_bits);
|
||||
|
||||
// we should never drop a direct mdir, because we always have our
|
||||
@@ -6888,7 +7009,7 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
|
||||
mdir->rbyd.blocks[0], mdir->rbyd.blocks[1],
|
||||
mdir_[0].rbyd.blocks[0], mdir_[0].rbyd.blocks[1]);
|
||||
|
||||
relocate:;
|
||||
relocated:;
|
||||
// new mtree?
|
||||
if (lfsr_mtree_ismptr(&lfs->mtree)) {
|
||||
mtree_ = LFSR_MTREE_MPTR(
|
||||
@@ -8612,10 +8733,15 @@ static int lfs_alloc(lfs_t *lfs, lfs_block_t *block, bool erase) {
|
||||
// found a free block
|
||||
*block = (lfs->lookahead.start + lfs->lookahead.next)
|
||||
% lfs->cfg->block_count;
|
||||
|
||||
// erase requested?
|
||||
if (erase) {
|
||||
int err = lfsr_bd_erase(lfs, *block);
|
||||
if (err) {
|
||||
// bad erase? try another block
|
||||
if (err == LFS_ERR_CORRUPT) {
|
||||
goto next;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
}
|
||||
@@ -8629,11 +8755,14 @@ static int lfs_alloc(lfs_t *lfs, lfs_block_t *block, bool erase) {
|
||||
if (lfs->lookahead.next >= lfs->lookahead.size
|
||||
|| !(lfs->lookahead.buffer[lfs->lookahead.next / 8]
|
||||
& (1 << (lfs->lookahead.next % 8)))) {
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
next:;
|
||||
lfs->lookahead.next += 1;
|
||||
lfs->lookahead.ckpoint -= 1;
|
||||
}
|
||||
@@ -10334,7 +10463,7 @@ static int lfsr_bshrub_commit(lfs_t *lfs, lfsr_file_t *file,
|
||||
//
|
||||
if ((lfs_size_t)estimate > lfs->cfg->shrub_size/2
|
||||
|| estimate + commit_estimate > lfs->cfg->shrub_size) {
|
||||
goto evict;
|
||||
goto relocate;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10372,7 +10501,7 @@ static int lfsr_bshrub_commit(lfs_t *lfs, lfsr_file_t *file,
|
||||
LFS_ASSERT(lfsr_shrub_trunk(&file->bshrub.u.bshrub));
|
||||
return 0;
|
||||
|
||||
evict:;
|
||||
relocate:;
|
||||
// convert to btree
|
||||
lfsr_rbyd_t rbyd;
|
||||
err = lfsr_rbyd_alloc(lfs, &rbyd);
|
||||
@@ -10386,6 +10515,10 @@ evict:;
|
||||
&file->bshrub.u.btree, -1, -1);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
if (err == LFS_ERR_CORRUPT) {
|
||||
goto relocate;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
}
|
||||
@@ -10393,11 +10526,21 @@ evict:;
|
||||
err = lfsr_rbyd_appendattrs(lfs, &rbyd, bid, -1, -1,
|
||||
attrs, attr_count);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
if (err == LFS_ERR_CORRUPT) {
|
||||
goto relocate;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
err = lfsr_rbyd_appendcksum(lfs, &rbyd);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
if (err == LFS_ERR_CORRUPT) {
|
||||
goto relocate;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -10866,7 +11009,11 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
|
||||
}
|
||||
}
|
||||
|
||||
relocate:;
|
||||
// allocate a new block
|
||||
//
|
||||
// note if we relocate, we rewrite the entire block from block_start
|
||||
// using what we can find in our tree
|
||||
int err = lfs_alloc(lfs, &bptr.data.u.disk.block, true);
|
||||
if (err) {
|
||||
return err;
|
||||
@@ -10877,7 +11024,7 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
|
||||
bptr.cksum = 0;
|
||||
|
||||
compact:;
|
||||
// compact data into our new block
|
||||
// compact data into our block
|
||||
//
|
||||
// eagerly merge any right neighbors we see unless that would
|
||||
// put us over our block size
|
||||
@@ -10908,6 +11055,10 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
|
||||
&bptr.cksum);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
if (err == LFS_ERR_CORRUPT) {
|
||||
goto relocate;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -10964,6 +11115,10 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
|
||||
&bptr.cksum);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
if (err == LFS_ERR_CORRUPT) {
|
||||
goto relocate;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -10982,6 +11137,10 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
|
||||
&bptr.cksum);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
if (err == LFS_ERR_CORRUPT) {
|
||||
goto relocate;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -10999,6 +11158,10 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
|
||||
d,
|
||||
&bptr.cksum);
|
||||
if (err) {
|
||||
// bad prog? try another block
|
||||
if (err == LFS_ERR_CORRUPT) {
|
||||
goto relocate;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -11009,6 +11172,10 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
|
||||
// finalize our write
|
||||
err = lfsr_bd_flush(lfs, &bptr.cksum);
|
||||
if (err) {
|
||||
// bad prog? try another block
|
||||
if (err == LFS_ERR_CORRUPT) {
|
||||
goto relocate;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user