Added some more mtree tests, fixed mroot extension bug
- Finally figured out how to test multiple mroot extensions without an allocator, though hopefully forcing PROG_SIZE doesn't break test framework things at some point... - Added tests that magic string is always in the same place. This isn't strictly required for littlefs to work, but is a nice feature to have. Of course, the new tests found a bug, but it was in a surprisingly place. Accidentally allowed the revision count to be uninitialized when compacting the mroot. At least there's a test that covers this now.
This commit is contained in:
@@ -4846,9 +4846,9 @@ static int lfsr_mdir_compact_(lfs_t *lfs, lfsr_mdir_t *mdir,
|
|||||||
// - mid = manchor => never alloc (mroot anchor)
|
// - mid = manchor => never alloc (mroot anchor)
|
||||||
// - mid = wl => only alloc if mdir is tired (wear-leveling)
|
// - mid = wl => only alloc if mdir is tired (wear-leveling)
|
||||||
// - otherwise => always alloc, use this mid (new mdir)
|
// - otherwise => always alloc, use this mid (new mdir)
|
||||||
uint32_t rev;
|
|
||||||
if (mid != LFSR_MID_MANCHOR) {
|
|
||||||
// first thing we need to do is read our current revision count
|
// first thing we need to do is read our current revision count
|
||||||
|
uint32_t rev;
|
||||||
int err = lfsr_bd_read(lfs, source->rbyd.block, 0, sizeof(uint32_t),
|
int err = lfsr_bd_read(lfs, source->rbyd.block, 0, sizeof(uint32_t),
|
||||||
&rev, sizeof(uint32_t));
|
&rev, sizeof(uint32_t));
|
||||||
if (err && err != LFS_ERR_CORRUPT) {
|
if (err && err != LFS_ERR_CORRUPT) {
|
||||||
@@ -4858,10 +4858,10 @@ static int lfsr_mdir_compact_(lfs_t *lfs, lfsr_mdir_t *mdir,
|
|||||||
rev = (err != LFS_ERR_CORRUPT ? lfs_fromle32_(&rev) : 0);
|
rev = (err != LFS_ERR_CORRUPT ? lfs_fromle32_(&rev) : 0);
|
||||||
|
|
||||||
// decide if we need to relocate
|
// decide if we need to relocate
|
||||||
if (mid != LFSR_MID_WL || (
|
if (mid != LFSR_MID_MANCHOR && (mid != LFSR_MID_WL || (
|
||||||
lfs->cfg->block_cycles > 0
|
lfs->cfg->block_cycles > 0
|
||||||
// TODO rev things
|
// TODO rev things
|
||||||
&& (rev + 1) % lfs->cfg->block_cycles == 0)) {
|
&& (rev + 1) % lfs->cfg->block_cycles == 0))) {
|
||||||
// allocate a new mdir for relocation
|
// allocate a new mdir for relocation
|
||||||
err = lfsr_mdir_alloc(lfs, mdir,
|
err = lfsr_mdir_alloc(lfs, mdir,
|
||||||
(mid != LFSR_MID_WL ? mid : mdir->mid));
|
(mid != LFSR_MID_WL ? mid : mdir->mid));
|
||||||
@@ -4881,7 +4881,6 @@ static int lfsr_mdir_compact_(lfs_t *lfs, lfsr_mdir_t *mdir,
|
|||||||
// note we allow corrupt errors here, as long as they are consistent
|
// note we allow corrupt errors here, as long as they are consistent
|
||||||
rev = (err != LFS_ERR_CORRUPT ? lfs_fromle32_(&rev) : 0);
|
rev = (err != LFS_ERR_CORRUPT ? lfs_fromle32_(&rev) : 0);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// swap our rbyds
|
// swap our rbyds
|
||||||
lfs_swap32(&mdir->rbyd.block, &mdir->other_block);
|
lfs_swap32(&mdir->rbyd.block, &mdir->other_block);
|
||||||
@@ -4892,7 +4891,7 @@ static int lfsr_mdir_compact_(lfs_t *lfs, lfsr_mdir_t *mdir,
|
|||||||
mdir->rbyd.crc = 0;
|
mdir->rbyd.crc = 0;
|
||||||
|
|
||||||
// erase, preparing for compact
|
// erase, preparing for compact
|
||||||
int err = lfsr_bd_erase(lfs, mdir->rbyd.block);
|
err = lfsr_bd_erase(lfs, mdir->rbyd.block);
|
||||||
if (err) {
|
if (err) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1787,6 +1787,64 @@ code = '''
|
|||||||
lfsr_unmount(&lfs) => 0;
|
lfsr_unmount(&lfs) => 0;
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
[cases.test_mtree_extend_twice]
|
||||||
|
# this should be set so only one entry can fit in a metadata block
|
||||||
|
defines.SIZE = 'BLOCK_SIZE / 4'
|
||||||
|
# make it so blocks relocate every two compacts
|
||||||
|
defines.BLOCK_CYCLES = 2
|
||||||
|
# force our block to compact by setting prog_size=block_size, we don't have
|
||||||
|
# any way to indirectly force the intermediary mroots to compact otherwise
|
||||||
|
defines.PROG_SIZE = 'BLOCK_SIZE'
|
||||||
|
in = 'lfs.c'
|
||||||
|
code = '''
|
||||||
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
||||||
|
lfs_t lfs;
|
||||||
|
lfsr_format(&lfs, cfg) => 0;
|
||||||
|
lfsr_mount(&lfs, cfg) => 0;
|
||||||
|
|
||||||
|
// prepare mroot with an attr
|
||||||
|
uint8_t buffer[SIZE];
|
||||||
|
memset(buffer, alphas[0 % 26], SIZE);
|
||||||
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
||||||
|
LFSR_ATTR(-1, UATTR(1), 0, buffer, SIZE))) => 0;
|
||||||
|
|
||||||
|
// force mroot to compact 2x2 times, this should extend the mroot twice
|
||||||
|
lfsr_mdir_t old_mroot = lfs.mroot;
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
lfs.mroot.rbyd.off = BLOCK_SIZE;
|
||||||
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, NULL, 0) => 0;
|
||||||
|
}
|
||||||
|
lfs.mroot.rbyd.off = BLOCK_SIZE;
|
||||||
|
memset(buffer, alphas[1 % 26], SIZE);
|
||||||
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
||||||
|
LFSR_ATTR(-1, UATTR(1), 0, buffer, SIZE))) => 0;
|
||||||
|
|
||||||
|
// assert we relocated
|
||||||
|
assert(!lfsr_mdir_eq(&old_mroot, &lfs.mroot));
|
||||||
|
|
||||||
|
// assert that our attr is still in the mroot
|
||||||
|
lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1),
|
||||||
|
buffer, SIZE) => SIZE;
|
||||||
|
assert(memcmp(buffer, &alphas[1 % 26], 1) == 0);
|
||||||
|
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
|
||||||
|
|
||||||
|
// check things stay sane after remount
|
||||||
|
lfsr_mount(&lfs, cfg) => 0;
|
||||||
|
|
||||||
|
// assert we relocated
|
||||||
|
assert(!lfsr_mdir_eq(&old_mroot, &lfs.mroot));
|
||||||
|
|
||||||
|
// assert that our attr is still in the mroot
|
||||||
|
lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1),
|
||||||
|
buffer, SIZE) => SIZE;
|
||||||
|
assert(memcmp(buffer, &alphas[1 % 26], 1) == 0);
|
||||||
|
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
'''
|
||||||
|
|
||||||
[cases.test_mtree_relocate_mroot]
|
[cases.test_mtree_relocate_mroot]
|
||||||
# this should be set so only one entry can fit in a metadata block
|
# this should be set so only one entry can fit in a metadata block
|
||||||
defines.SIZE = 'BLOCK_SIZE / 4'
|
defines.SIZE = 'BLOCK_SIZE / 4'
|
||||||
@@ -3802,3 +3860,133 @@ code = '''
|
|||||||
lfsr_unmount(&lfs) => 0;
|
lfsr_unmount(&lfs) => 0;
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
## Magic consistency ##
|
||||||
|
|
||||||
|
# make sure our magic string ("littlefs") shows up in the same place (off=8)
|
||||||
|
[cases.test_mtree_magic]
|
||||||
|
# this should be set so only one entry can fit in a metadata block
|
||||||
|
defines.SIZE = 'BLOCK_SIZE / 4'
|
||||||
|
code = '''
|
||||||
|
lfs_t lfs;
|
||||||
|
lfsr_format(&lfs, cfg) => 0;
|
||||||
|
|
||||||
|
// check our magic string
|
||||||
|
//
|
||||||
|
// note if we lose power we may not have the magic string in both blocks!
|
||||||
|
// but we don't lose power in this test so we can assert the magic string
|
||||||
|
// is present in both
|
||||||
|
uint8_t magic[lfs_max(16, READ_SIZE)];
|
||||||
|
cfg->read(cfg, 0, 0, magic, lfs_max(16, READ_SIZE)) => 0;
|
||||||
|
assert(memcmp(&magic[8], "littlefs", 8) == 0);
|
||||||
|
cfg->read(cfg, 1, 0, magic, lfs_max(16, READ_SIZE)) => 0;
|
||||||
|
assert(memcmp(&magic[8], "littlefs", 8) == 0);
|
||||||
|
'''
|
||||||
|
|
||||||
|
[cases.test_mtree_magic_extend]
|
||||||
|
# this should be set so only one entry can fit in a metadata block
|
||||||
|
defines.SIZE = 'BLOCK_SIZE / 4'
|
||||||
|
# make it so blocks relocate every two compacts
|
||||||
|
defines.BLOCK_CYCLES = 2
|
||||||
|
in = 'lfs.c'
|
||||||
|
code = '''
|
||||||
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
||||||
|
lfs_t lfs;
|
||||||
|
lfsr_format(&lfs, cfg) => 0;
|
||||||
|
lfsr_mount(&lfs, cfg) => 0;
|
||||||
|
|
||||||
|
// prepare mroot with an attr
|
||||||
|
uint8_t buffer[SIZE];
|
||||||
|
memset(buffer, alphas[0 % 26], SIZE);
|
||||||
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
||||||
|
LFSR_ATTR(-1, UATTR(1), 0, buffer, SIZE))) => 0;
|
||||||
|
|
||||||
|
// force mroot to compact twice, this should extend the mroot
|
||||||
|
lfsr_mdir_t old_mroot = lfs.mroot;
|
||||||
|
|
||||||
|
lfs.mroot.rbyd.off = BLOCK_SIZE;
|
||||||
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, NULL, 0) => 0;
|
||||||
|
lfs.mroot.rbyd.off = BLOCK_SIZE;
|
||||||
|
memset(buffer, alphas[1 % 26], SIZE);
|
||||||
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
||||||
|
LFSR_ATTR(-1, UATTR(1), 0, buffer, SIZE))) => 0;
|
||||||
|
|
||||||
|
// assert we relocated
|
||||||
|
assert(!lfsr_mdir_eq(&old_mroot, &lfs.mroot));
|
||||||
|
|
||||||
|
// assert that our attr is still in the mroot
|
||||||
|
lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1),
|
||||||
|
buffer, SIZE) => SIZE;
|
||||||
|
assert(memcmp(buffer, &alphas[1 % 26], 1) == 0);
|
||||||
|
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
|
||||||
|
|
||||||
|
// check our magic string
|
||||||
|
//
|
||||||
|
// note if we lose power we may not have the magic string in both blocks!
|
||||||
|
// but we don't lose power in this test so we can assert the magic string
|
||||||
|
// is present in both
|
||||||
|
uint8_t magic[lfs_max(16, READ_SIZE)];
|
||||||
|
cfg->read(cfg, 0, 0, magic, lfs_max(16, READ_SIZE)) => 0;
|
||||||
|
assert(memcmp(&magic[8], "littlefs", 8) == 0);
|
||||||
|
cfg->read(cfg, 1, 0, magic, lfs_max(16, READ_SIZE)) => 0;
|
||||||
|
assert(memcmp(&magic[8], "littlefs", 8) == 0);
|
||||||
|
'''
|
||||||
|
|
||||||
|
[cases.test_mtree_magic_extend_twice]
|
||||||
|
# this should be set so only one entry can fit in a metadata block
|
||||||
|
defines.SIZE = 'BLOCK_SIZE / 4'
|
||||||
|
# make it so blocks relocate every two compacts
|
||||||
|
defines.BLOCK_CYCLES = 2
|
||||||
|
# force our block to compact by setting prog_size=block_size, we don't have
|
||||||
|
# any way to indirectly force the intermediary mroots to compact otherwise
|
||||||
|
defines.PROG_SIZE = 'BLOCK_SIZE'
|
||||||
|
in = 'lfs.c'
|
||||||
|
code = '''
|
||||||
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
||||||
|
lfs_t lfs;
|
||||||
|
lfsr_format(&lfs, cfg) => 0;
|
||||||
|
lfsr_mount(&lfs, cfg) => 0;
|
||||||
|
|
||||||
|
// prepare mroot with an attr
|
||||||
|
uint8_t buffer[SIZE];
|
||||||
|
memset(buffer, alphas[0 % 26], SIZE);
|
||||||
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
||||||
|
LFSR_ATTR(-1, UATTR(1), 0, buffer, SIZE))) => 0;
|
||||||
|
|
||||||
|
// force mroot to compact 2x2 times, this should extend the mroot twice
|
||||||
|
lfsr_mdir_t old_mroot = lfs.mroot;
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
lfs.mroot.rbyd.off = BLOCK_SIZE;
|
||||||
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, NULL, 0) => 0;
|
||||||
|
}
|
||||||
|
lfs.mroot.rbyd.off = BLOCK_SIZE;
|
||||||
|
memset(buffer, alphas[1 % 26], SIZE);
|
||||||
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
||||||
|
LFSR_ATTR(-1, UATTR(1), 0, buffer, SIZE))) => 0;
|
||||||
|
|
||||||
|
// assert we relocated
|
||||||
|
assert(!lfsr_mdir_eq(&old_mroot, &lfs.mroot));
|
||||||
|
|
||||||
|
// assert that our attr is still in the mroot
|
||||||
|
lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1),
|
||||||
|
buffer, SIZE) => SIZE;
|
||||||
|
assert(memcmp(buffer, &alphas[1 % 26], 1) == 0);
|
||||||
|
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
|
||||||
|
|
||||||
|
// check our magic string
|
||||||
|
//
|
||||||
|
// note if we lose power we may not have the magic string in both blocks!
|
||||||
|
// but we don't lose power in this test so we can assert the magic string
|
||||||
|
// is present in both
|
||||||
|
uint8_t magic[lfs_max(16, READ_SIZE)];
|
||||||
|
cfg->read(cfg, 0, 0, magic, lfs_max(16, READ_SIZE)) => 0;
|
||||||
|
assert(memcmp(&magic[8], "littlefs", 8) == 0);
|
||||||
|
cfg->read(cfg, 1, 0, magic, lfs_max(16, READ_SIZE)) => 0;
|
||||||
|
assert(memcmp(&magic[8], "littlefs", 8) == 0);
|
||||||
|
'''
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user