diff --git a/lfs.c b/lfs.c index 241aa118..a0c1f807 100644 --- a/lfs.c +++ b/lfs.c @@ -13952,7 +13952,7 @@ static int lfsr_mountinited(lfs_t *lfs) { } // needed in lfsr_mount -static int lfsr_fs_gc(lfs_t *lfs, lfsr_traversal_t *t, +static int lfsr_fs_gc_(lfs_t *lfs, lfsr_traversal_t *t, uint32_t flags, lfs_soff_t steps); int lfsr_mount(lfs_t *lfs, uint32_t flags, @@ -14010,7 +14010,7 @@ int lfsr_mount(lfs_t *lfs, uint32_t flags, | LFS_M_CKMETA | LFS_M_CKDATA)) { lfsr_traversal_t t; - err = lfsr_fs_gc(lfs, &t, + err = lfsr_fs_gc_(lfs, &t, flags & ( LFS_M_MTREEONLY | LFS_M_MKCONSISTENT @@ -14189,7 +14189,7 @@ int lfsr_format(lfs_t *lfs, uint32_t flags, | LFS_F_CKMETA | LFS_F_CKDATA)) { lfsr_traversal_t t; - err = lfsr_fs_gc(lfs, &t, + err = lfsr_fs_gc_(lfs, &t, flags & ( LFS_F_MTREEONLY | LFS_F_COMPACT @@ -14449,7 +14449,7 @@ int lfsr_fs_ckdata(lfs_t *lfs) { // // runs the traversal until all work is completed, which may take // multiple passes -static int lfsr_fs_gc(lfs_t *lfs, lfsr_traversal_t *t, +static int lfsr_fs_gc_(lfs_t *lfs, lfsr_traversal_t *t, uint32_t flags, lfs_soff_t steps) { // unknown gc flags? // @@ -14555,6 +14555,40 @@ static int lfsr_fs_gc(lfs_t *lfs, lfsr_traversal_t *t, return 0; } +#ifdef LFS_GC +// incremental filesystem gc +// +// perform any pending janitorial work +int lfsr_fs_gc(lfs_t *lfs) { + return lfsr_fs_gc_(lfs, &lfs->gc.t, + lfs->gc.flags, lfs->gc.steps); +} +#endif + +// unperform janitorial work +int lfsr_fs_unck(lfs_t *lfs, uint32_t flags) { + // unknown flags? + LFS_ASSERT((flags & ~( + LFS_I_INCONSISTENT + | LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED + | LFS_I_CANCKMETA + | LFS_I_CANCKDATA)) == 0); + + // reset the requested flags + lfs->flags |= flags; + + #ifdef LFS_GC + // and clear from any ongoing traversals + // + // lfsr_fs_gc will terminate early if it discovers it can no longer + // make progress + lfs->gc.t.o.o.flags &= ~flags; + #endif + + return 0; +} + // attempt to grow the filesystem int lfsr_fs_grow(lfs_t *lfs, lfs_size_t block_count_) { @@ -14786,42 +14820,6 @@ int lfsr_traversal_rewind(lfs_t *lfs, lfsr_traversal_t *t) { -/// Incremental gc operations /// - -#ifdef LFS_GC -// perform any pending janitorial work -int lfsr_gc(lfs_t *lfs) { - return lfsr_fs_gc(lfs, &lfs->gc.t, - lfs->gc.flags, lfs->gc.steps); -} -#endif - -#ifdef LFS_GC -// unperform janitorial work -int lfsr_gc_unck(lfs_t *lfs, uint32_t flags) { - // unknown flags? - LFS_ASSERT((flags & ~( - LFS_I_INCONSISTENT - | LFS_I_CANLOOKAHEAD - | LFS_I_UNCOMPACTED - | LFS_I_CANCKMETA - | LFS_I_CANCKDATA)) == 0); - - // reset the requested flags - lfs->flags |= flags; - - // and clear from any ongoing traversals - // - // lfsr_fs_gc will terminate early if it discovers it can no longer - // make progress - lfs->gc.t.o.o.flags &= ~flags; - - return 0; -} -#endif - - - diff --git a/lfs.h b/lfs.h index aae8660e..a9ff3eb6 100644 --- a/lfs.h +++ b/lfs.h @@ -1258,35 +1258,6 @@ int lfsr_traversal_read(lfs_t *lfs, lfsr_traversal_t *t, int lfsr_traversal_rewind(lfs_t *lfs, lfsr_traversal_t *t); -/// Incremental gc operations /// - -#ifdef LFS_GC -// Perform any janitorial work that may be pending. -// -// The exact janitorial work depends on the configured flags and steps. -// -// Calling this function is not required, but may allow the offloading of -// expensive janitorial work to a less time-critical code path. -// -// Returns a negative error code on failure. -int lfsr_gc(lfs_t *lfs); -#endif - -#ifdef LFS_GC -// Mark janitorial work as incomplete. -// -// Any info flags passed to lfsr_gc_unck will be reset internally, -// forcing the work to be redone. -// -// This is most useful for triggering new ckmeta/ckdata scans with -// LFS_I_CANCKMETA and LFS_I_CANCKDATA. Otherwise littlefs will perform -// only one scan after mount. -// -// Returns a negative error code on failure. -int lfsr_gc_unck(lfs_t *lfs, uint32_t flags); -#endif - - /// Filesystem-level filesystem operations // Find on-disk info about the filesystem @@ -1341,6 +1312,30 @@ int lfsr_fs_ckmeta(lfs_t *lfs); int lfsr_fs_ckdata(lfs_t *lfs); #endif +#ifdef LFS_GC +// Perform any janitorial work that may be pending +// +// The exact janitorial work depends on the configured flags and steps. +// +// Calling this function is not required, but may allow the offloading of +// expensive janitorial work to a less time-critical code path. +// +// Returns a negative error code on failure. +int lfsr_fs_gc(lfs_t *lfs); +#endif + +// Mark janitorial work as incomplete +// +// Any info flags passed to lfsr_gc_unck will be reset internally, +// forcing the work to be redone. +// +// This is most useful for triggering new ckmeta/ckdata scans with +// LFS_I_CANCKMETA and LFS_I_CANCKDATA. Otherwise littlefs will perform +// only one scan after mount. +// +// Returns a negative error code on failure. +int lfsr_fs_unck(lfs_t *lfs, uint32_t flags); + #ifndef LFS_READONLY // Change the number of blocks used by the filesystem // diff --git a/tests/test_attrs.toml b/tests/test_attrs.toml index 94517a13..ce71b907 100644 --- a/tests/test_attrs.toml +++ b/tests/test_attrs.toml @@ -531,7 +531,7 @@ code = ''' // try compacting? #ifdef LFS_GC if (COMPACT) { - lfsr_gc(&lfs) => 0; + lfsr_fs_gc(&lfs) => 0; } #endif @@ -614,7 +614,7 @@ code = ''' // try compacting? #ifdef LFS_GC if (COMPACT) { - lfsr_gc(&lfs) => 0; + lfsr_fs_gc(&lfs) => 0; } #endif @@ -4119,7 +4119,7 @@ code = ''' // try compacting? #ifdef LFS_GC if (COMPACT) { - lfsr_gc(&lfs) => 0; + lfsr_fs_gc(&lfs) => 0; } #endif diff --git a/tests/test_ck.toml b/tests/test_ck.toml index 5e5b9a0a..a3f7ab75 100644 --- a/tests/test_ck.toml +++ b/tests/test_ck.toml @@ -8,7 +8,7 @@ after = ['test_traversal', 'test_gc', 'test_mount'] # test we can detect at least fully clobbered blocks [cases.test_ck_ckmeta_easy] # METHOD=0 => lfsr_fs_ckmeta -# METHOD=1 => lfsr_gc +# METHOD=1 => lfsr_fs_gc # METHOD=2 => lfsr_traversal_read # METHOD=3 => lfsr_mount defines.METHOD = [0, 1, 2, 3] @@ -99,10 +99,10 @@ code = ''' if (METHOD == 0) { lfsr_fs_ckmeta(&lfs) => LFS_ERR_CORRUPT; - // find clobbered blocks with lfsr_gc + // find clobbered blocks with lfsr_fs_gc } else if (METHOD == 1) { #ifdef LFS_GC - lfsr_gc(&lfs) => LFS_ERR_CORRUPT; + lfsr_fs_gc(&lfs) => LFS_ERR_CORRUPT; #else LFS_UNREACHABLE(); #endif @@ -145,7 +145,7 @@ done:; [cases.test_ck_ckdata_easy] # METHOD=0 => lfsr_fs_ckdata -# METHOD=1 => lfsr_gc +# METHOD=1 => lfsr_fs_gc # METHOD=2 => lfsr_traversal_read # METHOD=3 => lfsr_mount defines.METHOD = [0, 1, 2, 3] @@ -237,10 +237,10 @@ code = ''' if (METHOD == 0) { lfsr_fs_ckdata(&lfs) => LFS_ERR_CORRUPT; - // find clobbered blocks with lfsr_gc + // find clobbered blocks with lfsr_fs_gc } else if (METHOD == 1) { #ifdef LFS_GC - lfsr_gc(&lfs) => LFS_ERR_CORRUPT; + lfsr_fs_gc(&lfs) => LFS_ERR_CORRUPT; #else LFS_UNREACHABLE(); #endif diff --git a/tests/test_gc.toml b/tests/test_gc.toml index 8fb6afbc..8c67ccd8 100644 --- a/tests/test_gc.toml +++ b/tests/test_gc.toml @@ -52,7 +52,7 @@ code = ''' // a bit hacky, but this catches infinite loops LFS_ASSERT(i < 2*BLOCK_COUNT); - lfsr_gc(&lfs) => 0; + lfsr_fs_gc(&lfs) => 0; lfsr_fs_stat(&lfs, &fsinfo) => 0; if (!(fsinfo.flags & LFS_I_CANLOOKAHEAD)) { @@ -115,7 +115,7 @@ code = ''' assert(lfs.omdirs != &lfs.gc.t.o.o); // run GC one step - lfsr_gc(&lfs) => 0; + lfsr_fs_gc(&lfs) => 0; assert(lfs.omdirs == &lfs.gc.t.o.o); // mutate the filesystem @@ -129,7 +129,7 @@ code = ''' // run GC until our traversal is done while (lfs.omdirs == &lfs.gc.t.o.o) { - lfsr_gc(&lfs) => 0; + lfsr_fs_gc(&lfs) => 0; } // we should _not_ make progress @@ -204,7 +204,7 @@ code = ''' // a bit hacky, but this catches infinite loops LFS_ASSERT(i < 2*BLOCK_COUNT); - lfsr_gc(&lfs) => 0; + lfsr_fs_gc(&lfs) => 0; lfsr_fs_stat(&lfs, &fsinfo) => 0; if (!(fsinfo.flags & LFS_I_UNCOMPACTED)) { @@ -290,14 +290,14 @@ code = ''' // run GC one traversal + one step while (true) { - lfsr_gc(&lfs) => 0; + lfsr_fs_gc(&lfs) => 0; // internal traversal done? if (lfs.omdirs != &lfs.gc.t.o.o) { break; } } - lfsr_gc(&lfs) => 0; + lfsr_fs_gc(&lfs) => 0; assert(lfs.omdirs == &lfs.gc.t.o.o); // mutate the filesystem @@ -310,7 +310,7 @@ code = ''' // run GC until our traversal is done (twice for compact) while (lfs.omdirs == &lfs.gc.t.o.o) { - lfsr_gc(&lfs) => 0; + lfsr_fs_gc(&lfs) => 0; } // we should _not_ make progress @@ -414,7 +414,7 @@ code = ''' // a bit hacky, but this catches infinite loops LFS_ASSERT(i < 2*BLOCK_COUNT); - lfsr_gc(&lfs) => 0; + lfsr_fs_gc(&lfs) => 0; lfsr_fs_stat(&lfs, &fsinfo) => 0; if (!(fsinfo.flags & LFS_I_INCONSISTENT)) { @@ -606,7 +606,7 @@ code = ''' // run GC one step assert(lfs.omdirs != &lfs.gc.t.o.o); - lfsr_gc(&lfs) => 0; + lfsr_fs_gc(&lfs) => 0; assert(lfs.omdirs == &lfs.gc.t.o.o); // create the rest of the orphans after GC has started @@ -627,7 +627,7 @@ code = ''' // run GC until our traversal is done while (lfs.omdirs == &lfs.gc.t.o.o) { - lfsr_gc(&lfs) => 0; + lfsr_fs_gc(&lfs) => 0; } // we should _not_ make progress @@ -747,12 +747,12 @@ code = ''' } clobbered:; - // running lfsr_gc should eventually find the clobbered block + // running lfsr_fs_gc should eventually find the clobbered block for (lfs_block_t i = 0;; i++) { // a bit hacky, but this catches infinite loops LFS_ASSERT(i < 2*BLOCK_COUNT); - int err = lfsr_gc(&lfs); + int err = lfsr_fs_gc(&lfs); assert(!err || err == LFS_ERR_CORRUPT); // found it if (err == LFS_ERR_CORRUPT) { @@ -848,14 +848,14 @@ code = ''' } clobbered:; - // running lfsr_gc should eventually find the clobbered block + // running lfsr_fs_gc should eventually find the clobbered block // // note LFS_GC_CKDATA implies LFS_GC_CKMETA for (lfs_block_t i = 0;; i++) { // a bit hacky, but this catches infinite loops LFS_ASSERT(i < 2*BLOCK_COUNT); - int err = lfsr_gc(&lfs); + int err = lfsr_fs_gc(&lfs); assert(!err || err == LFS_ERR_CORRUPT); // found it if (err == LFS_ERR_CORRUPT) { @@ -1046,10 +1046,10 @@ done:; ''' # test we can detect fully clobbered blocks after a ck pass, if we call -# lfsr_gc_unck +# lfsr_fs_unck [cases.test_gc_ckmeta_unck] -# AFTER=0 => after running lfsr_gc once -# AFTER=1 => after running lfsr_gc to completion +# AFTER=0 => after running lfsr_fs_gc once +# AFTER=1 => after running lfsr_fs_gc to completion # AFTER=2 => after lfsr_fs_ckmeta # AFTER=3 => after remounting with LFS_M_CKMETA defines.AFTER = [0, 1, 2, 3] @@ -1095,13 +1095,14 @@ code = ''' lfsr_file_close(&lfs, &file) => 0; } - // run lfsr_gc before clobbering, this should not find anything + // run lfsr_fs_gc before clobbering, this should not find + // anything - // run lfsr_gc once + // run lfsr_fs_gc once if (AFTER == 0) { - lfsr_gc(&lfs) => 0; + lfsr_fs_gc(&lfs) => 0; - // run lfsr_gc to completion + // run lfsr_fs_gc to completion } else if (AFTER == 1) { while (true) { struct lfs_fsinfo fsinfo; @@ -1110,7 +1111,7 @@ code = ''' break; } - lfsr_gc(&lfs) => 0; + lfsr_fs_gc(&lfs) => 0; } // run lfsr_fs_ckmeta @@ -1174,14 +1175,14 @@ code = ''' clobbered:; // clear relevant ck flags - lfsr_gc_unck(&lfs, LFS_I_CANCKMETA) => 0; + lfsr_fs_unck(&lfs, LFS_I_CANCKMETA) => 0; - // running lfsr_gc should eventually find the clobbered block + // running lfsr_fs_gc should eventually find the clobbered block for (lfs_block_t i = 0;; i++) { // a bit hacky, but this catches infinite loops LFS_ASSERT(i < 2*BLOCK_COUNT); - int err = lfsr_gc(&lfs); + int err = lfsr_fs_gc(&lfs); assert(!err || err == LFS_ERR_CORRUPT); // found it if (err == LFS_ERR_CORRUPT) { @@ -1195,8 +1196,8 @@ done:; ''' [cases.test_gc_ckdata_unck] -# AFTER=0 => after running lfsr_gc once -# AFTER=1 => after running lfsr_gc to completion +# AFTER=0 => after running lfsr_fs_gc once +# AFTER=1 => after running lfsr_fs_gc to completion # AFTER=2 => after lfsr_fs_ckdata # AFTER=3 => after remounting with LFS_M_CKDATA defines.AFTER = [0, 1, 2] @@ -1242,13 +1243,14 @@ code = ''' lfsr_file_close(&lfs, &file) => 0; } - // run lfsr_gc before clobbering, this should not find anything + // run lfsr_fs_gc before clobbering, this should not find + // anything - // run lfsr_gc once + // run lfsr_fs_gc once if (AFTER == 0) { - lfsr_gc(&lfs) => 0; + lfsr_fs_gc(&lfs) => 0; - // run lfsr_gc to completion + // run lfsr_fs_gc to completion } else if (AFTER == 1) { while (true) { struct lfs_fsinfo fsinfo; @@ -1257,7 +1259,7 @@ code = ''' break; } - lfsr_gc(&lfs) => 0; + lfsr_fs_gc(&lfs) => 0; } // run lfsr_fs_ckdata @@ -1322,16 +1324,16 @@ code = ''' clobbered:; // clear relevant ck flags - lfsr_gc_unck(&lfs, LFS_I_CANCKDATA) => 0; + lfsr_fs_unck(&lfs, LFS_I_CANCKDATA) => 0; - // running lfsr_gc should eventually find the clobbered block + // running lfsr_fs_gc should eventually find the clobbered block // // note LFS_GC_CKDATA implies LFS_GC_CKMETA for (lfs_block_t i = 0;; i++) { // a bit hacky, but this catches infinite loops LFS_ASSERT(i < 2*BLOCK_COUNT); - int err = lfsr_gc(&lfs); + int err = lfsr_fs_gc(&lfs); assert(!err || err == LFS_ERR_CORRUPT); // found it if (err == LFS_ERR_CORRUPT) { @@ -1401,7 +1403,7 @@ code = ''' lfsr_file_close(&lfs, &file) => 0; // gc! - lfsr_gc(&lfs) => 0; + lfsr_fs_gc(&lfs) => 0; } // check the file contents @@ -1414,7 +1416,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -# pseudo-fuzz test that spamming lfsr_gc_unck doesn't break anything +# pseudo-fuzz test that spamming lfsr_fs_unck doesn't break anything [cases.test_gc_mutation_unck] defines.N = 100 defines.MKCONSISTENT = [false, true] @@ -1471,10 +1473,10 @@ code = ''' // choose a random set of flags to unck every cycle uint32_t flags = GC_FLAGS & TEST_PRNG(&prng); - lfsr_gc_unck(&lfs, flags) => 0; + lfsr_fs_unck(&lfs, flags) => 0; // gc! - lfsr_gc(&lfs) => 0; + lfsr_fs_gc(&lfs) => 0; } // check the file contents @@ -1525,11 +1527,11 @@ code = ''' assert(!err || (TEST_PLS && err == LFS_ERR_EXIST)); // gc! - lfsr_gc(&lfs) => 0; + lfsr_fs_gc(&lfs) => 0; // unck to keep things interesting? if (UNCK) { - lfsr_gc_unck(&lfs, LFS_I_CANCKMETA | LFS_I_CANCKDATA) => 0; + lfsr_fs_unck(&lfs, LFS_I_CANCKMETA | LFS_I_CANCKDATA) => 0; } } @@ -1712,11 +1714,11 @@ code = ''' } // gc! - lfsr_gc(&lfs) => 0; + lfsr_fs_gc(&lfs) => 0; // unck to keep things interesting? if (UNCK) { - lfsr_gc_unck(&lfs, LFS_I_CANCKMETA | LFS_I_CANCKDATA) => 0; + lfsr_fs_unck(&lfs, LFS_I_CANCKMETA | LFS_I_CANCKDATA) => 0; } } @@ -1824,11 +1826,11 @@ code = ''' lfsr_file_close(&lfs, &file) => 0; // gc! - lfsr_gc(&lfs) => 0; + lfsr_fs_gc(&lfs) => 0; // unck to keep things interesting? if (UNCK) { - lfsr_gc_unck(&lfs, LFS_I_CANCKMETA | LFS_I_CANCKDATA) => 0; + lfsr_fs_unck(&lfs, LFS_I_CANCKMETA | LFS_I_CANCKDATA) => 0; } } @@ -2032,11 +2034,11 @@ code = ''' } // gc! - lfsr_gc(&lfs) => 0; + lfsr_fs_gc(&lfs) => 0; // unck to keep things interesting? if (UNCK) { - lfsr_gc_unck(&lfs, LFS_I_CANCKMETA | LFS_I_CANCKDATA) => 0; + lfsr_fs_unck(&lfs, LFS_I_CANCKMETA | LFS_I_CANCKDATA) => 0; } } @@ -2205,11 +2207,11 @@ code = ''' } // gc! - lfsr_gc(&lfs) => 0; + lfsr_fs_gc(&lfs) => 0; // unck to keep things interesting? if (UNCK) { - lfsr_gc_unck(&lfs, LFS_I_CANCKMETA | LFS_I_CANCKDATA) => 0; + lfsr_fs_unck(&lfs, LFS_I_CANCKMETA | LFS_I_CANCKDATA) => 0; } } @@ -2544,11 +2546,11 @@ code = ''' } // gc! - lfsr_gc(&lfs) => 0; + lfsr_fs_gc(&lfs) => 0; // unck to keep things interesting? if (UNCK) { - lfsr_gc_unck(&lfs, LFS_I_CANCKMETA | LFS_I_CANCKDATA) => 0; + lfsr_fs_unck(&lfs, LFS_I_CANCKMETA | LFS_I_CANCKDATA) => 0; } } @@ -2971,11 +2973,11 @@ code = ''' } // gc! - lfsr_gc(&lfs) => 0; + lfsr_fs_gc(&lfs) => 0; // unck to keep things interesting? if (UNCK) { - lfsr_gc_unck(&lfs, LFS_I_CANCKMETA | LFS_I_CANCKDATA) => 0; + lfsr_fs_unck(&lfs, LFS_I_CANCKMETA | LFS_I_CANCKDATA) => 0; } }