Extended alloc tests to more disk sizes, fixed alloc ckpoint bug

I thought it was a bit funny we test various disk sizes in test_grow,
but no where else! test_grow actually found several bugs when reworking
the lookahead buffer related to small disks, so I figured we should have
some more intentional tests... And behold! A bug!

The issue is that we implicitly call lfs_alloc_ckpoint in
lfsr_mdir_commit. Originally the thinking was that this would be fine
since any in-flight blocks should be committed to a tracked btree/bshrub
first, but lfsr_bshrub_commit goes _through_ lfsr_mdir_commit. Bit of a
problem.

So if we call lfsr_bshrub_commit to add a recently allocated block, it
may end up calling lfsr_mdir_commit, erronously ckpointing the
allocator, and then clobbering the new block if the mdir needs to be
relocated, split, etc.

---

The fix here is to just move lfs_alloc_ckpoint out of lfsr_mdir_commit.
This adds a bit of noise, but it's probably a good thing for alloc
ckpoints to be explicit.

At least lfs_alloc_ckpoint is cheap:

           code          stack
  before: 36412           2680
  after:  36472 (+0.2%)   2680 (+0.0%)
This commit is contained in:
Christopher Haster
2024-07-16 18:41:22 -05:00
parent 4fc03f95a7
commit ac600ae35e
2 changed files with 84 additions and 36 deletions
+66 -27
View File
@@ -5,23 +5,29 @@
#
# It's counter-intuitive, but we run the alloc tests _after_ file/dir tests,
# since you can usually ignore allocator issues temporarily by making the test
# device really big (-DDISK_SIZE=16777216, etc)
# device really big
#
after = ['test_mtree', 'test_dirs', 'test_files']
# TODO test all of these with weird block sizes? would be nice to make this
# easy via the test_runner, either by handling it there or letting a single
# config limit the block count by a couple blocks
# test that we can alloc
[cases.test_alloc_alloc]
in = 'lfs.c'
defines.COUNT = [
'BLOCK_COUNT',
'BLOCK_COUNT-1',
'BLOCK_COUNT/2',
'BLOCK_COUNT/4',
'5',
'2',
]
defines.ERASE = [false, true]
in = 'lfs.c'
code = '''
// test various block counts
struct lfs_config cfg = *CFG;
cfg.block_count = COUNT;
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
lfsr_format(&lfs, &cfg) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, &cfg) => 0;
// start allocating
lfs_alloc_ckpoint(&lfs);
@@ -36,25 +42,36 @@ code = '''
alloced += 1;
// our allocator should stop at some point...
assert(alloced < 2*BLOCK_COUNT);
assert(alloced < 2*COUNT);
}
// excluding our mroot, we should have allocated exactly
// block_count-2 blocks
printf("alloced %d/%d blocks\n", alloced, (lfs_block_t)BLOCK_COUNT);
assert(alloced == BLOCK_COUNT-2);
printf("alloced %d/%d blocks\n", alloced, (lfs_block_t)COUNT);
assert(alloced == COUNT-2);
lfsr_unmount(&lfs) => 0;
'''
# test that we can realloc after an ack
[cases.test_alloc_reuse]
in = 'lfs.c'
defines.COUNT = [
'BLOCK_COUNT',
'BLOCK_COUNT-1',
'BLOCK_COUNT/2',
'BLOCK_COUNT/4',
'5',
'2',
]
defines.ERASE = [false, true]
in = 'lfs.c'
code = '''
// test various block counts
struct lfs_config cfg = *CFG;
cfg.block_count = COUNT;
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
lfsr_format(&lfs, &cfg) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, &cfg) => 0;
// start allocating
lfs_alloc_ckpoint(&lfs);
@@ -69,13 +86,13 @@ code = '''
alloced += 1;
// our allocator should stop at some point...
assert(alloced < 2*BLOCK_COUNT);
assert(alloced < 2*COUNT);
}
// excluding our mroot, we should have allocated exactly
// block_count-2 blocks
printf("alloced %d/%d blocks\n", alloced, (lfs_block_t)BLOCK_COUNT);
assert(alloced == BLOCK_COUNT-2);
printf("alloced %d/%d blocks\n", alloced, (lfs_block_t)COUNT);
assert(alloced == COUNT-2);
// ack again, effectively releasing all the previously alloced blocks
lfs_alloc_ckpoint(&lfs);
@@ -90,13 +107,13 @@ code = '''
alloced += 1;
// our allocator should stop at some point...
assert(alloced < 2*BLOCK_COUNT);
assert(alloced < 2*COUNT);
}
// excluding our mroot, we should have allocated exactly
// block_count-2 blocks
printf("alloced %d/%d blocks\n", alloced, (lfs_block_t)BLOCK_COUNT);
assert(alloced == BLOCK_COUNT-2);
printf("alloced %d/%d blocks\n", alloced, (lfs_block_t)COUNT);
assert(alloced == COUNT-2);
lfsr_unmount(&lfs) => 0;
'''
@@ -614,10 +631,21 @@ code = '''
# nospc tests mostly test that things still work when block allocation
# wraparound occurs
[cases.test_alloc_nospc_dirs]
defines.COUNT = [
'BLOCK_COUNT',
'BLOCK_COUNT-1',
'BLOCK_COUNT/2',
'BLOCK_COUNT/4',
'5',
'2',
]
code = '''
// test various block counts
struct lfs_config cfg = *CFG;
cfg.block_count = COUNT;
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
lfsr_format(&lfs, &cfg) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, &cfg) => 0;
// create directories until we run out of space
lfs_size_t n = 0;
@@ -635,7 +663,7 @@ code = '''
// remount?
if (remount) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, &cfg) => 0;
}
// check that our mkdir worked until we ran out of space
@@ -676,6 +704,14 @@ code = '''
'''
[cases.test_alloc_nospc_files]
defines.COUNT = [
'BLOCK_COUNT',
'BLOCK_COUNT-1',
'BLOCK_COUNT/2',
'BLOCK_COUNT/4',
'5',
'2',
]
defines.SIZE = [
'0',
'FILE_BUFFER_SIZE/2',
@@ -686,9 +722,12 @@ defines.SIZE = [
'8*BLOCK_SIZE',
]
code = '''
// test various block counts
struct lfs_config cfg = *CFG;
cfg.block_count = COUNT;
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
lfsr_format(&lfs, &cfg) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, &cfg) => 0;
// create files until we run out of space
uint32_t prng = 42;
@@ -728,7 +767,7 @@ code = '''
// remount?
if (remount) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, &cfg) => 0;
}
// check that our file writes worked until we ran out of space