e622656538
New bmap range tags:
LFS3_TAG_BMRANGE 0x033u v--- --11 --11 uuuu
LFS3_TAG_BMFREE 0x0330 v--- --11 --11 ----
LFS3_TAG_BMINUSE 0x0331 v--- --11 --11 ---1
LFS3_TAG_BMERASED 0x0332 v--- --11 --11 --1-
LFS3_TAG_BMBAD 0x0333 v--- --11 --11 --11
Note 0x334-0x33f are still reserved for future bmap tags, but the new
encoding fits in the surprisingly common 2-bit subfield that may
deduplicate some decoding code.
Fitting in 2-bits is the main reason for this, now that in-flight ranges
look like they won't be worth exploring further. Worst case we can
always add more bm tags in the future. And it may even make sense to use
an entire bit for in-flight tags, since in theory the concept can apply
to more than just in-use blocks.
---
Another benefit of this encoding: In-use vs free is a bit check, and I
like the implication that an in-use + erased block can only be a bad
block.
No code changes:
code stack ctx
before: 37172 2352 684
after: 37172 (+0.0%) 2352 (+0.0%) 684 (+0.0%)
code stack ctx
bmap before: 38844 2456 800
bmap after: 38844 (+0.0%) 2456 (+0.0%) 800 (+0.0%)
378 lines
12 KiB
TOML
378 lines
12 KiB
TOML
# Test some low-level block-map operations
|
|
after = ['test_btree', 'test_mtree']
|
|
ifdef = 'LFS3_BMAP'
|
|
|
|
# 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_BMBAD) => 0;
|
|
lfs3_bmap_set(&lfs3, &bmap, 8, LFS3_TAG_BMBAD) => 0;
|
|
lfs3_bmap_set(&lfs3, &bmap, 10, LFS3_TAG_BMBAD) => 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_BMBAD;
|
|
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_BMBAD;
|
|
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_BMBAD;
|
|
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;
|
|
'''
|