From 0617244aa39bb37869623d9a16426cb997e6fafa Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Mon, 6 Jan 2025 23:21:42 -0600 Subject: [PATCH] gc: Dropped lfsr_gc_setflags/setsteps Now that you can provide gc_flags/gc_steps in lfs_config, I think it's a bit more clear that _mutating_ the flags/steps is a niche feature, and not worth implementing/testing. It raises the question why not have a similar lfsr_setflags or lfsr_file_setflags, and the answer there is it would be a pain-in-the- ass to make sure all possible corner cases are covered. It actually already was a pain-in-the-ass to test lfsr_gcsetflags/ setsteps... but just because we already did the work is not a good reason for keeping complexity around. --- Note that most of the use cases for lfsr_gc_setflags/setsteps can be covered by either remounting the filesystem or through the lfsr_traversal_t APIs directly. The end result is a bit of code savings when incremental gc is enabled: code stack ctx default before: 37796 2608 620 default after: 37796 (+0.0%) 2608 (+0.0%) 620 (+0.0%) gc before: 37944 2608 768 gc after 37896 (-0.1%) 2608 (+0.0%) 768 (+0.0%) --- lfs.c | 28 -- lfs.h | 21 -- tests/test_gc.toml | 758 --------------------------------------------- 3 files changed, 807 deletions(-) diff --git a/lfs.c b/lfs.c index 3ff06466..3e3ae8a5 100644 --- a/lfs.c +++ b/lfs.c @@ -14759,34 +14759,6 @@ int lfsr_traversal_rewind(lfs_t *lfs, lfsr_traversal_t *t) { /// Incremental gc operations /// -#ifdef LFS_GC -int lfsr_gc_setflags(lfs_t *lfs, uint32_t flags) { - // unknown gc flags? - LFS_ASSERT((flags & ~( - LFS_GC_MTREEONLY - | LFS_GC_MKCONSISTENT - | LFS_GC_LOOKAHEAD - | LFS_GC_COMPACT - | LFS_GC_CKMETA - | LFS_GC_CKDATA)) == 0); - - // clobber any existing traversals - if (lfsr_omdir_isopen(lfs, &lfs->gc.t.o.o)) { - lfsr_omdir_close(lfs, &lfs->gc.t.o.o); - } - - lfs->gc.flags = flags; - return 0; -} -#endif - -#ifdef LFS_GC -int lfsr_gc_setsteps(lfs_t *lfs, lfs_soff_t steps) { - lfs->gc.steps = steps; - return 0; -} -#endif - #ifdef LFS_GC // perform any pending janitorial work int lfsr_gc(lfs_t *lfs) { diff --git a/lfs.h b/lfs.h index 8f0608c7..0b2a5eca 100644 --- a/lfs.h +++ b/lfs.h @@ -1270,27 +1270,6 @@ int lfsr_traversal_rewind(lfs_t *lfs, lfsr_traversal_t *t); int lfsr_gc(lfs_t *lfs); #endif -#ifdef LFS_GC -// Sets the gc flags. -// -// Returns a negative error code on failure. -int lfsr_gc_setflags(lfs_t *lfs, uint32_t flags); -#endif - -#ifdef LFS_GC -// Sets the number of gc steps per lfsr_gc call, with each step being -// ~1 block of work. -// -// More steps per call will make more progress if interleaved with -// other filesystem operations, but may also introduce more latency. -// steps=1 will do the minimum amount of work to make progress, and -// steps=-1 will not return until all pending janitorial work has -// been completed. -// -// Returns a negative error code on failure. -int lfsr_gc_setsteps(lfs_t *lfs, lfs_soff_t steps); -#endif - /// Filesystem-level filesystem operations diff --git a/tests/test_gc.toml b/tests/test_gc.toml index 03866834..21e42790 100644 --- a/tests/test_gc.toml +++ b/tests/test_gc.toml @@ -70,136 +70,6 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -# test that we can change flags after mount -[cases.test_gc_setflags] -defines.CKMETA = [false, true] -defines.CKDATA = [false, true] -defines.GC_FLAGS = ''' - ((CKMETA) ? LFS_GC_CKMETA : 0) - | ((CKDATA) ? LFS_GC_CKDATA : 0) -''' -defines.SIZE = [ - 'FILE_BUFFER_SIZE/2', - '2*FILE_BUFFER_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '8*BLOCK_SIZE', -] -ifdef = 'LFS_GC' -code = ''' - lfs_t lfs; - lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; - lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; - - uint32_t prng = 42; - - // create a file - lfsr_file_t file; - lfsr_file_open(&lfs, &file, "spider", - 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; - lfsr_file_close(&lfs, &file) => 0; - - // expect dirty initial state or else our test doesn't work - struct lfs_fsinfo fsinfo; - lfsr_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.flags & LFS_I_CANLOOKAHEAD); - assert(lfs.omdirs != &lfs.gc.t.o.o); - - // change flags - lfsr_gc_setflags(&lfs, GC_FLAGS | LFS_GC_LOOKAHEAD) => 0; - - // run GC until we make progress - for (lfs_block_t i = 0;; i++) { - // a bit hacky, but this catches infinite loops - LFS_ASSERT(i < 2*BLOCK_COUNT); - - lfsr_gc(&lfs) => 0; - - lfsr_fs_stat(&lfs, &fsinfo) => 0; - if (!(fsinfo.flags & LFS_I_CANLOOKAHEAD)) { - break; - } - } - - // check the file contents - lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 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; -''' - -# test that we can change steps after mount -[cases.test_gc_setsteps] -defines.CKMETA = [false, true] -defines.CKDATA = [false, true] -defines.GC_FLAGS = ''' - LFS_GC_LOOKAHEAD - | ((CKMETA) ? LFS_GC_CKMETA : 0) - | ((CKDATA) ? LFS_GC_CKDATA : 0) -''' -defines.GC_STEPS = 1 -defines.SIZE = [ - 'FILE_BUFFER_SIZE/2', - '2*FILE_BUFFER_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '8*BLOCK_SIZE', -] -ifdef = 'LFS_GC' -code = ''' - lfs_t lfs; - lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; - lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; - - uint32_t prng = 42; - - // create a file - lfsr_file_t file; - lfsr_file_open(&lfs, &file, "spider", - 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; - lfsr_file_close(&lfs, &file) => 0; - - // expect dirty initial state or else our test doesn't work - struct lfs_fsinfo fsinfo; - lfsr_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.flags & LFS_I_CANLOOKAHEAD); - assert(lfs.omdirs != &lfs.gc.t.o.o); - - // change steps - lfsr_gc_setsteps(&lfs, -1) => 0; - - // run GC - lfsr_gc(&lfs) => 0; - - // we should have made progress - lfsr_fs_stat(&lfs, &fsinfo) => 0; - assert(!(fsinfo.flags & LFS_I_CANLOOKAHEAD)); - - // check the file contents - lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 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; -''' - # test that lookahead dirtying still works with the GC API [cases.test_gc_lookahead_mutation] defines.CKMETA = [false, true] @@ -276,143 +146,6 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -# test that adding flags doesn't break lookahead -[cases.test_gc_lookahead_add_flags] -defines.CKMETA = [false, true] -defines.CKDATA = [false, true] -defines.GC_FLAGS = ''' - ((CKMETA) ? LFS_GC_CKMETA : 0) - | ((CKDATA) ? LFS_GC_CKDATA : 0) -''' -defines.SIZE = [ - 'FILE_BUFFER_SIZE/2', - '2*FILE_BUFFER_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '8*BLOCK_SIZE', -] -# we need something to keep the traversal running -if = 'CKMETA || CKDATA' -ifdef = 'LFS_GC' -code = ''' - lfs_t lfs; - lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; - lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; - - uint32_t prng = 42; - - // create a file - lfsr_file_t file; - lfsr_file_open(&lfs, &file, "spider", - 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; - lfsr_file_close(&lfs, &file) => 0; - - // expect dirty initial state or else our test doesn't work - struct lfs_fsinfo fsinfo; - lfsr_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.flags & LFS_I_CANLOOKAHEAD); - assert(lfs.omdirs != &lfs.gc.t.o.o); - - // run GC one step - lfsr_gc(&lfs) => 0; - assert(lfs.omdirs == &lfs.gc.t.o.o); - - // change flags and run GC until our traversal is done - lfsr_gc_setflags(&lfs, GC_FLAGS | LFS_GC_LOOKAHEAD) => 0; - - while (lfs.omdirs == &lfs.gc.t.o.o) { - lfsr_gc(&lfs) => 0; - } - - // we should _not_ make progress - lfsr_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.flags & LFS_I_CANLOOKAHEAD); - - // check the file contents - lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 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; -''' - -# test that removing flags invalidates lookahead -[cases.test_gc_lookahead_remove_flags] -defines.CKMETA = [false, true] -defines.CKDATA = [false, true] -defines.GC_FLAGS = ''' - LFS_GC_LOOKAHEAD - | ((CKMETA) ? LFS_GC_CKMETA : 0) - | ((CKDATA) ? LFS_GC_CKDATA : 0) -''' -defines.SIZE = [ - 'FILE_BUFFER_SIZE/2', - '2*FILE_BUFFER_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '8*BLOCK_SIZE', -] -# we need something to keep the traversal running -if = 'CKMETA || CKDATA' -ifdef = 'LFS_GC' -code = ''' - lfs_t lfs; - lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; - lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; - - uint32_t prng = 42; - - // create a file - lfsr_file_t file; - lfsr_file_open(&lfs, &file, "spider", - 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; - lfsr_file_close(&lfs, &file) => 0; - - // expect dirty initial state or else our test doesn't work - struct lfs_fsinfo fsinfo; - lfsr_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.flags & LFS_I_CANLOOKAHEAD); - assert(lfs.omdirs != &lfs.gc.t.o.o); - - // run GC one step - lfsr_gc(&lfs) => 0; - assert(lfs.omdirs == &lfs.gc.t.o.o); - - // change flags and run GC until our traversal is done - lfsr_gc_setflags(&lfs, GC_FLAGS & ~LFS_GC_LOOKAHEAD) => 0; - - while (lfs.omdirs == &lfs.gc.t.o.o) { - lfsr_gc(&lfs) => 0; - } - - // we should _not_ make progress - lfsr_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.flags & LFS_I_CANLOOKAHEAD); - - // check the file contents - lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 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; -''' - # test that compact can make progress in isolation [cases.test_gc_compact_progress] @@ -604,197 +337,6 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -# test that adding flags doesn't break compact -[cases.test_gc_compact_add_flags] -defines.LOOKAHEAD = [false, true] -defines.CKMETA = [false, true] -defines.CKDATA = [false, true] -defines.GC_FLAGS = ''' - ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) - | ((CKMETA) ? LFS_GC_CKMETA : 0) - | ((CKDATA) ? LFS_GC_CKDATA : 0) -''' -# set compact thresh to minimum -defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2' -defines.SIZE = [ - 'FILE_BUFFER_SIZE/2', - '2*FILE_BUFFER_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '8*BLOCK_SIZE', -] -# we need something to keep the traversal running -if = 'CKMETA || CKDATA' -ifdef = 'LFS_GC' -code = ''' - lfs_t lfs; - lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; - lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; - - uint32_t prng = 42; - - // write to our mdir until >gc_compact_thresh full - lfsr_file_t file; - lfsr_file_open(&lfs, &file, "jellyfish", - LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; - - // hack, don't use the internals like this - uint8_t wbuf[SIZE]; - while ((file.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) { - lfsr_file_rewind(&lfs, &file) => 0; - for (lfs_size_t j = 0; j < SIZE; j++) { - wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); - } - lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; - lfsr_file_sync(&lfs, &file) => 0; - } - - // expect dirty initial state or else our test doesn't work - struct lfs_fsinfo fsinfo; - lfsr_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.flags & LFS_I_UNCOMPACTED); - assert(lfs.omdirs != &lfs.gc.t.o.o); - - // run GC one traversal + one step - while (true) { - lfsr_gc(&lfs) => 0; - - // internal traversal done? - if (lfs.omdirs != &lfs.gc.t.o.o) { - break; - } - } - lfsr_gc(&lfs) => 0; - assert(lfs.omdirs == &lfs.gc.t.o.o); - - // change flags and run GC until our traversal is done (twice for compact) - lfsr_gc_setflags(&lfs, GC_FLAGS | LFS_GC_COMPACT) => 0; - - while (lfs.omdirs == &lfs.gc.t.o.o) { - lfsr_gc(&lfs) => 0; - } - - // we should _not_ make progress - lfsr_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.flags & LFS_I_UNCOMPACTED); - - // 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, LFS_M_RDWR, 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; -''' - -# test that removing flags invalidates compact -[cases.test_gc_compact_remove_flags] -defines.LOOKAHEAD = [false, true] -defines.CKMETA = [false, true] -defines.CKDATA = [false, true] -defines.GC_FLAGS = ''' - LFS_GC_COMPACT - | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) - | ((CKMETA) ? LFS_GC_CKMETA : 0) - | ((CKDATA) ? LFS_GC_CKDATA : 0) -''' -# set compact thresh to minimum -defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2' -defines.SIZE = [ - 'FILE_BUFFER_SIZE/2', - '2*FILE_BUFFER_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '8*BLOCK_SIZE', -] -# we need something to keep the traversal running -if = 'CKMETA || CKDATA' -ifdef = 'LFS_GC' -code = ''' - lfs_t lfs; - lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; - lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; - - uint32_t prng = 42; - - // write to our mdir until >gc_compact_thresh full - lfsr_file_t file; - lfsr_file_open(&lfs, &file, "jellyfish", - LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; - - // hack, don't use the internals like this - uint8_t wbuf[SIZE]; - while ((file.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) { - lfsr_file_rewind(&lfs, &file) => 0; - for (lfs_size_t j = 0; j < SIZE; j++) { - wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); - } - lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; - lfsr_file_sync(&lfs, &file) => 0; - } - - // expect dirty initial state or else our test doesn't work - struct lfs_fsinfo fsinfo; - lfsr_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.flags & LFS_I_UNCOMPACTED); - assert(lfs.omdirs != &lfs.gc.t.o.o); - - // run GC one traversal + one step - while (true) { - lfsr_gc(&lfs) => 0; - - // internal traversal done? - if (lfs.omdirs != &lfs.gc.t.o.o) { - break; - } - } - lfsr_gc(&lfs) => 0; - assert(lfs.omdirs == &lfs.gc.t.o.o); - - // change flags and run GC until our traversal is done (twice for compact) - lfsr_gc_setflags(&lfs, GC_FLAGS & ~LFS_GC_COMPACT) => 0; - - while (lfs.omdirs == &lfs.gc.t.o.o) { - lfsr_gc(&lfs) => 0; - } - - // we should _not_ make progress - lfsr_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.flags & LFS_I_UNCOMPACTED); - - // 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, LFS_M_RDWR, 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; -''' - # test that mkconsistent can make progress in isolation [cases.test_gc_mkconsistent_progress] @@ -1119,233 +661,6 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -# test that adding flags doesn't break mkconsistent -[cases.test_gc_mkconsistent_add_flags] -defines.LOOKAHEAD = [false, true] -defines.COMPACT = [false, true] -defines.CKMETA = [false, true] -defines.CKDATA = [false, true] -defines.GC_FLAGS = ''' - ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) - | ((COMPACT) ? LFS_GC_COMPACT : 0) - | ((CKMETA) ? LFS_GC_CKMETA : 0) - | ((CKDATA) ? LFS_GC_CKDATA : 0) -''' -defines.SIZE = 'FILE_BUFFER_SIZE/2' -# <=2 => grm-able -# >2 => requires orphans -defines.ORPHANS = [3, 100] -# we need something to keep the traversal running -if = 'CKMETA || CKDATA' -ifdef = 'LFS_GC' -code = ''' - lfs_t lfs; - lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; - lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; - - uint32_t prng = 42; - - // create two files - lfsr_file_t file1; - lfsr_file_open(&lfs, &file1, "cuttlefish", - LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 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; - lfsr_file_sync(&lfs, &file1) => 0; - - lfsr_file_t file2; - lfsr_file_open(&lfs, &file2, "octopus", - 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; - - // create this many orphaned files - // - // anytime we close a not-yet-created desync file, we create an - // orphan, but note we need these to be different files, and we need - // to close them after all open calls, otherwise we just end up with - // one orphan (littlefs is eager to clean up orphans) - // - lfsr_file_t orphans[ORPHANS]; - for (lfs_size_t i = 0; i < ORPHANS; i++) { - char name[256]; - sprintf(name, "jellyfish%03x", i); - lfsr_file_open(&lfs, &orphans[i], name, - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0; - } - for (lfs_size_t i = 0; i < ORPHANS; i++) { - lfsr_file_close(&lfs, &orphans[i]) => 0; - } - - // expect dirty initial state or else our test doesn't work - struct lfs_fsinfo fsinfo; - lfsr_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.flags & LFS_I_INCONSISTENT); - assert(lfs.omdirs != &lfs.gc.t.o.o); - - // run GC one step - lfsr_gc(&lfs) => 0; - assert(lfs.omdirs == &lfs.gc.t.o.o); - - // change flags and run GC until our traversal is done - lfsr_gc_setflags(&lfs, GC_FLAGS | LFS_GC_MKCONSISTENT) => 0; - - while (lfs.omdirs == &lfs.gc.t.o.o) { - lfsr_gc(&lfs) => 0; - } - - // we should _not_ make progress - lfsr_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.flags & LFS_I_INCONSISTENT); - - // check we can still read the files - for (int remount = 0; remount < 2; remount++) { - // remount? - if (remount) { - lfsr_file_close(&lfs, &file1) => 0; - lfsr_file_close(&lfs, &file2) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; - lfsr_file_open(&lfs, &file1, "cuttlefish", LFS_O_RDONLY) => 0; - lfsr_file_open(&lfs, &file2, "octopus", LFS_O_RDONLY) => 0; - } - - 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_close(&lfs, &file1) => 0; - lfsr_file_close(&lfs, &file2) => 0; - lfsr_unmount(&lfs) => 0; -''' - -# test that removing flags invalidates mkconsistent -[cases.test_gc_mkconsistent_remove_flags] -defines.LOOKAHEAD = [false, true] -defines.COMPACT = [false, true] -defines.CKMETA = [false, true] -defines.CKDATA = [false, true] -defines.GC_FLAGS = ''' - LFS_GC_MKCONSISTENT - | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) - | ((COMPACT) ? LFS_GC_COMPACT : 0) - | ((CKMETA) ? LFS_GC_CKMETA : 0) - | ((CKDATA) ? LFS_GC_CKDATA : 0) -''' -defines.SIZE = 'FILE_BUFFER_SIZE/2' -# <=2 => grm-able -# >2 => requires orphans -defines.ORPHANS = [3, 100] -# we need something to keep the traversal running -if = 'CKMETA || CKDATA' -ifdef = 'LFS_GC' -code = ''' - lfs_t lfs; - lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; - lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; - - uint32_t prng = 42; - - // create two files - lfsr_file_t file1; - lfsr_file_open(&lfs, &file1, "cuttlefish", - LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 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; - lfsr_file_sync(&lfs, &file1) => 0; - - lfsr_file_t file2; - lfsr_file_open(&lfs, &file2, "octopus", - 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; - - // create this many orphaned files - // - // anytime we close a not-yet-created desync file, we create an - // orphan, but note we need these to be different files, and we need - // to close them after all open calls, otherwise we just end up with - // one orphan (littlefs is eager to clean up orphans) - // - lfsr_file_t orphans[ORPHANS]; - for (lfs_size_t i = 0; i < ORPHANS; i++) { - char name[256]; - sprintf(name, "jellyfish%03x", i); - lfsr_file_open(&lfs, &orphans[i], name, - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0; - } - for (lfs_size_t i = 0; i < ORPHANS; i++) { - lfsr_file_close(&lfs, &orphans[i]) => 0; - } - - // expect dirty initial state or else our test doesn't work - struct lfs_fsinfo fsinfo; - lfsr_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.flags & LFS_I_INCONSISTENT); - assert(lfs.omdirs != &lfs.gc.t.o.o); - - // run GC one step - lfsr_gc(&lfs) => 0; - assert(lfs.omdirs == &lfs.gc.t.o.o); - - // change flags and run GC until our traversal is done - lfsr_gc_setflags(&lfs, GC_FLAGS & ~LFS_GC_MKCONSISTENT) => 0; - - while (lfs.omdirs == &lfs.gc.t.o.o) { - lfsr_gc(&lfs) => 0; - } - - // we should _not_ make progress - lfsr_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.flags & LFS_I_INCONSISTENT); - - // check we can still read the files - for (int remount = 0; remount < 2; remount++) { - // remount? - if (remount) { - lfsr_file_close(&lfs, &file1) => 0; - lfsr_file_close(&lfs, &file2) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; - lfsr_file_open(&lfs, &file1, "cuttlefish", LFS_O_RDONLY) => 0; - lfsr_file_open(&lfs, &file2, "octopus", LFS_O_RDONLY) => 0; - } - - 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_close(&lfs, &file1) => 0; - lfsr_file_close(&lfs, &file2) => 0; - lfsr_unmount(&lfs) => 0; -''' - # test we can detect at least fully clobbered blocks # @@ -1800,79 +1115,6 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -# pseudo-fuzz test that adding/removing flags doesn't break anything -[cases.test_gc_changing_flags] -defines.N = 100 -defines.MKCONSISTENT = [false, true] -defines.LOOKAHEAD = [false, true] -defines.COMPACT = [false, true] -defines.CKMETA = [false, true] -defines.CKDATA = [false, true] -defines.GC_FLAGS = ''' - ((MKCONSISTENT) ? LFS_GC_MKCONSISTENT : 0) - | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) - | ((COMPACT) ? LFS_GC_COMPACT : 0) - | ((CKMETA) ? LFS_GC_CKMETA : 0) - | ((CKDATA) ? LFS_GC_CKDATA : 0) -''' -defines.GC_STEPS = [-1, 1, 2, 10, 100, 1000] -# set compact thresh to minimum -defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2' -defines.SIZE = [ - 'FILE_BUFFER_SIZE/2', - '2*FILE_BUFFER_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '8*BLOCK_SIZE', -] -ifdef = 'LFS_GC' -code = ''' - lfs_t lfs; - lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; - lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; - - uint32_t prng = 42; - - // create a file - lfsr_file_t file; - lfsr_file_open(&lfs, &file, "spider", - 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; - lfsr_file_close(&lfs, &file) => 0; - - for (uint32_t i = 0; i < N; i++) { - // rewrite the file every gc cycle - lfsr_file_open(&lfs, &file, "spider", - LFS_O_WRONLY | LFS_O_TRUNC) => 0; - for (lfs_size_t j = 0; j < SIZE; j++) { - wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); - } - lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; - lfsr_file_close(&lfs, &file) => 0; - - // choose a new subset of flags every cycle - uint32_t flags = GC_FLAGS & TEST_PRNG(&prng); - lfsr_gc_setflags(&lfs, flags) => 0; - - // gc! - lfsr_gc(&lfs) => 0; - } - - // check the file contents - lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 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; -''' - # many/fuzz tests mixed with GC