bmap: Added low-level bmap set algorithm and related tests
The neat thing about the on-disk bmap is that it's a range tree. We can leverage order-statistic properties to compactly represent ranges of similar blocks. However, this does make updating the bmap slightly more complicated...
This commit is contained in:
@@ -5588,9 +5588,9 @@ static int lfs3_btree_commit_(lfs3_t *lfs3,
|
||||
|
||||
// are we root?
|
||||
} else if (child.blocks[0] == btree->r.blocks[0]) {
|
||||
// mark btree as unerased in case of failure, our btree rbyd and
|
||||
// mark btree as unfetched in case of failure, our btree rbyd and
|
||||
// root rbyd can diverge if there's a split, but we would have
|
||||
// marked the old root as unerased earlier anyways
|
||||
// marked the old root as unfetched earlier anyways
|
||||
lfs3_btree_claim(btree);
|
||||
|
||||
// need to lookup child's parent
|
||||
@@ -6920,15 +6920,19 @@ static int lfs3_bshrub_commitroot_(lfs3_t *lfs3, lfs3_bshrub_t *bshrub,
|
||||
static int lfs3_bshrub_commit(lfs3_t *lfs3, lfs3_bshrub_t *bshrub,
|
||||
lfs3_bid_t bid, const lfs3_rattr_t *rattrs, lfs3_size_t rattr_count) {
|
||||
#ifndef LFS3_2BONLY
|
||||
// TODO why are we marking bshrubs as unfetched here? we should either
|
||||
// be marking all btrees in lfs3_btree_commit, or move this to
|
||||
// lfs3_file_commit for finer control...
|
||||
//
|
||||
// before we touch anything, we need to mark all other btree references
|
||||
// as unerased
|
||||
// as unfetched
|
||||
if (lfs3_bshrub_isbtree(bshrub)) {
|
||||
for (lfs3_handle_t *h = lfs3->handles; h; h = h->next) {
|
||||
if (lfs3_o_isbshrub(h->flags)
|
||||
&& h != &bshrub->h
|
||||
&& ((lfs3_bshrub_t*)h)->shrub.r.blocks[0]
|
||||
== bshrub->shrub.r.blocks[0]) {
|
||||
// mark as unerased
|
||||
// mark as unfetched
|
||||
lfs3_btree_claim(&((lfs3_bshrub_t*)h)->shrub);
|
||||
}
|
||||
}
|
||||
@@ -9282,7 +9286,7 @@ static int lfs3_mdir_commit(lfs3_t *lfs3, lfs3_mdir_t *mdir,
|
||||
|
||||
// update our mtree
|
||||
} else {
|
||||
// mark as unerased in case of failure
|
||||
// mark as unfetched in case of failure
|
||||
lfs3_btree_claim(&lfs3->mtree);
|
||||
|
||||
err = lfs3_mtree_commit(lfs3, &mtree_,
|
||||
@@ -9321,7 +9325,7 @@ static int lfs3_mdir_commit(lfs3_t *lfs3, lfs3_mdir_t *mdir,
|
||||
// how can we drop if we have no mtree?
|
||||
LFS3_ASSERT(lfs3->mtree.r.weight != 0);
|
||||
|
||||
// mark as unerased in case of failure
|
||||
// mark as unfetched in case of failure
|
||||
lfs3_btree_claim(&lfs3->mtree);
|
||||
|
||||
// update our mtree
|
||||
@@ -9358,7 +9362,7 @@ static int lfs3_mdir_commit(lfs3_t *lfs3, lfs3_mdir_t *mdir,
|
||||
|
||||
// update our mtree
|
||||
} else {
|
||||
// mark as unerased in case of failure
|
||||
// mark as unfetched in case of failure
|
||||
lfs3_btree_claim(&lfs3->mtree);
|
||||
|
||||
err = lfs3_mtree_commit(lfs3, &mtree_,
|
||||
@@ -10598,6 +10602,162 @@ static int lfs3_data_readgbmap(lfs3_t *lfs3, lfs3_data_t *data) {
|
||||
}
|
||||
#endif
|
||||
|
||||
// on-disk block map operations
|
||||
//
|
||||
// note these take any btree, since we sometimes update the gbmap, and
|
||||
// sometimes the gbatc
|
||||
|
||||
#ifdef LFS3_BMAP
|
||||
static int lfs3_bmap_lookupnext(lfs3_t *lfs3, lfs3_btree_t *bmap,
|
||||
lfs3_bid_t bid,
|
||||
lfs3_bid_t *bid_, lfs3_bid_t *weight_) {
|
||||
return lfs3_btree_lookupnext(lfs3, bmap, bid,
|
||||
bid_, weight_, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
// this is the same as lfs3_btree_commit, but we set the inbmap flag
|
||||
// for debugging reasons
|
||||
#ifdef LFS3_BMAP
|
||||
static int lfs3_bmap_commit(lfs3_t *lfs3, lfs3_btree_t *bmap,
|
||||
lfs3_bid_t bid, const lfs3_rattr_t *rattrs, lfs3_size_t rattr_count) {
|
||||
#ifdef LFS3_REVDBG
|
||||
lfs3->flags |= LFS3_i_INBMAP;
|
||||
#endif
|
||||
|
||||
int err = lfs3_btree_commit(lfs3, bmap, bid, rattrs, rattr_count);
|
||||
if (err) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
#ifdef LFS3_REVDBG
|
||||
lfs3->flags &= ~LFS3_i_INBMAP;
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
failed:;
|
||||
#ifdef LFS3_REVDBG
|
||||
lfs3->flags &= ~LFS3_i_INBMAP;
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LFS3_BMAP
|
||||
static int lfs3_bmap_set(lfs3_t *lfs3, lfs3_btree_t *bmap,
|
||||
lfs3_block_t block, lfs3_tag_t tag) {
|
||||
// lookup bmap range
|
||||
lfs3_bid_t bid__;
|
||||
lfs3_bid_t weight__;
|
||||
lfs3_stag_t tag__ = lfs3_bmap_lookupnext(lfs3, bmap, block,
|
||||
&bid__, &weight__);
|
||||
if (tag__ < 0) {
|
||||
LFS3_ASSERT(tag__ != LFS3_ERR_NOENT);
|
||||
return tag__;
|
||||
}
|
||||
|
||||
// wait, already set to expected type? guess we're done
|
||||
if (tag__ == tag) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// temporary copy, we definitely _don't_ want to leave this in a
|
||||
// weird state on error
|
||||
lfs3_btree_t bmap_ = *bmap;
|
||||
// TODO should we mark the gbmaps as unfetched as well?
|
||||
// TODO should we just claim all matching btrees in lfs3_btree_commit?
|
||||
// mark as unfetched in case of error
|
||||
lfs3_btree_claim(bmap);
|
||||
|
||||
// TODO should these use builder pattern?
|
||||
|
||||
// first delete block from range, this may split the range into
|
||||
// multiple neighbors
|
||||
//
|
||||
// note this is never unnecessary work, the resulting neighbors
|
||||
// can't share a type or else we would've already returned
|
||||
int err = lfs3_bmap_commit(lfs3, &bmap_, bid__, LFS3_RATTRS(
|
||||
(bid__-(weight__-1) < block)
|
||||
? LFS3_RATTR(LFS3_TAG_GROW, -((bid__+1) - block))
|
||||
: LFS3_RATTR(LFS3_TAG_RM, -((bid__+1) - block)),
|
||||
(bid__ > block)
|
||||
? LFS3_RATTR(tag__, +(bid__ - block))
|
||||
: LFS3_RATTR_NOOP()));
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// weight of new range
|
||||
lfs3_bid_t weight = 1;
|
||||
|
||||
// can we merge with right neighbor?
|
||||
if (block < lfs3->block_count-1) {
|
||||
// note the use of the old bmap to try to leverage leaf caching
|
||||
tag__ = lfs3_bmap_lookupnext(lfs3, bmap, block+1,
|
||||
&bid__, &weight__);
|
||||
if (tag__ < 0) {
|
||||
LFS3_ASSERT(tag__ != LFS3_ERR_NOENT);
|
||||
return tag__;
|
||||
}
|
||||
|
||||
if (tag__ == tag) {
|
||||
LFS3_ASSERT(weight__ == bid__ - block);
|
||||
// merge
|
||||
weight += weight__;
|
||||
|
||||
// delete to prepare merge
|
||||
//
|
||||
// note the shifted bid because of the previous delete
|
||||
err = lfs3_bmap_commit(lfs3, &bmap_, bid__-1, LFS3_RATTRS(
|
||||
LFS3_RATTR(LFS3_TAG_RM, -weight__)));
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// can we merge with left neighbor?
|
||||
if (block > 0) {
|
||||
// note the use of the old bmap to try to leverage leaf caching
|
||||
tag__ = lfs3_bmap_lookupnext(lfs3, bmap, block-1,
|
||||
&bid__, &weight__);
|
||||
if (tag__ < 0) {
|
||||
LFS3_ASSERT(tag__ != LFS3_ERR_NOENT);
|
||||
return tag__;
|
||||
}
|
||||
|
||||
if (tag__ == tag) {
|
||||
LFS3_ASSERT(bid__ == block-1);
|
||||
// we can merge everything in one commit here
|
||||
err = lfs3_bmap_commit(lfs3, &bmap_, bid__, LFS3_RATTRS(
|
||||
LFS3_RATTR(LFS3_TAG_GROW, +weight)));
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// done!
|
||||
*bmap = bmap_;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// needs a new range
|
||||
err = lfs3_bmap_commit(lfs3, &bmap_, block, LFS3_RATTRS(
|
||||
LFS3_RATTR(tag, +weight)));
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// done!
|
||||
*bmap = bmap_;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// TODO lfs3_bmap_relocate
|
||||
// TODO lfs3_bmap_mdirdiff
|
||||
// TODO lfs3_bmap_btreediff
|
||||
|
||||
|
||||
|
||||
/// Block allocator ///
|
||||
@@ -10878,9 +11038,9 @@ static lfs3_sblock_t lfs3_alloc(lfs3_t *lfs3, uint32_t flags) {
|
||||
while (lfs3->bmap.known > 0
|
||||
&& lfs3->lookahead.ckpoint > 0) {
|
||||
lfs3_bid_t bid;
|
||||
lfs3_stag_t tag = lfs3_btree_lookupnext(lfs3, &lfs3->bmap.gbatc,
|
||||
lfs3_stag_t tag = lfs3_bmap_lookupnext(lfs3, &lfs3->bmap.gbatc,
|
||||
lfs3->bmap.cursor,
|
||||
&bid, NULL, NULL);
|
||||
&bid, NULL);
|
||||
if (tag < 0) {
|
||||
return tag;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# since you can usually ignore allocator issues temporarily by making the test
|
||||
# device really big
|
||||
#
|
||||
after = ['test_mtree', 'test_dirs', 'test_files']
|
||||
after = ['test_mtree', 'test_bmap', 'test_dirs', 'test_files']
|
||||
|
||||
# test that we can alloc
|
||||
[cases.test_alloc_alloc]
|
||||
|
||||
@@ -0,0 +1,381 @@
|
||||
# Test some low-level block-map operations
|
||||
after = ['test_btree', 'test_mtree']
|
||||
ifdef = 'LFS3_BMAP'
|
||||
|
||||
# maximize lookahead buffer, we don't actually gc so we only get one pass
|
||||
# of the disk for these tests
|
||||
defines.LOOKAHEAD_SIZE = '(BLOCK_COUNT+8-1) / 8'
|
||||
|
||||
# test simple set operations
|
||||
[cases.test_bmap_set_split]
|
||||
in = 'lfs3.c'
|
||||
code = '''
|
||||
lfs3_t lfs3;
|
||||
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
||||
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
||||
lfs3_alloc_ckpoint(&lfs3);
|
||||
|
||||
// create an initial bmap
|
||||
lfs3_btree_t bmap;
|
||||
lfs3_btree_init(&bmap);
|
||||
lfs3_bmap_commit(&lfs3, &bmap, 0, LFS3_RATTRS(
|
||||
LFS3_RATTR(LFS3_TAG_BMFREE, +BLOCK_COUNT))) => 0;
|
||||
|
||||
// set some blocks as in-use, avoid any weird merges for now
|
||||
lfs3_bmap_set(&lfs3, &bmap, 6, LFS3_TAG_BMINUSE) => 0;
|
||||
lfs3_bmap_set(&lfs3, &bmap, 8, LFS3_TAG_BMINUSE) => 0;
|
||||
lfs3_bmap_set(&lfs3, &bmap, 10, LFS3_TAG_BMINUSE) => 0;
|
||||
printf("bmap: w%d 0x%x.%x\n",
|
||||
bmap.r.weight,
|
||||
bmap.r.blocks[0],
|
||||
bmap.r.trunk);
|
||||
|
||||
// weight should stay the same
|
||||
assert(bmap.r.weight == BLOCK_COUNT);
|
||||
|
||||
// check ranges
|
||||
lfs3_bid_t bid_;
|
||||
lfs3_bid_t weight_;
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 0,
|
||||
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
||||
assert(bid_ == 5);
|
||||
assert(weight_ == 6);
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 6,
|
||||
&bid_, &weight_) => LFS3_TAG_BMINUSE;
|
||||
assert(bid_ == 6);
|
||||
assert(weight_ == 1);
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 7,
|
||||
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
||||
assert(bid_ == 7);
|
||||
assert(weight_ == 1);
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 8,
|
||||
&bid_, &weight_) => LFS3_TAG_BMINUSE;
|
||||
assert(bid_ == 8);
|
||||
assert(weight_ == 1);
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 9,
|
||||
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
||||
assert(bid_ == 9);
|
||||
assert(weight_ == 1);
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 10,
|
||||
&bid_, &weight_) => LFS3_TAG_BMINUSE;
|
||||
assert(bid_ == 10);
|
||||
assert(weight_ == 1);
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 11,
|
||||
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
||||
assert(bid_ == BLOCK_COUNT-1);
|
||||
assert(weight_ == BLOCK_COUNT-11);
|
||||
|
||||
lfs3_unmount(&lfs3) => 0;
|
||||
'''
|
||||
|
||||
[cases.test_bmap_set_replace]
|
||||
in = 'lfs3.c'
|
||||
code = '''
|
||||
lfs3_t lfs3;
|
||||
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
||||
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
||||
lfs3_alloc_ckpoint(&lfs3);
|
||||
|
||||
// create an initial bmap
|
||||
lfs3_btree_t bmap;
|
||||
lfs3_btree_init(&bmap);
|
||||
lfs3_bmap_commit(&lfs3, &bmap, 0, LFS3_RATTRS(
|
||||
LFS3_RATTR(LFS3_TAG_BMFREE, +BLOCK_COUNT))) => 0;
|
||||
|
||||
// set some blocks as in-use, avoid any weird merges for now
|
||||
lfs3_bmap_set(&lfs3, &bmap, 6, LFS3_TAG_BMINUSE) => 0;
|
||||
lfs3_bmap_set(&lfs3, &bmap, 8, LFS3_TAG_BMINUSE) => 0;
|
||||
lfs3_bmap_set(&lfs3, &bmap, 10, LFS3_TAG_BMINUSE) => 0;
|
||||
// replace those blocks as bad, this tests deleting ranges
|
||||
lfs3_bmap_set(&lfs3, &bmap, 6, LFS3_TAG_BMINFLIGHT) => 0;
|
||||
lfs3_bmap_set(&lfs3, &bmap, 8, LFS3_TAG_BMINFLIGHT) => 0;
|
||||
lfs3_bmap_set(&lfs3, &bmap, 10, LFS3_TAG_BMINFLIGHT) => 0;
|
||||
printf("bmap: w%d 0x%x.%x\n",
|
||||
bmap.r.weight,
|
||||
bmap.r.blocks[0],
|
||||
bmap.r.trunk);
|
||||
|
||||
// weight should stay the same
|
||||
assert(bmap.r.weight == BLOCK_COUNT);
|
||||
|
||||
// check ranges
|
||||
lfs3_bid_t bid_;
|
||||
lfs3_bid_t weight_;
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 0,
|
||||
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
||||
assert(bid_ == 5);
|
||||
assert(weight_ == 6);
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 6,
|
||||
&bid_, &weight_) => LFS3_TAG_BMINFLIGHT;
|
||||
assert(bid_ == 6);
|
||||
assert(weight_ == 1);
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 7,
|
||||
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
||||
assert(bid_ == 7);
|
||||
assert(weight_ == 1);
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 8,
|
||||
&bid_, &weight_) => LFS3_TAG_BMINFLIGHT;
|
||||
assert(bid_ == 8);
|
||||
assert(weight_ == 1);
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 9,
|
||||
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
||||
assert(bid_ == 9);
|
||||
assert(weight_ == 1);
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 10,
|
||||
&bid_, &weight_) => LFS3_TAG_BMINFLIGHT;
|
||||
assert(bid_ == 10);
|
||||
assert(weight_ == 1);
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 11,
|
||||
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
||||
assert(bid_ == BLOCK_COUNT-1);
|
||||
assert(weight_ == BLOCK_COUNT-11);
|
||||
|
||||
lfs3_unmount(&lfs3) => 0;
|
||||
'''
|
||||
|
||||
[cases.test_bmap_set_merge]
|
||||
in = 'lfs3.c'
|
||||
code = '''
|
||||
lfs3_t lfs3;
|
||||
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
||||
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
||||
lfs3_alloc_ckpoint(&lfs3);
|
||||
|
||||
// create an initial bmap
|
||||
lfs3_btree_t bmap;
|
||||
lfs3_btree_init(&bmap);
|
||||
lfs3_bmap_commit(&lfs3, &bmap, 0, LFS3_RATTRS(
|
||||
LFS3_RATTR(LFS3_TAG_BMFREE, +BLOCK_COUNT))) => 0;
|
||||
|
||||
// set some blocks as in-use, avoid any weird merges for now
|
||||
lfs3_bmap_set(&lfs3, &bmap, 6, LFS3_TAG_BMINUSE) => 0;
|
||||
lfs3_bmap_set(&lfs3, &bmap, 8, LFS3_TAG_BMINUSE) => 0;
|
||||
lfs3_bmap_set(&lfs3, &bmap, 10, LFS3_TAG_BMINUSE) => 0;
|
||||
lfs3_bmap_set(&lfs3, &bmap, 12, LFS3_TAG_BMINUSE) => 0;
|
||||
// set neighboring blocks as in-use, triggering merges
|
||||
lfs3_bmap_set(&lfs3, &bmap, 5, LFS3_TAG_BMINUSE) => 0;
|
||||
lfs3_bmap_set(&lfs3, &bmap, 9, LFS3_TAG_BMINUSE) => 0;
|
||||
lfs3_bmap_set(&lfs3, &bmap, 13, LFS3_TAG_BMINUSE) => 0;
|
||||
printf("bmap: w%d 0x%x.%x\n",
|
||||
bmap.r.weight,
|
||||
bmap.r.blocks[0],
|
||||
bmap.r.trunk);
|
||||
|
||||
// weight should stay the same
|
||||
assert(bmap.r.weight == BLOCK_COUNT);
|
||||
|
||||
// check ranges
|
||||
lfs3_bid_t bid_;
|
||||
lfs3_bid_t weight_;
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 0,
|
||||
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
||||
assert(bid_ == 4);
|
||||
assert(weight_ == 5);
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 5,
|
||||
&bid_, &weight_) => LFS3_TAG_BMINUSE;
|
||||
assert(bid_ == 6);
|
||||
assert(weight_ == 2);
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 7,
|
||||
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
||||
assert(bid_ == 7);
|
||||
assert(weight_ == 1);
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 8,
|
||||
&bid_, &weight_) => LFS3_TAG_BMINUSE;
|
||||
assert(bid_ == 10);
|
||||
assert(weight_ == 3);
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 11,
|
||||
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
||||
assert(bid_ == 11);
|
||||
assert(weight_ == 1);
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 12,
|
||||
&bid_, &weight_) => LFS3_TAG_BMINUSE;
|
||||
assert(bid_ == 13);
|
||||
assert(weight_ == 2);
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 14,
|
||||
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
||||
assert(bid_ == BLOCK_COUNT-1);
|
||||
assert(weight_ == BLOCK_COUNT-14);
|
||||
|
||||
lfs3_unmount(&lfs3) => 0;
|
||||
'''
|
||||
|
||||
[cases.test_bmap_set_noop]
|
||||
in = 'lfs3.c'
|
||||
code = '''
|
||||
lfs3_t lfs3;
|
||||
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
||||
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
||||
lfs3_alloc_ckpoint(&lfs3);
|
||||
|
||||
// create an initial bmap
|
||||
lfs3_btree_t bmap;
|
||||
lfs3_btree_init(&bmap);
|
||||
lfs3_bmap_commit(&lfs3, &bmap, 0, LFS3_RATTRS(
|
||||
LFS3_RATTR(LFS3_TAG_BMFREE, +BLOCK_COUNT))) => 0;
|
||||
|
||||
// set some blocks as in-use, avoid any weird merges for now
|
||||
lfs3_bmap_set(&lfs3, &bmap, 6, LFS3_TAG_BMINUSE) => 0;
|
||||
lfs3_bmap_set(&lfs3, &bmap, 8, LFS3_TAG_BMINUSE) => 0;
|
||||
lfs3_bmap_set(&lfs3, &bmap, 10, LFS3_TAG_BMINUSE) => 0;
|
||||
// test a bunch of noops
|
||||
lfs3_bmap_set(&lfs3, &bmap, 6, LFS3_TAG_BMINUSE) => 0;
|
||||
lfs3_bmap_set(&lfs3, &bmap, 7, LFS3_TAG_BMFREE) => 0;
|
||||
lfs3_bmap_set(&lfs3, &bmap, 8, LFS3_TAG_BMINUSE) => 0;
|
||||
lfs3_bmap_set(&lfs3, &bmap, 9, LFS3_TAG_BMFREE) => 0;
|
||||
lfs3_bmap_set(&lfs3, &bmap, 10, LFS3_TAG_BMINUSE) => 0;
|
||||
printf("bmap: w%d 0x%x.%x\n",
|
||||
bmap.r.weight,
|
||||
bmap.r.blocks[0],
|
||||
bmap.r.trunk);
|
||||
|
||||
// weight should stay the same
|
||||
assert(bmap.r.weight == BLOCK_COUNT);
|
||||
|
||||
// check ranges
|
||||
lfs3_bid_t bid_;
|
||||
lfs3_bid_t weight_;
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 0,
|
||||
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
||||
assert(bid_ == 5);
|
||||
assert(weight_ == 6);
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 6,
|
||||
&bid_, &weight_) => LFS3_TAG_BMINUSE;
|
||||
assert(bid_ == 6);
|
||||
assert(weight_ == 1);
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 7,
|
||||
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
||||
assert(bid_ == 7);
|
||||
assert(weight_ == 1);
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 8,
|
||||
&bid_, &weight_) => LFS3_TAG_BMINUSE;
|
||||
assert(bid_ == 8);
|
||||
assert(weight_ == 1);
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 9,
|
||||
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
||||
assert(bid_ == 9);
|
||||
assert(weight_ == 1);
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 10,
|
||||
&bid_, &weight_) => LFS3_TAG_BMINUSE;
|
||||
assert(bid_ == 10);
|
||||
assert(weight_ == 1);
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 11,
|
||||
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
||||
assert(bid_ == BLOCK_COUNT-1);
|
||||
assert(weight_ == BLOCK_COUNT-11);
|
||||
|
||||
lfs3_unmount(&lfs3) => 0;
|
||||
'''
|
||||
|
||||
[cases.test_bmap_set_bounds]
|
||||
in = 'lfs3.c'
|
||||
code = '''
|
||||
lfs3_t lfs3;
|
||||
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
||||
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
||||
lfs3_alloc_ckpoint(&lfs3);
|
||||
|
||||
// create an initial bmap
|
||||
lfs3_btree_t bmap;
|
||||
lfs3_btree_init(&bmap);
|
||||
lfs3_bmap_commit(&lfs3, &bmap, 0, LFS3_RATTRS(
|
||||
LFS3_RATTR(LFS3_TAG_BMFREE, +BLOCK_COUNT))) => 0;
|
||||
|
||||
// test that setting the first and last blocks don't break anything
|
||||
//
|
||||
// though in theory blocks 0x{0,1} are immutable...
|
||||
lfs3_bmap_set(&lfs3, &bmap, 0, LFS3_TAG_BMINUSE) => 0;
|
||||
lfs3_bmap_set(&lfs3, &bmap, BLOCK_COUNT-1, LFS3_TAG_BMINUSE) => 0;
|
||||
printf("bmap: w%d 0x%x.%x\n",
|
||||
bmap.r.weight,
|
||||
bmap.r.blocks[0],
|
||||
bmap.r.trunk);
|
||||
|
||||
// weight should stay the same
|
||||
assert(bmap.r.weight == BLOCK_COUNT);
|
||||
|
||||
// check ranges
|
||||
lfs3_bid_t bid_;
|
||||
lfs3_bid_t weight_;
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 0,
|
||||
&bid_, &weight_) => LFS3_TAG_BMINUSE;
|
||||
assert(bid_ == 0);
|
||||
assert(weight_ == 1);
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, 1,
|
||||
&bid_, &weight_) => LFS3_TAG_BMFREE;
|
||||
assert(bid_ == BLOCK_COUNT-2);
|
||||
assert(weight_ == BLOCK_COUNT-2);
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, BLOCK_COUNT-1,
|
||||
&bid_, &weight_) => LFS3_TAG_BMINUSE;
|
||||
assert(bid_ == BLOCK_COUNT-1);
|
||||
assert(weight_ == 1);
|
||||
|
||||
lfs3_unmount(&lfs3) => 0;
|
||||
'''
|
||||
|
||||
[cases.test_bmap_set_fuzz]
|
||||
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
||||
defines.TYPES = [2, 3, 4]
|
||||
defines.SEED = 'range(20)'
|
||||
fuzz = 'SEED'
|
||||
in = 'lfs3.c'
|
||||
code = '''
|
||||
lfs3_t lfs3;
|
||||
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
||||
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
||||
lfs3_alloc_ckpoint(&lfs3);
|
||||
|
||||
// create an initial bmap
|
||||
lfs3_btree_t bmap;
|
||||
lfs3_btree_init(&bmap);
|
||||
lfs3_bmap_commit(&lfs3, &bmap, 0, LFS3_RATTRS(
|
||||
LFS3_RATTR(LFS3_TAG_BMFREE, +BLOCK_COUNT))) => 0;
|
||||
|
||||
// create a simulation to compare against
|
||||
lfs3_tag_t *sim = malloc(BLOCK_COUNT*sizeof(lfs3_tag_t));
|
||||
for (lfs3_size_t i = 0; i < BLOCK_COUNT; i++) {
|
||||
sim[i] = LFS3_TAG_BMFREE;
|
||||
}
|
||||
|
||||
uint32_t prng = SEED;
|
||||
for (lfs3_size_t i = 0; i < N; i++) {
|
||||
// choose a pseudo-random block
|
||||
lfs3_block_t block = TEST_PRNG(&prng) % BLOCK_COUNT;
|
||||
// and pseudo-random bmap type
|
||||
lfs3_tag_t tag = LFS3_TAG_BMRANGE + (TEST_PRNG(&prng) % TYPES);
|
||||
|
||||
// set in bmap
|
||||
lfs3_bmap_set(&lfs3, &bmap, block, tag) => 0;
|
||||
|
||||
// and set in sim
|
||||
sim[block] = tag;
|
||||
}
|
||||
printf("bmap: w%d 0x%x.%x\n",
|
||||
bmap.r.weight,
|
||||
bmap.r.blocks[0],
|
||||
bmap.r.trunk);
|
||||
|
||||
// check if bmap matches sim
|
||||
lfs3_size_t i = 0;
|
||||
while (i < BLOCK_COUNT) {
|
||||
// we need to convert our sim's raw blocks to compressed ranges,
|
||||
// in theory our bmap is optimal
|
||||
lfs3_tag_t tag = sim[i];
|
||||
lfs3_size_t d = 1;
|
||||
while (i+d < BLOCK_COUNT && sim[i+d] == tag) {
|
||||
d += 1;
|
||||
}
|
||||
|
||||
// does our bmap contain the optimal range?
|
||||
lfs3_bid_t bid_;
|
||||
lfs3_bid_t weight_;
|
||||
lfs3_bmap_lookupnext(&lfs3, &bmap, i,
|
||||
&bid_, &weight_) => tag;
|
||||
assert(bid_ == i+(weight_-1));
|
||||
assert(weight_ == d);
|
||||
|
||||
i += d;
|
||||
}
|
||||
|
||||
free(sim);
|
||||
lfs3_unmount(&lfs3) => 0;
|
||||
'''
|
||||
Reference in New Issue
Block a user