t: Separated mtree traversal/iteration, bshrub staging
A number of traversal changes:
- Traversal now traverses the mtree's btree (the inner btree nodes)
separately from iterating over mdirs in the mtree.
This makes resuming clobbered traversals more robust as there's less
state to worry about. It also reduces all btree traversals to a single
state which simplifies the traversal logic and _in theory_ reduces
code/RAM costs.
This does add a second O(n logbn) pass through the mtree, but this
takes the fast path since we already validated btree nodes. mtree
traversal is probably dominated by mdir fetching anyways...
- lfsr_mdir_commit no longer clobbers mid-related traversals. This was a
bit too complicated with attrs potentially inserting new mids.
Instead, it's up to upper layers to explicitly clobber traversals.
Most of these already need to update dir positions, so it's not that
much extra code, but it does add cost.
lfsr_mdir_commit still clobbers mroot/mtree related traversals.
- We now stage bshrubs in traversals during mdir compaction, so we
shouldn't need to clobber traversals when the mdir compacts.
In theory as long as we clobber traversals that reference opened
files, we should never end up being the only reference to a bshrub. So
we should be able to stage bshrubs without cost.
This is _not_ working at the moment, because we aren't updating the
actual btraversal state correctly... not sure how to fix this yet...
Code/stack changes:
code stack
before: 34682 2544
after: 34716 (+0.1%) 2648 (+4.1%)
The surprise stack cost is _very_ interesting. Where is this coming
from?
It turns out when we reduce all btree traversals to a single state, and a
single function call, GCC is happy to inline lfsr_btree_traverse
directly into lfsr_fs_traverse.
This is great for code cost, but now lfs_fs_traverse contains the entire
stack frame of lfsr_btree_traverse, which is quite large. When we called
lfsr_btree_traverse twice, this stack frame was never nested with
lfsr_mtree_lookup, but now our tools think it is...
I'm not sure how to fix this. Maybe improving our tooling to understand
shrinkwrap optimizations will find this doesn't actually cost as much?
Or maybe not since this is in a complicated switch case state machine?
We could use an explicit __attribute__((noinline)), but this sort of
heavy-handed optimization guidance has been out-of-scope for littlefs up
until now...
I'm leaving this as-is for now, but it may be worth looking this again
in the future.
This commit is contained in:
@@ -5209,26 +5209,28 @@ static bool lfsr_mid_isopen(lfs_t *lfs, lfsr_smid_t mid) {
|
||||
}
|
||||
|
||||
// needed in lfsr_opened_clobber
|
||||
static void lfsr_fs_traverseclobber(lfs_t *lfs, lfsr_mtraversal_t *mt);
|
||||
static void lfsr_traversal_clobber(lfs_t *lfs, lfsr_traversal_t *t);
|
||||
|
||||
// clobber any traversals that match our mid, or all traversals if mid=-1
|
||||
static void lfsr_opened_clobber(lfs_t *lfs, lfsr_smid_t mid, bool dirty) {
|
||||
for (lfsr_omdir_t *o = lfs->opened; o; o = o->next) {
|
||||
if (o->type == LFS_TYPE_TRAVERSAL) {
|
||||
// mark as dirty
|
||||
o->flags |= (dirty) ? LFS_F_DIRTY : 0;
|
||||
|
||||
// clobber if mid matches
|
||||
if (mid == -1 || o->mdir.mid == mid) {
|
||||
lfsr_traversal_t *t = (lfsr_traversal_t*)o;
|
||||
lfsr_fs_traverseclobber(lfs, &t->mt);
|
||||
// and clear any pending blocks
|
||||
t->blocks[0] = -1;
|
||||
t->blocks[1] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO
|
||||
//
|
||||
//// clobber any traversals that match our mid, or all traversals if mid=-1
|
||||
//static void lfsr_opened_clobber(lfs_t *lfs, lfsr_smid_t mid, bool dirty) {
|
||||
// for (lfsr_omdir_t *o = lfs->opened; o; o = o->next) {
|
||||
// if (o->type == LFS_TYPE_TRAVERSAL) {
|
||||
// // mark as dirty
|
||||
// o->flags |= (dirty) ? LFS_F_DIRTY : 0;
|
||||
//
|
||||
// // clobber if mid matches
|
||||
// if (mid == -1 || o->mdir.mid == mid) {
|
||||
// lfsr_traversal_t *t = (lfsr_traversal_t*)o;
|
||||
// lfsr_fs_traverseclobber(lfs, &t->mt);
|
||||
// // and clear any pending blocks
|
||||
// t->blocks[0] = -1;
|
||||
// t->blocks[1] = -1;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
|
||||
@@ -5451,7 +5453,8 @@ static int lfsr_shrub_compact(lfs_t *lfs, lfsr_rbyd_t *rbyd_,
|
||||
// this should include our current bshrub
|
||||
for (lfsr_omdir_t *o = lfs->opened; o; o = o->next) {
|
||||
lfsr_file_t *file_ = (lfsr_file_t*)o;
|
||||
if (file_->o.type == LFS_TYPE_REG
|
||||
if ((file_->o.type == LFS_TYPE_REG
|
||||
|| file_->o.type == LFS_TYPE_TRAVERSAL)
|
||||
&& lfsr_bshrub_isbshrub(&file_->o.mdir, &file_->bshrub)
|
||||
&& lfsr_shrub_cmp(&file_->bshrub.u.bshrub, shrub) == 0) {
|
||||
file_->bshrub_.u.bshrub.blocks[0] = rbyd_->blocks[0];
|
||||
@@ -5981,6 +5984,7 @@ static int lfsr_mtree_lookup(lfs_t *lfs, const lfsr_mtree_t *mtree,
|
||||
mid,
|
||||
&bid, &tag, NULL, &data);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_NOENT);
|
||||
return err;
|
||||
}
|
||||
LFS_ASSERT((lfsr_sbid_t)bid == lfsr_mid_bid(lfs, mid));
|
||||
@@ -6845,8 +6849,6 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
|
||||
|
||||
// checkpoint the allocator
|
||||
lfs_alloc_ckpoint(lfs);
|
||||
// clobber any related traversals
|
||||
lfsr_opened_clobber(lfs, mdir->mid, true);
|
||||
|
||||
// play out any attrs that affect our grm _before_ committing to disk,
|
||||
// keep in mind we revert to on-disk gstate if we run into an error
|
||||
@@ -6893,7 +6895,8 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
|
||||
}
|
||||
|
||||
// stage any bsprouts/bshrubs
|
||||
if (o->type == LFS_TYPE_REG) {
|
||||
if (o->type == LFS_TYPE_REG
|
||||
|| o->type == LFS_TYPE_TRAVERSAL) {
|
||||
lfsr_file_t *file = (lfsr_file_t*)o;
|
||||
file->bshrub_ = file->bshrub;
|
||||
}
|
||||
@@ -7362,12 +7365,6 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
|
||||
// update any gstate changes
|
||||
lfsr_fs_commitgdelta(lfs);
|
||||
|
||||
// if mtree/mroot changed, clobber all traversals, too much has changed
|
||||
if (lfsr_mdir_cmp(&mroot_, &lfs->mroot) != 0
|
||||
|| lfsr_mtree_cmp(&mtree_, &lfs->mtree) != 0) {
|
||||
lfsr_opened_clobber(lfs, -1, true);
|
||||
}
|
||||
|
||||
// play out any attrs that affect internal state
|
||||
mid_ = mdir->mid;
|
||||
for (lfs_size_t i = 0; i < attr_count; i++) {
|
||||
@@ -7394,7 +7391,8 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
|
||||
|
||||
// update any staged bsprouts/bshrubs
|
||||
for (lfsr_omdir_t *o = lfs->opened; o; o = o->next) {
|
||||
if (o->type == LFS_TYPE_REG) {
|
||||
if (o->type == LFS_TYPE_REG
|
||||
|| o->type == LFS_TYPE_TRAVERSAL) {
|
||||
lfsr_file_t *file = (lfsr_file_t*)o;
|
||||
file->bshrub = file->bshrub_;
|
||||
}
|
||||
@@ -7409,7 +7407,6 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
|
||||
|
||||
// update any splits/drops
|
||||
if (lfsr_mdir_cmp(&o->mdir, mdir) == 0) {
|
||||
LFS_ASSERT(mdir->mid != -1 || mdir == &lfs->mroot);
|
||||
if (mdelta > 0
|
||||
&& lfsr_mid_rid(lfs, o->mdir.mid)
|
||||
>= (lfsr_srid_t)mdir_[0].rbyd.weight) {
|
||||
@@ -7434,6 +7431,20 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
|
||||
mdir->rbyd = mdir_[0].rbyd;
|
||||
}
|
||||
|
||||
// mark all traversals as dirty
|
||||
for (lfsr_omdir_t *o = lfs->opened; o; o = o->next) {
|
||||
if (o->type == LFS_TYPE_TRAVERSAL) {
|
||||
o->flags |= LFS_F_DIRTY;
|
||||
|
||||
// clobber any mroot/mtree traversals
|
||||
if (o->mdir.mid == -1
|
||||
&& (lfsr_mdir_cmp(&mroot_, &lfs->mroot) != 0
|
||||
|| lfsr_mtree_cmp(&mtree_, &lfs->mtree) != 0)) {
|
||||
lfsr_traversal_clobber(lfs, (lfsr_traversal_t*)o);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update mroot and mtree
|
||||
lfs->mroot = mroot_;
|
||||
lfs->mtree = mtree_;
|
||||
@@ -7766,7 +7777,7 @@ enum {
|
||||
LFSR_TSTATE_MROOTCHAIN = 1,
|
||||
LFSR_TSTATE_MTREE = 2,
|
||||
LFSR_TSTATE_MDIR = 3,
|
||||
LFSR_TSTATE_OMDIR = 4,
|
||||
LFSR_TSTATE_OPENED = 4,
|
||||
LFSR_TSTATE_BTREE = 5,
|
||||
LFSR_TSTATE_DONE = 6,
|
||||
};
|
||||
@@ -7777,6 +7788,7 @@ enum {
|
||||
.o.state=LFSR_TSTATE_MROOTANCHOR, \
|
||||
.o.flags=_flags, \
|
||||
.o.mdir.mid=-1, \
|
||||
.ot=NULL, \
|
||||
.u.mtortoise.mptr={{0, 0}}, \
|
||||
.u.mtortoise.step=0, \
|
||||
.u.mtortoise.power=0})
|
||||
@@ -7824,6 +7836,7 @@ static void lfsr_fs_traverserewind(lfs_t *lfs, lfsr_mtraversal_t *mt) {
|
||||
mt->o.flags &= ~LFS_F_DIRTY;
|
||||
mt->o.state = LFSR_TSTATE_MROOTANCHOR;
|
||||
mt->o.mdir.mid = -1;
|
||||
mt->ot = NULL;
|
||||
mt->u.mtortoise.mptr.blocks[0] = 0;
|
||||
mt->u.mtortoise.mptr.blocks[1] = 0;
|
||||
mt->u.mtortoise.step = 0;
|
||||
@@ -7837,7 +7850,8 @@ static void lfsr_fs_traverseclobber(lfs_t *lfs, lfsr_mtraversal_t *mt) {
|
||||
mt->o.mdir.mid = lfs_min(
|
||||
mt->o.mdir.mid + 1,
|
||||
lfsr_fs_weight(lfs));
|
||||
mt->u.bt = LFSR_BTRAVERSAL(mt->o.mdir.mid);
|
||||
// TODO do something different with this maybe?
|
||||
mt->ot = NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -7955,116 +7969,58 @@ static int lfsr_fs_traverse_(lfs_t *lfs, lfsr_mtraversal_t *mt,
|
||||
// found an mtree?
|
||||
} else if (tag == LFSR_TAG_MTREE) {
|
||||
// fetch the root of the mtree
|
||||
lfsr_btree_t mtree;
|
||||
err = lfsr_data_readbtree(lfs, &data, &mtree);
|
||||
err = lfsr_data_readbtree(lfs, &data, &mt->bshrub.u.btree);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// transition to traversing the mtree
|
||||
mt->u.bt = LFSR_BTRAVERSAL(0);
|
||||
mt->o.mdir.mid = 0;
|
||||
mt->o.state = LFSR_TSTATE_MTREE;
|
||||
|
||||
// go ahead and traverse the root
|
||||
//
|
||||
// this avoids an annoying situation where the mtree is
|
||||
// uninitialized in mountinited, but we really don't want
|
||||
// to store another copy of the mtree somewhere because
|
||||
// we're often traversing in extremely deep call stacks
|
||||
// such as lfs_alloc
|
||||
//
|
||||
// after traversing the mtree root, mountinited should
|
||||
// initialize lfs->mtree and we can switch to that
|
||||
//
|
||||
err = lfsr_btree_traverse(lfs, &mtree, &mt->u.bt,
|
||||
NULL, mtinfo);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_NOENT);
|
||||
return err;
|
||||
}
|
||||
|
||||
LFS_ASSERT(mtinfo->tag == LFSR_TAG_BRANCH);
|
||||
return 0;
|
||||
mt->o.state = LFSR_TSTATE_BTREE;
|
||||
continue;
|
||||
|
||||
} else {
|
||||
LFS_ERROR("Weird mroot entry? 0x%"PRIx32, tag);
|
||||
return LFS_ERR_CORRUPT;
|
||||
}
|
||||
|
||||
// traverse the mtree, including both inner btree nodes and mdirs
|
||||
// iterate over mdirs in the mtree
|
||||
case LFSR_TSTATE_MTREE:;
|
||||
// TODO should we move this into lfsr_mtree_lookup?
|
||||
// end of mtree? guess we're done
|
||||
if (mt->o.mdir.mid >= (lfsr_smid_t)lfsr_fs_weight(lfs)) {
|
||||
mt->o.state = LFSR_TSTATE_DONE;
|
||||
continue;
|
||||
}
|
||||
|
||||
// inlined mroot? transition to mdir traversal
|
||||
if (lfsr_mtree_isnull(&lfs->mtree)) {
|
||||
mt->o.mdir.rbyd = lfs->mroot.rbyd;
|
||||
mt->o.state = LFSR_TSTATE_MDIR;
|
||||
continue;
|
||||
|
||||
// direct mdir? transition to mdir traversal
|
||||
} else if (lfsr_mtree_ismptr(&lfs->mtree)) {
|
||||
err = lfsr_mdir_fetch(lfs, &mt->o.mdir,
|
||||
mt->o.mdir.mid, &lfs->mtree.u.mptr.mptr);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
mt->o.state = LFSR_TSTATE_MDIR;
|
||||
continue;
|
||||
}
|
||||
|
||||
// traverse through the mtree
|
||||
err = lfsr_btree_traverse(lfs, &lfs->mtree.u.btree, &mt->u.bt,
|
||||
NULL, mtinfo);
|
||||
// find the next mdir
|
||||
err = lfsr_mtree_lookup(lfs, &lfs->mtree, mt->o.mdir.mid,
|
||||
&mt->o.mdir);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_NOENT);
|
||||
return err;
|
||||
}
|
||||
|
||||
// inner btree nodes already decoded
|
||||
if (mtinfo->tag == LFSR_TAG_BRANCH) {
|
||||
return 0;
|
||||
|
||||
// fetch mdir if we're on a leaf
|
||||
} else if (mtinfo->tag == LFSR_TAG_MDIR) {
|
||||
lfsr_mptr_t mptr;
|
||||
err = lfsr_data_readmptr(lfs, &mtinfo->u.data, &mptr);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = lfsr_mdir_fetch(lfs, &mt->o.mdir,
|
||||
mt->o.mdir.mid, &mptr);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// transition to mdir traversal next
|
||||
mt->o.state = LFSR_TSTATE_MDIR;
|
||||
// transition to traversing the mdir
|
||||
mt->o.state = LFSR_TSTATE_MDIR;
|
||||
|
||||
// first time we've seen this mdir?
|
||||
if (lfsr_mid_rid(lfs, mt->o.mdir.mid) == 0) {
|
||||
mtinfo->tag = LFSR_TAG_MDIR;
|
||||
mtinfo->u.mdir = mt->o.mdir;
|
||||
return 0;
|
||||
|
||||
} else {
|
||||
LFS_ERROR("Weird mtree entry? 0x%"PRIx32, mtinfo->tag);
|
||||
return LFS_ERR_CORRUPT;
|
||||
}
|
||||
|
||||
continue;
|
||||
|
||||
// scan for blocks/btrees in the current mdir
|
||||
case LFSR_TSTATE_MDIR:;
|
||||
// not traversing all blocks? have we exceeded our mdir's weight?
|
||||
// return to mtree traversal
|
||||
// return to mtree iteration
|
||||
if (lfsr_t_ismtreeonly(mt->o.flags)
|
||||
|| lfsr_mid_rid(lfs, mt->o.mdir.mid)
|
||||
>= (lfsr_srid_t)mt->o.mdir.rbyd.weight) {
|
||||
// resume from our mid
|
||||
mt->o.mdir.mid = lfsr_mid_bid(lfs, mt->o.mdir.mid) + 1;
|
||||
mt->u.bt = LFSR_BTRAVERSAL(mt->o.mdir.mid);
|
||||
mt->o.state = LFSR_TSTATE_MTREE;
|
||||
continue;
|
||||
}
|
||||
@@ -8102,7 +8058,7 @@ static int lfsr_fs_traverse_(lfs_t *lfs, lfsr_mtraversal_t *mt,
|
||||
// no? next we need to check any opened files
|
||||
} else {
|
||||
mt->ot = &lfs->opened;
|
||||
mt->o.state = LFSR_TSTATE_OMDIR;
|
||||
mt->o.state = LFSR_TSTATE_OPENED;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -8113,7 +8069,7 @@ static int lfsr_fs_traverse_(lfs_t *lfs, lfsr_mtraversal_t *mt,
|
||||
continue;
|
||||
|
||||
// scan for blocks/btrees in our opened file list
|
||||
case LFSR_TSTATE_OMDIR:;
|
||||
case LFSR_TSTATE_OPENED:;
|
||||
// reached end of opened files? return to mdir traversal
|
||||
lfsr_omdir_t *o = *mt->ot;
|
||||
if (!o) {
|
||||
@@ -8145,16 +8101,22 @@ static int lfsr_fs_traverse_(lfs_t *lfs, lfsr_mtraversal_t *mt,
|
||||
mt->o.state = LFSR_TSTATE_BTREE;
|
||||
continue;
|
||||
|
||||
// traverse any file btrees, including both inner btree nodes and
|
||||
// block pointers
|
||||
// traverse any btrees we see, this includes the mtree and any file
|
||||
// btrees/bshrubs
|
||||
case LFSR_TSTATE_BTREE:;
|
||||
// traverse through our file
|
||||
err = lfsr_bshrub_traverse(lfs, (const lfsr_file_t*)mt, &mt->u.bt,
|
||||
NULL, mtinfo);
|
||||
if (err) {
|
||||
if (err == LFS_ERR_NOENT) {
|
||||
// end of mtree? start iterating over mdirs
|
||||
if (mt->o.mdir.mid == -1) {
|
||||
mt->o.mdir.mid = 0;
|
||||
mt->o.state = LFSR_TSTATE_MTREE;
|
||||
// end of btree? go to next opened file
|
||||
mt->o.state = LFSR_TSTATE_OMDIR;
|
||||
} else {
|
||||
mt->o.state = LFSR_TSTATE_OPENED;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
return err;
|
||||
@@ -8164,18 +8126,13 @@ static int lfsr_fs_traverse_(lfs_t *lfs, lfsr_mtraversal_t *mt,
|
||||
if (mtinfo->tag == LFSR_TAG_BRANCH) {
|
||||
return 0;
|
||||
|
||||
// found inlined data? ignore this
|
||||
} else if (mtinfo->tag == LFSR_TAG_DATA) {
|
||||
continue;
|
||||
|
||||
// found an indirect block?
|
||||
} else if (mtinfo->tag == LFSR_TAG_BLOCK) {
|
||||
return 0;
|
||||
|
||||
} else {
|
||||
LFS_UNREACHABLE();
|
||||
}
|
||||
|
||||
continue;
|
||||
|
||||
case LFSR_TSTATE_DONE:;
|
||||
return LFS_ERR_NOENT;
|
||||
|
||||
@@ -9398,6 +9355,16 @@ failed:;
|
||||
|
||||
/// High-level filesystem traversal ///
|
||||
|
||||
// TODO keep this?
|
||||
static void lfsr_traversal_clobber(lfs_t *lfs, lfsr_traversal_t *t) {
|
||||
// clobber the low-level traversal
|
||||
lfsr_fs_traverseclobber(lfs, &t->mt);
|
||||
|
||||
// and clear any pending blocks
|
||||
t->blocks[0] = -1;
|
||||
t->blocks[1] = -1;
|
||||
}
|
||||
|
||||
// needed in lfsr_traversal_open
|
||||
static int lfsr_traversal_rewind_(lfs_t *lfs, lfsr_traversal_t *t);
|
||||
|
||||
@@ -9827,6 +9794,11 @@ int lfsr_remove(lfs_t *lfs, const char *path) {
|
||||
} else {
|
||||
((lfsr_dir_t*)o)->pos -= 1;
|
||||
}
|
||||
|
||||
// clobber any problematic traversals
|
||||
} else if (o->type == LFS_TYPE_TRAVERSAL
|
||||
&& o->mdir.mid == mdir.mid) {
|
||||
lfsr_traversal_clobber(lfs, (lfsr_traversal_t*)o);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10007,6 +9979,12 @@ int lfsr_rename(lfs_t *lfs, const char *old_path, const char *new_path) {
|
||||
((lfsr_dir_t*)o)->pos -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// clobber any problematic traversals
|
||||
} else if (o->type == LFS_TYPE_TRAVERSAL
|
||||
&& ((exists && o->mdir.mid == new_mdir.mid)
|
||||
|| o->mdir.mid == lfs->grm.mids[0])) {
|
||||
lfsr_traversal_clobber(lfs, (lfsr_traversal_t*)o);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10659,7 +10637,13 @@ int lfsr_file_close(lfs_t *lfs, lfsr_file_t *file) {
|
||||
// if we're unsync, we need to clobber any traversals that may be
|
||||
// referencing our bshrub/memory, but we don't need to mark as dirty
|
||||
if (lfsr_f_isunsync(file->o.flags)) {
|
||||
lfsr_opened_clobber(lfs, file->o.mdir.mid, false);
|
||||
for (lfsr_omdir_t *o = lfs->opened; o; o = o->next) {
|
||||
if (o->type == LFS_TYPE_TRAVERSAL) {
|
||||
if (((lfsr_traversal_t*)o)->mt.ot == &file->o.next) {
|
||||
lfsr_traversal_clobber(lfs, (lfsr_traversal_t*)o);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove from tracked mdirs
|
||||
@@ -11982,8 +11966,15 @@ lfs_ssize_t lfsr_file_write(lfs_t *lfs, lfsr_file_t *file,
|
||||
|
||||
// checkpoint the allocator
|
||||
lfs_alloc_ckpoint(lfs);
|
||||
// clobber any related traversals
|
||||
lfsr_opened_clobber(lfs, file->o.mdir.mid, true);
|
||||
// clobber any problematic traversals
|
||||
for (lfsr_omdir_t *o = lfs->opened; o; o = o->next) {
|
||||
if (o->type == LFS_TYPE_TRAVERSAL) {
|
||||
o->flags |= LFS_F_DIRTY;
|
||||
if (((lfsr_traversal_t*)o)->mt.ot == &file->o.next) {
|
||||
lfsr_traversal_clobber(lfs, (lfsr_traversal_t*)o);
|
||||
}
|
||||
}
|
||||
}
|
||||
// mark as unsynced in case we fail
|
||||
file->o.flags |= LFS_F_UNSYNC;
|
||||
|
||||
@@ -12142,8 +12133,15 @@ int lfsr_file_flush(lfs_t *lfs, lfsr_file_t *file) {
|
||||
|
||||
// checkpoint the allocator
|
||||
lfs_alloc_ckpoint(lfs);
|
||||
// clobber any related traversals
|
||||
lfsr_opened_clobber(lfs, file->o.mdir.mid, true);
|
||||
// clobber any problematic traversals
|
||||
for (lfsr_omdir_t *o = lfs->opened; o; o = o->next) {
|
||||
if (o->type == LFS_TYPE_TRAVERSAL) {
|
||||
o->flags |= LFS_F_DIRTY;
|
||||
if (((lfsr_traversal_t*)o)->mt.ot == &file->o.next) {
|
||||
lfsr_traversal_clobber(lfs, (lfsr_traversal_t*)o);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// flush our buffer if it contains any unwritten data
|
||||
int err;
|
||||
@@ -12286,13 +12284,13 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) {
|
||||
}
|
||||
}
|
||||
|
||||
// but do update other file handles
|
||||
// update in-device state
|
||||
for (lfsr_omdir_t *o = lfs->opened; o; o = o->next) {
|
||||
lfsr_file_t *file_ = (lfsr_file_t*)o;
|
||||
if (file_->o.type == LFS_TYPE_REG
|
||||
&& file_->o.mdir.mid == file->o.mdir.mid
|
||||
if (o->type == LFS_TYPE_REG
|
||||
&& o->mdir.mid == file->o.mdir.mid
|
||||
// don't double update
|
||||
&& file_ != file) {
|
||||
&& o != &file->o) {
|
||||
lfsr_file_t *file_ = (lfsr_file_t*)o;
|
||||
// notify all files of creation
|
||||
file_->o.flags &= ~LFS_F_ORPHAN;
|
||||
|
||||
@@ -12317,6 +12315,11 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) {
|
||||
file->buffer.size);
|
||||
file_->buffer.size = file->buffer.size;
|
||||
}
|
||||
|
||||
// clobber any problematic traversals
|
||||
} else if (o->type == LFS_TYPE_TRAVERSAL
|
||||
&& o->mdir.mid == file->o.mdir.mid) {
|
||||
lfsr_traversal_clobber(lfs, (lfsr_traversal_t*)o);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12403,8 +12406,15 @@ int lfsr_file_truncate(lfs_t *lfs, lfsr_file_t *file, lfs_off_t size_) {
|
||||
|
||||
// checkpoint the allocator
|
||||
lfs_alloc_ckpoint(lfs);
|
||||
// clobber any related traversals
|
||||
lfsr_opened_clobber(lfs, file->o.mdir.mid, true);
|
||||
// clobber any problematic traversals
|
||||
for (lfsr_omdir_t *o = lfs->opened; o; o = o->next) {
|
||||
if (o->type == LFS_TYPE_TRAVERSAL) {
|
||||
o->flags |= LFS_F_DIRTY;
|
||||
if (((lfsr_traversal_t*)o)->mt.ot == &file->o.next) {
|
||||
lfsr_traversal_clobber(lfs, (lfsr_traversal_t*)o);
|
||||
}
|
||||
}
|
||||
}
|
||||
// mark as unsynced in case we fail
|
||||
file->o.flags |= LFS_F_UNSYNC;
|
||||
|
||||
@@ -12509,8 +12519,15 @@ int lfsr_file_fruncate(lfs_t *lfs, lfsr_file_t *file, lfs_off_t size_) {
|
||||
|
||||
// checkpoint the allocator
|
||||
lfs_alloc_ckpoint(lfs);
|
||||
// clobber any related traversals
|
||||
lfsr_opened_clobber(lfs, file->o.mdir.mid, true);
|
||||
// clobber any problematic traversals
|
||||
for (lfsr_omdir_t *o = lfs->opened; o; o = o->next) {
|
||||
if (o->type == LFS_TYPE_TRAVERSAL) {
|
||||
o->flags |= LFS_F_DIRTY;
|
||||
if (((lfsr_traversal_t*)o)->mt.ot == &file->o.next) {
|
||||
lfsr_traversal_clobber(lfs, (lfsr_traversal_t*)o);
|
||||
}
|
||||
}
|
||||
}
|
||||
// mark as unsynced in case we fail
|
||||
file->o.flags |= LFS_F_UNSYNC;
|
||||
|
||||
|
||||
@@ -610,6 +610,7 @@ typedef struct lfsr_mtraversal {
|
||||
// so only store the relevant bits, is this a hack? yes
|
||||
const struct lfs_file_config *cfg;
|
||||
lfsr_bshrub_t bshrub;
|
||||
lfsr_bshrub_t bshrub_;
|
||||
|
||||
// opened file state, we use an indirect pointer here so we
|
||||
// always point to data associated with the current mid
|
||||
|
||||
+28
-34
@@ -846,7 +846,7 @@ code = '''
|
||||
[cases.test_traversal_ckmdir_dirs]
|
||||
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
||||
code = '''
|
||||
for (lfs_block_t i = 0;; i++) {
|
||||
for (lfs_block_t i = 0;; i += 2) {
|
||||
// a bit hacky, but this catches infinite loops
|
||||
assert(i < 2*BLOCK_COUNT);
|
||||
|
||||
@@ -865,7 +865,6 @@ code = '''
|
||||
lfsr_traversal_t t;
|
||||
lfsr_traversal_open(&lfs, &t, 0) => 0;
|
||||
lfs_block_t k = 0;
|
||||
lfs_block_t c = 0;
|
||||
for (lfs_block_t j = 0;; j++) {
|
||||
assert(j < 2*BLOCK_COUNT);
|
||||
|
||||
@@ -879,7 +878,7 @@ code = '''
|
||||
}
|
||||
|
||||
if (tinfo.btype == LFS_BTYPE_MDIR) {
|
||||
if (k == i) {
|
||||
if (k == i || k == i+1) {
|
||||
// clobber this block
|
||||
printf("clobbering 0x%x\n", tinfo.block);
|
||||
uint8_t clobber_buf[BLOCK_SIZE];
|
||||
@@ -887,15 +886,12 @@ code = '''
|
||||
CFG->erase(CFG, tinfo.block) => 0;
|
||||
CFG->prog(CFG, tinfo.block, 0,
|
||||
clobber_buf, BLOCK_SIZE) => 0;
|
||||
if (c == 2-1) {
|
||||
if (k == i+1) {
|
||||
lfsr_traversal_close(&lfs, &t) => 0;
|
||||
goto clobbered;
|
||||
} else {
|
||||
c += 1;
|
||||
}
|
||||
} else {
|
||||
k += 1;
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -935,7 +931,7 @@ defines.CKMETADATA = [false, true]
|
||||
defines.CKDATA = [false, true]
|
||||
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
||||
code = '''
|
||||
for (lfs_block_t i = 0;; i++) {
|
||||
for (lfs_block_t i = 0;; i += 2) {
|
||||
// a bit hacky, but this catches infinite loops
|
||||
assert(i < 2*BLOCK_COUNT);
|
||||
|
||||
@@ -965,7 +961,6 @@ code = '''
|
||||
lfsr_traversal_t t;
|
||||
lfsr_traversal_open(&lfs, &t, 0) => 0;
|
||||
lfs_block_t k = 0;
|
||||
lfs_block_t c = 0;
|
||||
for (lfs_block_t j = 0;; j++) {
|
||||
assert(j < 2*BLOCK_COUNT);
|
||||
|
||||
@@ -979,7 +974,7 @@ code = '''
|
||||
}
|
||||
|
||||
if (tinfo.btype == LFS_BTYPE_MDIR) {
|
||||
if (k == i) {
|
||||
if (k == i || k == i+1) {
|
||||
// clobber this block
|
||||
printf("clobbering 0x%x\n", tinfo.block);
|
||||
uint8_t clobber_buf[BLOCK_SIZE];
|
||||
@@ -987,15 +982,12 @@ code = '''
|
||||
CFG->erase(CFG, tinfo.block) => 0;
|
||||
CFG->prog(CFG, tinfo.block, 0,
|
||||
clobber_buf, BLOCK_SIZE) => 0;
|
||||
if (c == 2-1) {
|
||||
if (k == i+1) {
|
||||
lfsr_traversal_close(&lfs, &t) => 0;
|
||||
goto clobbered;
|
||||
} else {
|
||||
c += 1;
|
||||
}
|
||||
} else {
|
||||
k += 1;
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1035,7 +1027,7 @@ defines.CKMETADATA = [false, true]
|
||||
defines.CKDATA = [false, true]
|
||||
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
||||
code = '''
|
||||
for (lfs_block_t i = 0;; i++) {
|
||||
for (lfs_block_t i = 0;; i += 2) {
|
||||
// a bit hacky, but this catches infinite loops
|
||||
assert(i < 2*BLOCK_COUNT);
|
||||
|
||||
@@ -1064,7 +1056,6 @@ code = '''
|
||||
lfsr_traversal_t t;
|
||||
lfsr_traversal_open(&lfs, &t, 0) => 0;
|
||||
lfs_block_t k = 0;
|
||||
lfs_block_t c = 0;
|
||||
for (lfs_block_t j = 0;; j++) {
|
||||
assert(j < 2*BLOCK_COUNT);
|
||||
|
||||
@@ -1081,7 +1072,7 @@ code = '''
|
||||
}
|
||||
|
||||
if (tinfo.btype == LFS_BTYPE_MDIR) {
|
||||
if (k == i) {
|
||||
if (k == i || k == i+1) {
|
||||
// clobber this block
|
||||
printf("clobbering 0x%x\n", tinfo.block);
|
||||
uint8_t clobber_buf[BLOCK_SIZE];
|
||||
@@ -1089,15 +1080,12 @@ code = '''
|
||||
CFG->erase(CFG, tinfo.block) => 0;
|
||||
CFG->prog(CFG, tinfo.block, 0,
|
||||
clobber_buf, BLOCK_SIZE) => 0;
|
||||
if (c == 2-1) {
|
||||
if (k == i+1) {
|
||||
lfsr_traversal_close(&lfs, &t) => 0;
|
||||
goto clobbered;
|
||||
} else {
|
||||
c += 1;
|
||||
}
|
||||
} else {
|
||||
k += 1;
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1174,9 +1162,8 @@ code = '''
|
||||
clobber_buf, BLOCK_SIZE) => 0;
|
||||
lfsr_traversal_close(&lfs, &t) => 0;
|
||||
goto clobbered;
|
||||
} else {
|
||||
k += 1;
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1269,9 +1256,8 @@ code = '''
|
||||
clobber_buf, BLOCK_SIZE) => 0;
|
||||
lfsr_traversal_close(&lfs, &t) => 0;
|
||||
goto clobbered;
|
||||
} else {
|
||||
k += 1;
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1366,9 +1352,8 @@ code = '''
|
||||
clobber_buf, BLOCK_SIZE) => 0;
|
||||
lfsr_traversal_close(&lfs, &t) => 0;
|
||||
goto clobbered;
|
||||
} else {
|
||||
k += 1;
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3004,9 +2989,6 @@ code = '''
|
||||
i += 1;
|
||||
}
|
||||
|
||||
// traverse the mtree root
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
||||
// traverse one mdir
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
@@ -3271,7 +3253,7 @@ code = '''
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
|
||||
# TODO
|
||||
#[cases.test_traversal_mutation_mroot_chain_split]
|
||||
#[cases.test_traversal_mutation_mroot_chain_split_l]
|
||||
#[cases.test_traversal_mutation_mroot_chain_split_r]
|
||||
@@ -3279,6 +3261,18 @@ code = '''
|
||||
#[cases.test_traversal_mutation_mtree_split_l]
|
||||
#[cases.test_traversal_mutation_mtree_split_r]
|
||||
|
||||
# TODO also rename/remove -> mv/rm above?
|
||||
#[cases.test_traversal_mutation_rm_bleaf]
|
||||
#[cases.test_traversal_mutation_rm_btree]
|
||||
#[cases.test_traversal_mutation_rm_bshrub]
|
||||
#[cases.test_traversal_mutation_mv_src_bleaf]
|
||||
#[cases.test_traversal_mutation_mv_src_btree]
|
||||
#[cases.test_traversal_mutation_mv_src_bshrub]
|
||||
#[cases.test_traversal_mutation_mv_dst_bleaf]
|
||||
#[cases.test_traversal_mutation_mv_dst_btree]
|
||||
#[cases.test_traversal_mutation_mv_dst_bshrub]
|
||||
|
||||
|
||||
|
||||
|
||||
# many/fuzz tests mixed with traversals
|
||||
|
||||
Reference in New Issue
Block a user