t: Implemented gc_compact_thresh over bshrub nodes
These aren't really different than btree nodes, except bshrubs need to
be enrolled in our opened list for commits to work.
Fortunately this is already true for explicit traversals, which are
currently the only traversals where we need to simultaneously mutate the
filesystem. This mainly just required adding additional checks for
LFS_TYPE_TRAVERSAL bshrubs, tests, and making sure traversal.bshrub is
never in an invalid state.
This continues to add code/stack cost for what is ultimately a
relatively niche feature:
code stack
before: 35268 2776
after: 35448 (+0.5%) 2800 (+0.9%)
Maybe btree/bshrub compactions should be disabled by default?
This commit is contained in:
+486
-7
@@ -6365,11 +6365,491 @@ code = '''
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
# TODO
|
||||
# [cases.test_traversal_compact_bshrub]
|
||||
# [cases.test_traversal_compact_bshrub_open]
|
||||
# [cases.test_traversal_compact_bshrub_orphan]
|
||||
# [cases.test_traversal_compact_bshrub_desync]
|
||||
[cases.test_traversal_compact_bshrub]
|
||||
defines.CKMETA = [false, true]
|
||||
defines.CK = [false, true]
|
||||
defines.LOOKAHEAD = [false, true]
|
||||
# this configuration should create a 2-layer bshrub, which may be
|
||||
# a bit delicate
|
||||
defines.INLINE_SIZE = 'BLOCK_SIZE/4'
|
||||
defines.CRYSTAL_THRESH = -1
|
||||
defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8'
|
||||
defines.SIZE = 'BLOCK_SIZE'
|
||||
# set compact thresh to minimum
|
||||
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
||||
in = 'lfs.c'
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfsr_format(&lfs, CFG) => 0;
|
||||
lfsr_mount(&lfs, CFG) => 0;
|
||||
|
||||
uint32_t prng = 42;
|
||||
|
||||
// create a file
|
||||
lfsr_file_t file;
|
||||
lfsr_file_open(&lfs, &file, "jellyfish",
|
||||
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
||||
|
||||
uint8_t wbuf[SIZE];
|
||||
for (lfs_size_t j = 0; j < SIZE; j++) {
|
||||
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
||||
|
||||
// rewrite part of our file until bshrub is >gc_compact_thresh full
|
||||
while (true) {
|
||||
// we need internals to check this
|
||||
lfsr_mdir_t mdir;
|
||||
// ckmeta needed for eoff
|
||||
lfsr_mtraversal_t mt = LFSR_MTRAVERSAL(LFS_T_CKMETA);
|
||||
lfsr_mtinfo_t mtinfo;
|
||||
lfsr_mtree_traverse(&lfs, &mdir, &mt, &mtinfo) => 0;
|
||||
assert(mtinfo.tag == LFSR_TAG_MDIR);
|
||||
lfsr_mtree_traverse(&lfs, &mdir, &mt, &mtinfo) => 0;
|
||||
assert(mtinfo.tag == LFSR_TAG_BRANCH);
|
||||
if (lfsr_rbyd_eoff(&mtinfo.u.rbyd) > GC_COMPACT_THRESH) {
|
||||
break;
|
||||
}
|
||||
|
||||
lfsr_file_rewind(&lfs, &file) => 0;
|
||||
for (lfs_size_t j = 0; j < FRAGMENT_SIZE; j++) {
|
||||
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &file, wbuf, FRAGMENT_SIZE) => FRAGMENT_SIZE;
|
||||
}
|
||||
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
|
||||
// try traversing and compacting
|
||||
lfsr_traversal_t t;
|
||||
lfsr_traversal_open(&lfs, &t,
|
||||
LFS_T_COMPACT
|
||||
| ((CKMETA) ? LFS_T_CKMETA : 0)
|
||||
| ((CK) ? LFS_T_CK : 0)
|
||||
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
||||
// traverse mroot
|
||||
struct lfs_tinfo tinfo;
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
assert(tinfo.block == 0 || tinfo.block == 1);
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
assert(tinfo.block == 0 || tinfo.block == 1);
|
||||
// compacting our bshrub nodes may cause them to split, so we may
|
||||
// need to traverse more nodes than we started with
|
||||
for (lfs_size_t i = 0; i < 5; i++) {
|
||||
// traverse bshrub
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
||||
}
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
||||
lfsr_traversal_close(&lfs, &t) => 0;
|
||||
|
||||
// bshrub should have been compacted
|
||||
lfsr_mdir_t mdir;
|
||||
lfsr_mtraversal_t mt = LFSR_MTRAVERSAL(LFS_T_CKMETA);
|
||||
lfsr_mtinfo_t mtinfo;
|
||||
lfsr_mtree_traverse(&lfs, &mdir, &mt, &mtinfo) => 0;
|
||||
assert(mtinfo.tag == LFSR_TAG_MDIR);
|
||||
while (true) {
|
||||
int err = lfsr_mtree_traverse(&lfs, &mdir, &mt, &mtinfo);
|
||||
assert(!err || err == LFS_ERR_NOENT);
|
||||
if (err == LFS_ERR_NOENT) {
|
||||
break;
|
||||
}
|
||||
assert(mtinfo.tag == LFSR_TAG_BRANCH);
|
||||
assert(lfsr_rbyd_eoff(&mtinfo.u.rbyd) <= GC_COMPACT_THRESH);
|
||||
}
|
||||
|
||||
// check we can still read the file
|
||||
for (int remount = 0; remount < 2; remount++) {
|
||||
// remount?
|
||||
if (remount) {
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
lfsr_mount(&lfs, CFG) => 0;
|
||||
}
|
||||
|
||||
lfsr_file_open(&lfs, &file, "jellyfish", LFS_O_RDONLY) => 0;
|
||||
lfsr_file_rewind(&lfs, &file) => 0;
|
||||
uint8_t rbuf[SIZE];
|
||||
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
||||
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
}
|
||||
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
[cases.test_traversal_compact_bshrub_open]
|
||||
defines.CKMETA = [false, true]
|
||||
defines.CK = [false, true]
|
||||
defines.LOOKAHEAD = [false, true]
|
||||
# this configuration should create a 2-layer bshrub, which may be
|
||||
# a bit delicate
|
||||
defines.INLINE_SIZE = 'BLOCK_SIZE/4'
|
||||
defines.CRYSTAL_THRESH = -1
|
||||
defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8'
|
||||
defines.SIZE = 'BLOCK_SIZE'
|
||||
# set compact thresh to minimum
|
||||
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
||||
in = 'lfs.c'
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfsr_format(&lfs, CFG) => 0;
|
||||
lfsr_mount(&lfs, CFG) => 0;
|
||||
|
||||
uint32_t prng = 42;
|
||||
|
||||
// create a file
|
||||
lfsr_file_t file;
|
||||
lfsr_file_open(&lfs, &file, "jellyfish",
|
||||
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
||||
|
||||
uint8_t wbuf[SIZE];
|
||||
for (lfs_size_t j = 0; j < SIZE; j++) {
|
||||
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
||||
|
||||
// rewrite part of our file until bshrub is >gc_compact_thresh full
|
||||
while (true) {
|
||||
// we need internals to check this
|
||||
lfsr_mdir_t mdir;
|
||||
// ckmeta needed for eoff
|
||||
lfsr_mtraversal_t mt = LFSR_MTRAVERSAL(LFS_T_CKMETA);
|
||||
lfsr_mtinfo_t mtinfo;
|
||||
lfsr_mtree_traverse(&lfs, &mdir, &mt, &mtinfo) => 0;
|
||||
assert(mtinfo.tag == LFSR_TAG_MDIR);
|
||||
lfsr_mtree_traverse(&lfs, &mdir, &mt, &mtinfo) => 0;
|
||||
assert(mtinfo.tag == LFSR_TAG_BRANCH);
|
||||
if (lfsr_rbyd_eoff(&mtinfo.u.rbyd) > GC_COMPACT_THRESH) {
|
||||
break;
|
||||
}
|
||||
|
||||
lfsr_file_rewind(&lfs, &file) => 0;
|
||||
for (lfs_size_t j = 0; j < FRAGMENT_SIZE; j++) {
|
||||
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &file, wbuf, FRAGMENT_SIZE) => FRAGMENT_SIZE;
|
||||
}
|
||||
|
||||
lfsr_file_sync(&lfs, &file) => 0;
|
||||
|
||||
// try traversing and compacting
|
||||
lfsr_traversal_t t;
|
||||
lfsr_traversal_open(&lfs, &t,
|
||||
LFS_T_COMPACT
|
||||
| ((CKMETA) ? LFS_T_CKMETA : 0)
|
||||
| ((CK) ? LFS_T_CK : 0)
|
||||
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
||||
// traverse mroot
|
||||
struct lfs_tinfo tinfo;
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
assert(tinfo.block == 0 || tinfo.block == 1);
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
assert(tinfo.block == 0 || tinfo.block == 1);
|
||||
// compacting our bshrub nodes may cause them to split, so we may
|
||||
// need to traverse more nodes than we started with
|
||||
for (lfs_size_t i = 0; i < 5; i++) {
|
||||
// traverse bshrub
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
||||
}
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
||||
lfsr_traversal_close(&lfs, &t) => 0;
|
||||
|
||||
// bshrub should have been compacted
|
||||
lfsr_mdir_t mdir;
|
||||
lfsr_mtraversal_t mt = LFSR_MTRAVERSAL(LFS_T_CKMETA);
|
||||
lfsr_mtinfo_t mtinfo;
|
||||
lfsr_mtree_traverse(&lfs, &mdir, &mt, &mtinfo) => 0;
|
||||
assert(mtinfo.tag == LFSR_TAG_MDIR);
|
||||
while (true) {
|
||||
int err = lfsr_mtree_traverse(&lfs, &mdir, &mt, &mtinfo);
|
||||
assert(!err || err == LFS_ERR_NOENT);
|
||||
if (err == LFS_ERR_NOENT) {
|
||||
break;
|
||||
}
|
||||
assert(mtinfo.tag == LFSR_TAG_BRANCH);
|
||||
assert(lfsr_rbyd_eoff(&mtinfo.u.rbyd) <= GC_COMPACT_THRESH);
|
||||
}
|
||||
|
||||
// check we can still read the file
|
||||
for (int remount = 0; remount < 2; remount++) {
|
||||
// remount?
|
||||
if (remount) {
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
lfsr_mount(&lfs, CFG) => 0;
|
||||
lfsr_file_open(&lfs, &file, "jellyfish", LFS_O_RDONLY) => 0;
|
||||
}
|
||||
|
||||
lfsr_file_rewind(&lfs, &file) => 0;
|
||||
uint8_t rbuf[SIZE];
|
||||
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
||||
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
||||
}
|
||||
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
[cases.test_traversal_compact_bshrub_orphan]
|
||||
defines.CKMETA = [false, true]
|
||||
defines.CK = [false, true]
|
||||
defines.LOOKAHEAD = [false, true]
|
||||
# this configuration should create a 2-layer bshrub, which may be
|
||||
# a bit delicate
|
||||
defines.INLINE_SIZE = 'BLOCK_SIZE/4'
|
||||
defines.CRYSTAL_THRESH = -1
|
||||
defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8'
|
||||
defines.SIZE = 'BLOCK_SIZE'
|
||||
# set compact thresh to minimum
|
||||
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
||||
in = 'lfs.c'
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfsr_format(&lfs, CFG) => 0;
|
||||
lfsr_mount(&lfs, CFG) => 0;
|
||||
|
||||
uint32_t prng = 42;
|
||||
|
||||
// create a file
|
||||
lfsr_file_t file;
|
||||
lfsr_file_open(&lfs, &file, "jellyfish",
|
||||
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
||||
|
||||
uint8_t wbuf[SIZE];
|
||||
for (lfs_size_t j = 0; j < SIZE; j++) {
|
||||
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
||||
|
||||
// rewrite part of our file until bshrub is >gc_compact_thresh full
|
||||
while (true) {
|
||||
// we need internals to check this
|
||||
lfsr_mdir_t mdir;
|
||||
// ckmeta needed for eoff
|
||||
lfsr_mtraversal_t mt = LFSR_MTRAVERSAL(LFS_T_CKMETA);
|
||||
lfsr_mtinfo_t mtinfo;
|
||||
lfsr_mtree_traverse(&lfs, &mdir, &mt, &mtinfo) => 0;
|
||||
assert(mtinfo.tag == LFSR_TAG_MDIR);
|
||||
lfsr_mtree_traverse(&lfs, &mdir, &mt, &mtinfo) => 0;
|
||||
assert(mtinfo.tag == LFSR_TAG_BRANCH);
|
||||
if (lfsr_rbyd_eoff(&mtinfo.u.rbyd) > GC_COMPACT_THRESH) {
|
||||
break;
|
||||
}
|
||||
|
||||
lfsr_file_rewind(&lfs, &file) => 0;
|
||||
for (lfs_size_t j = 0; j < FRAGMENT_SIZE; j++) {
|
||||
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &file, wbuf, FRAGMENT_SIZE) => FRAGMENT_SIZE;
|
||||
}
|
||||
|
||||
// try traversing and compacting
|
||||
lfsr_traversal_t t;
|
||||
lfsr_traversal_open(&lfs, &t,
|
||||
LFS_T_COMPACT
|
||||
| ((CKMETA) ? LFS_T_CKMETA : 0)
|
||||
| ((CK) ? LFS_T_CK : 0)
|
||||
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
||||
// traverse mroot
|
||||
struct lfs_tinfo tinfo;
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
assert(tinfo.block == 0 || tinfo.block == 1);
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
assert(tinfo.block == 0 || tinfo.block == 1);
|
||||
// compacting our bshrub nodes may cause them to split, so we may
|
||||
// need to traverse more nodes than we started with
|
||||
for (lfs_size_t i = 0; i < 5; i++) {
|
||||
// traverse bshrub
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
||||
}
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
||||
lfsr_traversal_close(&lfs, &t) => 0;
|
||||
|
||||
// bshrub should have been compacted
|
||||
lfsr_mdir_t mdir;
|
||||
lfsr_mtraversal_t mt = LFSR_MTRAVERSAL(LFS_T_CKMETA);
|
||||
lfsr_mtinfo_t mtinfo;
|
||||
lfsr_mtree_traverse(&lfs, &mdir, &mt, &mtinfo) => 0;
|
||||
assert(mtinfo.tag == LFSR_TAG_MDIR);
|
||||
while (true) {
|
||||
int err = lfsr_mtree_traverse(&lfs, &mdir, &mt, &mtinfo);
|
||||
assert(!err || err == LFS_ERR_NOENT);
|
||||
if (err == LFS_ERR_NOENT) {
|
||||
break;
|
||||
}
|
||||
assert(mtinfo.tag == LFSR_TAG_BRANCH);
|
||||
assert(lfsr_rbyd_eoff(&mtinfo.u.rbyd) <= GC_COMPACT_THRESH);
|
||||
}
|
||||
|
||||
// check we can still read the file
|
||||
for (int remount = 0; remount < 2; remount++) {
|
||||
// remount?
|
||||
if (remount) {
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
lfsr_mount(&lfs, CFG) => 0;
|
||||
lfsr_file_open(&lfs, &file, "jellyfish", LFS_O_RDONLY) => 0;
|
||||
}
|
||||
|
||||
lfsr_file_rewind(&lfs, &file) => 0;
|
||||
uint8_t rbuf[SIZE];
|
||||
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
||||
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
||||
}
|
||||
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
[cases.test_traversal_compact_bshrub_desync]
|
||||
defines.CKMETA = [false, true]
|
||||
defines.CK = [false, true]
|
||||
defines.LOOKAHEAD = [false, true]
|
||||
# this configuration should create a 2-layer bshrub, which may be
|
||||
# a bit delicate
|
||||
defines.INLINE_SIZE = 'BLOCK_SIZE/4'
|
||||
defines.CRYSTAL_THRESH = -1
|
||||
defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8'
|
||||
defines.SIZE = 'BLOCK_SIZE'
|
||||
# set compact thresh to minimum
|
||||
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
||||
in = 'lfs.c'
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfsr_format(&lfs, CFG) => 0;
|
||||
lfsr_mount(&lfs, CFG) => 0;
|
||||
|
||||
uint32_t prng = 42;
|
||||
|
||||
// create a desync file
|
||||
lfsr_file_t file1;
|
||||
lfsr_file_open(&lfs, &file1, "jellyfish",
|
||||
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
||||
|
||||
uint8_t wbuf1[SIZE];
|
||||
for (lfs_size_t j = 0; j < SIZE; j++) {
|
||||
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &file1, wbuf1, SIZE) => SIZE;
|
||||
|
||||
// rewrite part of our file until bshrub is >gc_compact_thresh full
|
||||
while (true) {
|
||||
// we need internals to check this
|
||||
lfsr_mdir_t mdir;
|
||||
// ckmeta needed for eoff
|
||||
lfsr_mtraversal_t mt = LFSR_MTRAVERSAL(LFS_T_CKMETA);
|
||||
lfsr_mtinfo_t mtinfo;
|
||||
lfsr_mtree_traverse(&lfs, &mdir, &mt, &mtinfo) => 0;
|
||||
assert(mtinfo.tag == LFSR_TAG_MDIR);
|
||||
lfsr_mtree_traverse(&lfs, &mdir, &mt, &mtinfo) => 0;
|
||||
assert(mtinfo.tag == LFSR_TAG_BRANCH);
|
||||
if (lfsr_rbyd_eoff(&mtinfo.u.rbyd) > GC_COMPACT_THRESH) {
|
||||
break;
|
||||
}
|
||||
|
||||
lfsr_file_rewind(&lfs, &file1) => 0;
|
||||
for (lfs_size_t j = 0; j < FRAGMENT_SIZE; j++) {
|
||||
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &file1, wbuf1, FRAGMENT_SIZE) => FRAGMENT_SIZE;
|
||||
}
|
||||
|
||||
// create some overlapping files, these should not get messed with
|
||||
lfsr_file_t file2;
|
||||
lfsr_file_open(&lfs, &file2, "jellyfish",
|
||||
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
||||
uint8_t wbuf2[SIZE];
|
||||
for (lfs_size_t j = 0; j < SIZE; j++) {
|
||||
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &file2, wbuf2, SIZE) => SIZE;
|
||||
lfsr_file_sync(&lfs, &file2) => 0;
|
||||
|
||||
lfsr_file_t file3;
|
||||
lfsr_file_open(&lfs, &file3, "jellyfish",
|
||||
LFS_O_RDWR) => 0;
|
||||
uint8_t wbuf3[SIZE];
|
||||
for (lfs_size_t j = 0; j < SIZE; j++) {
|
||||
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &file3, wbuf3, SIZE) => SIZE;
|
||||
lfsr_file_desync(&lfs, &file3) => 0;
|
||||
|
||||
// try traversing and compacting
|
||||
lfsr_traversal_t t;
|
||||
lfsr_traversal_open(&lfs, &t,
|
||||
LFS_T_COMPACT
|
||||
| ((CKMETA) ? LFS_T_CKMETA : 0)
|
||||
| ((CK) ? LFS_T_CK : 0)
|
||||
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
||||
// traverse mroot
|
||||
struct lfs_tinfo tinfo;
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
assert(tinfo.block == 0 || tinfo.block == 1);
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
assert(tinfo.block == 0 || tinfo.block == 1);
|
||||
// compacting our bshrub nodes may cause them to split, so we may
|
||||
// need to traverse more nodes than we started with
|
||||
for (lfs_size_t i = 0; i < 3*5; i++) {
|
||||
// traverse bshrub
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
||||
}
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
||||
lfsr_traversal_close(&lfs, &t) => 0;
|
||||
|
||||
// bshrub should have been compacted
|
||||
lfsr_mdir_t mdir;
|
||||
lfsr_mtraversal_t mt = LFSR_MTRAVERSAL(LFS_T_CKMETA);
|
||||
lfsr_mtinfo_t mtinfo;
|
||||
lfsr_mtree_traverse(&lfs, &mdir, &mt, &mtinfo) => 0;
|
||||
assert(mtinfo.tag == LFSR_TAG_MDIR);
|
||||
while (true) {
|
||||
int err = lfsr_mtree_traverse(&lfs, &mdir, &mt, &mtinfo);
|
||||
assert(!err || err == LFS_ERR_NOENT);
|
||||
if (err == LFS_ERR_NOENT) {
|
||||
break;
|
||||
}
|
||||
assert(mtinfo.tag == LFSR_TAG_BRANCH);
|
||||
assert(lfsr_rbyd_eoff(&mtinfo.u.rbyd) <= GC_COMPACT_THRESH);
|
||||
}
|
||||
|
||||
// check we can still read the files
|
||||
lfsr_file_rewind(&lfs, &file1) => 0;
|
||||
uint8_t rbuf[SIZE];
|
||||
lfsr_file_read(&lfs, &file1, rbuf, SIZE) => SIZE;
|
||||
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
||||
|
||||
lfsr_file_rewind(&lfs, &file2) => 0;
|
||||
lfsr_file_read(&lfs, &file2, rbuf, SIZE) => SIZE;
|
||||
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
||||
|
||||
lfsr_file_rewind(&lfs, &file3) => 0;
|
||||
lfsr_file_read(&lfs, &file3, rbuf, SIZE) => SIZE;
|
||||
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
||||
|
||||
// at least try closing/opening our synced file
|
||||
lfsr_file_close(&lfs, &file2) => 0;
|
||||
lfsr_file_open(&lfs, &file2, "jellyfish", LFS_O_RDONLY) => 0;
|
||||
lfsr_file_read(&lfs, &file2, rbuf, SIZE) => SIZE;
|
||||
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
||||
|
||||
lfsr_file_close(&lfs, &file1) => 0;
|
||||
lfsr_file_close(&lfs, &file2) => 0;
|
||||
lfsr_file_close(&lfs, &file3) => 0;
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
|
||||
|
||||
@@ -7024,8 +7504,7 @@ defines.EXCL = [false, true]
|
||||
defines.CKMETA = [true]
|
||||
defines.CK = [true]
|
||||
defines.LOOKAHEAD = [false, true]
|
||||
# TODO !!! Enable this when bshrub compaction is working
|
||||
defines.COMPACT = [false]
|
||||
defines.COMPACT = [false, true]
|
||||
# set compact thresh to minimum
|
||||
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
||||
defines.OPS = 20
|
||||
|
||||
Reference in New Issue
Block a user