From eced94368592c638b3b83b5fc1e2ccaca6d362ea Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Wed, 17 Jul 2024 01:56:12 -0500 Subject: [PATCH] Changed gc_steps into a runtime parameter, better dedup mount gc So instead of configuring gc_steps at mount time (or eventually compile time), lfsr_fs_gc now takes a steps parameter that controls how much gc work to attempt: int lfsr_fs_gc(lfs_t *lfs, lfs_soff_t steps, uint32_t flags); This API was needed internally to better deduplicate on-mount gc, and I figured it might also be useful for users to be able to easily change gc_steps per lfsr_fs_gc call. I realize this could also be accomplished with the theoretical lfsr_fs_gccfg, but it's a bit easier to not need a struct every call. Most likely, depending on project/system, users will always call lfsr_fs_gc with either 1 (minimal work) or -1 (maximal work), or, worst case, can define a system-wide GC_STEPS somewhere. --- Deduplicating on-mount gc work better saved some code, though it's worth noting this could have been done internally and not exposed to users: code stack before: 36476 2680 (+0.0%) after: 36316 (-0.4%) 2680 (+0.0%) --- lfs.c | 205 ++++++++++++++++------------------------- lfs.h | 23 ++--- runners/bench_runner.h | 2 - runners/test_runner.h | 2 - tests/test_gc.toml | 153 +++++++++++++++--------------- 5 files changed, 163 insertions(+), 222 deletions(-) diff --git a/lfs.c b/lfs.c index 2cc867b8..394ff9dd 100644 --- a/lfs.c +++ b/lfs.c @@ -12623,13 +12623,13 @@ int lfsr_mount(lfs_t *lfs, uint32_t flags, int err = lfs_init(lfs, // strip out the one-time traversal flags - flags - & ~LFS_M_MTREEONLY - & ~LFS_M_MKCONSISTENT - & ~LFS_M_LOOKAHEAD - & ~LFS_M_COMPACT - & ~LFS_M_CKMETA - & ~LFS_M_CKDATA, + flags & ~( + LFS_M_MTREEONLY + | LFS_M_MKCONSISTENT + | LFS_M_LOOKAHEAD + | LFS_M_COMPACT + | LFS_M_CKMETA + | LFS_M_CKDATA), cfg); if (err) { return err; @@ -12655,73 +12655,17 @@ int lfsr_mount(lfs_t *lfs, uint32_t flags, lfsr_mtree_weight_(&lfs->mtree) >> lfs->mdir_bits, 1 << lfs->mdir_bits); - // fix pending grms if requested - if (lfsr_t_ismkconsistent(flags) - && lfsr_grm_count(lfs) > 0) { - if (lfsr_grm_count(lfs) == 2) { - LFS_DEBUG("Fixing grm %"PRId32".%"PRId32" %"PRId32".%"PRId32, - lfsr_mid_bid(lfs, lfs->grm.mids[0]) >> lfs->mdir_bits, - lfsr_mid_rid(lfs, lfs->grm.mids[0]), - lfsr_mid_bid(lfs, lfs->grm.mids[1]) >> lfs->mdir_bits, - lfsr_mid_rid(lfs, lfs->grm.mids[1])); - } else if (lfsr_grm_count(lfs) == 1) { - LFS_DEBUG("Fixing grm %"PRId32".%"PRId32, - lfsr_mid_bid(lfs, lfs->grm.mids[0]) >> lfs->mdir_bits, - lfsr_mid_rid(lfs, lfs->grm.mids[0])); - } - - err = lfsr_fs_fixgrm(lfs); - if (err) { - goto failed; - } - } - - // run gc until all requested mount work is done - bool mutated = true; - while ((lfsr_t_ismkconsistent(flags) - && lfsr_f_hasorphans(lfs->flags)) - || (lfsr_t_islookahead(flags) - && lfsr_fs_canlookahead(lfs)) - || (lfsr_t_iscompact(flags) - && lfsr_i_isuncompacted(lfs->flags)) - || (lfsr_t_isckmeta(flags) - && mutated) - || (lfsr_t_isckdata(flags) - && mutated)) { - - // checkpoint the allocator to maximize any lookahead scans - lfs_alloc_ckpoint(lfs); - - // do we really need a full traversal? - uint32_t flags_ = flags; - if (!((lfsr_t_islookahead(flags) - && lfsr_fs_canlookahead(lfs)) - || (lfsr_t_iscompact(flags) - && lfsr_i_isuncompacted(lfs->flags)) - || (lfsr_t_isckmeta(flags) - && mutated) - || (lfsr_t_isckdata(flags) - && mutated))) { - flags_ |= LFS_GC_MTREEONLY; - } - - lfsr_traversal_t t = LFSR_TRAVERSAL(flags_); - lfsr_omdir_open(lfs, &t.o.o); - - while (true) { - err = lfsr_mtree_gc(lfs, &t, - NULL, NULL); - if (err) { - lfsr_omdir_close(lfs, &t.o.o); - if (err == LFS_ERR_NOENT) { - break; - } - goto failed; - } - } - - // mutated? we need another pass for ckmeta/ckdata - mutated = lfsr_f_ismutated(t.o.o.flags); + // run gc if requested + err = lfsr_fs_gc(lfs, -1, + flags & ( + LFS_M_MTREEONLY + | LFS_M_MKCONSISTENT + | LFS_M_LOOKAHEAD + | LFS_M_COMPACT + | LFS_M_CKMETA + | LFS_M_CKDATA)); + if (err) { + goto failed; } return 0; @@ -13078,10 +13022,7 @@ int lfsr_fs_ckdata(lfs_t *lfs) { } // perform any pending janitorial work -int lfsr_fs_gc(lfs_t *lfs, uint32_t flags) { - // some flags don't make sense when only traversing the mtree - LFS_ASSERT(!lfsr_t_ismtreeonly(flags) || !lfsr_t_islookahead(flags)); - LFS_ASSERT(!lfsr_t_ismtreeonly(flags) || !lfsr_t_isckdata(flags)); +int lfsr_fs_gc(lfs_t *lfs, lfs_soff_t steps, uint32_t flags) { // unknown flags? LFS_ASSERT((flags & ~LFS_GC_MTREEONLY @@ -13090,6 +13031,9 @@ int lfsr_fs_gc(lfs_t *lfs, uint32_t flags) { & ~LFS_GC_COMPACT & ~LFS_GC_CKMETA & ~LFS_GC_CKDATA) == 0); + // some flags don't make sense when only traversing the mtree + LFS_ASSERT(!lfsr_t_ismtreeonly(flags) || !lfsr_t_islookahead(flags)); + LFS_ASSERT(!lfsr_t_ismtreeonly(flags) || !lfsr_t_isckdata(flags)); // fix pending grms if requested if (lfsr_t_ismkconsistent(flags) @@ -13112,57 +13056,70 @@ int lfsr_fs_gc(lfs_t *lfs, uint32_t flags) { } } - // do we need to do anything? - if (!((lfsr_t_ismkconsistent(flags) - && lfsr_f_hasorphans(lfs->flags)) - || (lfsr_t_islookahead(flags) - && lfsr_fs_canlookahead(lfs)) - || (lfsr_t_iscompact(flags) - && lfsr_i_isuncompacted(lfs->flags)) - || lfsr_t_isckmeta(flags) - || lfsr_t_isckdata(flags))) { - return 0; - } + // do we have any pending work? + bool cked = false; + while ((lfs_off_t)steps > 0 + && ((lfsr_t_ismkconsistent(flags) + && lfsr_f_hasorphans(lfs->flags)) + || (lfsr_t_islookahead(flags) + && lfsr_fs_canlookahead(lfs)) + || (lfsr_t_iscompact(flags) + && lfsr_i_isuncompacted(lfs->flags)) + || (lfsr_t_isckmeta(flags) + && !cked) + || (lfsr_t_isckdata(flags) + && !cked))) { + // checkpoint the allocator to maximize any lookahead scans + lfs_alloc_ckpoint(lfs); - // checkpoint the allocator to maximize any lookahead scans - lfs_alloc_ckpoint(lfs); + // existing traversal? + if (lfsr_omdir_isopen(lfs, &lfs->gc.o.o)) { + // note that we mask flags (except mtreeonly)! if you change flags + // mid-traversal, the result is equivalent to the worst-case set + // of flags + lfs->gc.o.o.flags &= ( + ~LFS_GC_MKCONSISTENT + & ~LFS_GC_LOOKAHEAD + & ~LFS_GC_COMPACT + & ~LFS_GC_CKMETA + & ~LFS_GC_CKDATA + ) | flags; + // start a new traversal + } else { + lfs->gc = LFSR_TRAVERSAL( + flags + // do we really need a full traversal? + | ((!((lfsr_t_islookahead(flags) + && lfsr_fs_canlookahead(lfs)) + || (lfsr_t_iscompact(flags) + && lfsr_i_isuncompacted(lfs->flags)) + || (lfsr_t_isckmeta(flags) + && !cked) + || (lfsr_t_isckdata(flags) + && !cked))) + ? LFS_T_MTREEONLY + : 0)); + lfsr_omdir_open(lfs, &lfs->gc.o.o); + } - // do we really need a full traversal? - if (!((lfsr_t_islookahead(flags) - && lfsr_fs_canlookahead(lfs)) - || (lfsr_t_iscompact(flags) - && lfsr_i_isuncompacted(lfs->flags)) - || lfsr_t_isckmeta(flags) - || lfsr_t_isckdata(flags))) { - flags |= LFS_GC_MTREEONLY; - } - - // existing traversal? - if (lfsr_omdir_isopen(lfs, &lfs->gc.o.o)) { - // note that we mask flags (except mtreeonly)! if you change flags - // mid-traversal, the result is equivalent to the worst-case set - // of flags - lfs->gc.o.o.flags &= ( - ~LFS_GC_MKCONSISTENT - & ~LFS_GC_LOOKAHEAD - & ~LFS_GC_COMPACT - & ~LFS_GC_CKMETA - & ~LFS_GC_CKDATA - ) | flags; - // start a new traversal - } else { - lfs->gc = LFSR_TRAVERSAL(flags); - lfsr_omdir_open(lfs, &lfs->gc.o.o); - } - - for (uint32_t i = 0; - i < (lfs->cfg->gc_steps ? (uint32_t)lfs->cfg->gc_steps : 1); - i++) { + // progress gc int err = lfsr_mtree_gc(lfs, &lfs->gc, NULL, NULL); - if (err) { + if (err && err != LFS_ERR_NOENT) { + return err; + } + + if (err == LFS_ERR_NOENT) { lfsr_omdir_close(lfs, &lfs->gc.o.o); - return (err == LFS_ERR_NOENT) ? 0 : err; + + // consider our filesystem checked if we completed a traversal + // with no mutation + cked = !lfsr_f_ismutated(lfs->gc.o.o.flags); + } + + // decrement steps + if (steps > 0) { + steps -= 1; } } diff --git a/lfs.h b/lfs.h index c055f52e..ed5ed342 100644 --- a/lfs.h +++ b/lfs.h @@ -310,17 +310,6 @@ struct lfs_config { // can track 8 blocks. lfs_size_t lookahead_size; - // How many gc steps to perform on each lfsr_fs_gc call. - // - // Each gc step progresses janitorial work by ~1 block (this is equivalent - // to lfsr_traversal_read). More steps per call may make more progress if - // interleaving with other work. - // - // 0 defaults to 1 step, and -1 will perform a full traversal every call, - // though multiple traversals may still be needed to complete all - // janitorial work. - int32_t gc_steps; - // Threshold for metadata compaction during gc in bytes. Metadata logs // that exceed this threshold will be compacted during gc operations. // Defaults to ~88% block_size when zero, though this default may change @@ -1183,14 +1172,20 @@ int lfsr_fs_ckdata(lfs_t *lfs); #ifndef LFS_READONLY // Perform any janitorial work that may be pending. // -// The exact janitorial work depends on the provided flags. Note multiple -// calls may be required to complete all janitorial work. +// The exact janitorial work depends on the provided flags. +// +// The steps parameter controls how many gc steps to progress before +// returning, with each gc step being ~1 block of work. More steps per call +// will make more progress if interleaved with other filesystem writes, 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. // // 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, uint32_t flags); +int lfsr_fs_gc(lfs_t *lfs, lfs_soff_t steps, uint32_t flags); #endif #ifndef LFS_READONLY diff --git a/runners/bench_runner.h b/runners/bench_runner.h index 3f157567..3a01eec4 100644 --- a/runners/bench_runner.h +++ b/runners/bench_runner.h @@ -120,7 +120,6 @@ void bench_permutation(size_t i, uint32_t *buffer, size_t size); BENCH_DEFINE(PCACHE_SIZE, LFS_MAX(16, PROG_SIZE) ) \ BENCH_DEFINE(FILE_BUFFER_SIZE, 16 ) \ BENCH_DEFINE(LOOKAHEAD_SIZE, 16 ) \ - BENCH_DEFINE(GC_STEPS, 0 ) \ BENCH_DEFINE(GC_COMPACT_THRESH, 0 ) \ BENCH_DEFINE(INLINE_SIZE, BLOCK_SIZE/4 ) \ BENCH_DEFINE(SHRUB_SIZE, INLINE_SIZE ) \ @@ -150,7 +149,6 @@ void bench_permutation(size_t i, uint32_t *buffer, size_t size); .pcache_size = PCACHE_SIZE, \ .file_buffer_size = FILE_BUFFER_SIZE, \ .lookahead_size = LOOKAHEAD_SIZE, \ - .gc_steps = GC_STEPS, \ .gc_compact_thresh = GC_COMPACT_THRESH, \ .inline_size = INLINE_SIZE, \ .shrub_size = SHRUB_SIZE, \ diff --git a/runners/test_runner.h b/runners/test_runner.h index c1ff6495..4835d2ea 100644 --- a/runners/test_runner.h +++ b/runners/test_runner.h @@ -105,7 +105,6 @@ void test_permutation(size_t i, uint32_t *buffer, size_t size); TEST_DEFINE(PCACHE_SIZE, LFS_MAX(16, PROG_SIZE) ) \ TEST_DEFINE(FILE_BUFFER_SIZE, 16 ) \ TEST_DEFINE(LOOKAHEAD_SIZE, 16 ) \ - TEST_DEFINE(GC_STEPS, 0 ) \ TEST_DEFINE(GC_COMPACT_THRESH, 0 ) \ TEST_DEFINE(INLINE_SIZE, BLOCK_SIZE/4 ) \ TEST_DEFINE(SHRUB_SIZE, INLINE_SIZE ) \ @@ -135,7 +134,6 @@ void test_permutation(size_t i, uint32_t *buffer, size_t size); .pcache_size = PCACHE_SIZE, \ .file_buffer_size = FILE_BUFFER_SIZE, \ .lookahead_size = LOOKAHEAD_SIZE, \ - .gc_steps = GC_STEPS, \ .gc_compact_thresh = GC_COMPACT_THRESH, \ .inline_size = INLINE_SIZE, \ .shrub_size = SHRUB_SIZE, \ diff --git a/tests/test_gc.toml b/tests/test_gc.toml index 3f423ce6..8e6270bf 100644 --- a/tests/test_gc.toml +++ b/tests/test_gc.toml @@ -41,23 +41,22 @@ code = ''' assert(fsinfo.flags & LFS_I_CANLOOKAHEAD); assert(lfs.omdirs != &lfs.gc.o.o); - // run GC until our traversal is done - while (true) { - lfsr_fs_gc(&lfs, + // 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_fs_gc(&lfs, GC_STEPS, LFS_GC_LOOKAHEAD | ((CKMETA) ? LFS_GC_CKMETA : 0) | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; - // internal traversal done? - if (lfs.omdirs != &lfs.gc.o.o) { + lfsr_fs_stat(&lfs, &fsinfo) => 0; + if (!(fsinfo.flags & LFS_I_CANLOOKAHEAD)) { break; } } - // 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]; @@ -106,7 +105,7 @@ code = ''' assert(lfs.omdirs != &lfs.gc.o.o); // run GC one step - lfsr_fs_gc(&lfs, + lfsr_fs_gc(&lfs, GC_STEPS, LFS_GC_LOOKAHEAD | ((CKMETA) ? LFS_GC_CKMETA : 0) | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; @@ -123,7 +122,7 @@ code = ''' // run GC until our traversal is done while (lfs.omdirs == &lfs.gc.o.o) { - lfsr_fs_gc(&lfs, + lfsr_fs_gc(&lfs, GC_STEPS, LFS_GC_LOOKAHEAD | ((CKMETA) ? LFS_GC_CKMETA : 0) | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; @@ -183,14 +182,14 @@ code = ''' assert(lfs.omdirs != &lfs.gc.o.o); // run GC one step - lfsr_fs_gc(&lfs, + lfsr_fs_gc(&lfs, GC_STEPS, ((CKMETA) ? LFS_GC_CKMETA : 0) | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; assert(lfs.omdirs == &lfs.gc.o.o); // change flags and run GC until our traversal is done while (lfs.omdirs == &lfs.gc.o.o) { - lfsr_fs_gc(&lfs, + lfsr_fs_gc(&lfs, GC_STEPS, LFS_GC_LOOKAHEAD | ((CKMETA) ? LFS_GC_CKMETA : 0) | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; @@ -250,7 +249,7 @@ code = ''' assert(lfs.omdirs != &lfs.gc.o.o); // run GC one step - lfsr_fs_gc(&lfs, + lfsr_fs_gc(&lfs, GC_STEPS, LFS_GC_LOOKAHEAD | ((CKMETA) ? LFS_GC_CKMETA : 0) | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; @@ -258,7 +257,7 @@ code = ''' // change flags and run GC until our traversal is done while (lfs.omdirs == &lfs.gc.o.o) { - lfsr_fs_gc(&lfs, + lfsr_fs_gc(&lfs, GC_STEPS, ((CKMETA) ? LFS_GC_CKMETA : 0) | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; } @@ -323,29 +322,26 @@ code = ''' assert(fsinfo.flags & LFS_I_UNCOMPACTED); assert(lfs.omdirs != &lfs.gc.o.o); - // run GC until our traversal is done (twice for compact) - for (int i = 0; i < 2; i++) { - while (true) { - lfsr_fs_gc(&lfs, - LFS_GC_COMPACT - | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) - | ((CKMETA) ? LFS_GC_CKMETA : 0) - | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 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); - // internal traversal done? - if (lfs.omdirs != &lfs.gc.o.o) { - break; - } + lfsr_fs_gc(&lfs, GC_STEPS, + LFS_GC_COMPACT + | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) + | ((CKMETA) ? LFS_GC_CKMETA : 0) + | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; + + lfsr_fs_stat(&lfs, &fsinfo) => 0; + if (!(fsinfo.flags & LFS_I_UNCOMPACTED)) { + break; } } // mdir should have been compacted assert((file.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); - // we should have made 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? @@ -413,7 +409,7 @@ code = ''' // run GC one traversal + one step while (true) { - lfsr_fs_gc(&lfs, + lfsr_fs_gc(&lfs, GC_STEPS, LFS_GC_COMPACT | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) | ((CKMETA) ? LFS_GC_CKMETA : 0) @@ -424,7 +420,7 @@ code = ''' break; } } - lfsr_fs_gc(&lfs, + lfsr_fs_gc(&lfs, GC_STEPS, LFS_GC_COMPACT | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) | ((CKMETA) ? LFS_GC_CKMETA : 0) @@ -441,7 +437,7 @@ code = ''' // run GC until our traversal is done (twice for compact) while (lfs.omdirs == &lfs.gc.o.o) { - lfsr_fs_gc(&lfs, + lfsr_fs_gc(&lfs, GC_STEPS, LFS_GC_COMPACT | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) | ((CKMETA) ? LFS_GC_CKMETA : 0) @@ -521,7 +517,7 @@ code = ''' // run GC one traversal + one step while (true) { - lfsr_fs_gc(&lfs, + lfsr_fs_gc(&lfs, GC_STEPS, ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) | ((CKMETA) ? LFS_GC_CKMETA : 0) | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; @@ -531,7 +527,7 @@ code = ''' break; } } - lfsr_fs_gc(&lfs, + lfsr_fs_gc(&lfs, GC_STEPS, ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) | ((CKMETA) ? LFS_GC_CKMETA : 0) | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; @@ -539,7 +535,7 @@ code = ''' // change flags and run GC until our traversal is done (twice for compact) while (lfs.omdirs == &lfs.gc.o.o) { - lfsr_fs_gc(&lfs, + lfsr_fs_gc(&lfs, GC_STEPS, LFS_GC_COMPACT | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) | ((CKMETA) ? LFS_GC_CKMETA : 0) @@ -619,7 +615,7 @@ code = ''' // run GC one traversal + one step while (true) { - lfsr_fs_gc(&lfs, + lfsr_fs_gc(&lfs, GC_STEPS, LFS_GC_COMPACT | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) | ((CKMETA) ? LFS_GC_CKMETA : 0) @@ -630,7 +626,7 @@ code = ''' break; } } - lfsr_fs_gc(&lfs, + lfsr_fs_gc(&lfs, GC_STEPS, LFS_GC_COMPACT | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) | ((CKMETA) ? LFS_GC_CKMETA : 0) @@ -639,7 +635,7 @@ code = ''' // change flags and run GC until our traversal is done (twice for compact) while (lfs.omdirs == &lfs.gc.o.o) { - lfsr_fs_gc(&lfs, + lfsr_fs_gc(&lfs, GC_STEPS, ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) | ((CKMETA) ? LFS_GC_CKMETA : 0) | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; @@ -733,25 +729,24 @@ code = ''' assert(fsinfo.flags & LFS_I_INCONSISTENT); assert(lfs.omdirs != &lfs.gc.o.o); - // run GC until our traversal is done - while (true) { - lfsr_fs_gc(&lfs, + // 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_fs_gc(&lfs, GC_STEPS, LFS_GC_MKCONSISTENT | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) | ((COMPACT) ? LFS_GC_COMPACT : 0) | ((CKMETA) ? LFS_GC_CKMETA : 0) | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; - // internal traversal done? - if (lfs.omdirs != &lfs.gc.o.o) { + lfsr_fs_stat(&lfs, &fsinfo) => 0; + if (!(fsinfo.flags & LFS_I_INCONSISTENT)) { break; } } - // we should have made 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? @@ -925,7 +920,7 @@ code = ''' // run GC one step assert(lfs.omdirs != &lfs.gc.o.o); - lfsr_fs_gc(&lfs, + lfsr_fs_gc(&lfs, GC_STEPS, LFS_GC_MKCONSISTENT | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) | ((COMPACT) ? LFS_GC_COMPACT : 0) @@ -951,7 +946,7 @@ code = ''' // run GC until our traversal is done while (lfs.omdirs == &lfs.gc.o.o) { - lfsr_fs_gc(&lfs, + lfsr_fs_gc(&lfs, GC_STEPS, LFS_GC_MKCONSISTENT | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) | ((COMPACT) ? LFS_GC_COMPACT : 0) @@ -1057,7 +1052,7 @@ code = ''' assert(lfs.omdirs != &lfs.gc.o.o); // run GC one step - lfsr_fs_gc(&lfs, + lfsr_fs_gc(&lfs, GC_STEPS, ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) | ((COMPACT) ? LFS_GC_COMPACT : 0) | ((CKMETA) ? LFS_GC_CKMETA : 0) @@ -1066,7 +1061,7 @@ code = ''' // change flags and run GC until our traversal is done while (lfs.omdirs == &lfs.gc.o.o) { - lfsr_fs_gc(&lfs, + lfsr_fs_gc(&lfs, GC_STEPS, LFS_GC_MKCONSISTENT | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) | ((COMPACT) ? LFS_GC_COMPACT : 0) @@ -1171,7 +1166,7 @@ code = ''' assert(lfs.omdirs != &lfs.gc.o.o); // run GC one step - lfsr_fs_gc(&lfs, + lfsr_fs_gc(&lfs, GC_STEPS, LFS_GC_MKCONSISTENT | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) | ((COMPACT) ? LFS_GC_COMPACT : 0) @@ -1181,7 +1176,7 @@ code = ''' // change flags and run GC until our traversal is done while (lfs.omdirs == &lfs.gc.o.o) { - lfsr_fs_gc(&lfs, + lfsr_fs_gc(&lfs, GC_STEPS, ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) | ((COMPACT) ? LFS_GC_COMPACT : 0) | ((CKMETA) ? LFS_GC_CKMETA : 0) @@ -1302,17 +1297,16 @@ code = ''' clobbered:; // running lfsr_fs_gc should eventually find the clobbered block - while (true) { - int err = lfsr_fs_gc(&lfs, 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_fs_gc(&lfs, GC_STEPS, LFS_GC_CKMETA); assert(!err || err == LFS_ERR_CORRUPT); // found it if (err == LFS_ERR_CORRUPT) { break; } - - // we should find the clobbered block before finishing the - // traversal - assert(lfs.omdirs == &lfs.gc.o.o); } lfsr_unmount(&lfs) => 0; @@ -1404,17 +1398,16 @@ code = ''' // running lfsr_fs_gc should eventually find the clobbered block // // note LFS_GC_CKDATA implies LFS_GC_CKMETA - while (true) { - int err = lfsr_fs_gc(&lfs, LFS_GC_CKDATA); + for (lfs_block_t i = 0;; i++) { + // a bit hacky, but this catches infinite loops + LFS_ASSERT(i < 2*BLOCK_COUNT); + + int err = lfsr_fs_gc(&lfs, GC_STEPS, LFS_GC_CKDATA); assert(!err || err == LFS_ERR_CORRUPT); // found it if (err == LFS_ERR_CORRUPT) { break; } - - // we should find the clobbered block before finishing the - // traversal - assert(lfs.omdirs == &lfs.gc.o.o); } lfsr_unmount(&lfs) => 0; @@ -1603,7 +1596,7 @@ done:; # pseudo-fuzz test that dirtying still works with the GC API [cases.test_gc_mutation] defines.GC_STEPS = [-1, 1, 2, 10, 100, 1000] -defines.STEPS = 100 +defines.N = 100 defines.MKCONSISTENT = [false, true] defines.LOOKAHEAD = [false, true] defines.COMPACT = [false, true] @@ -1637,7 +1630,7 @@ code = ''' lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; lfsr_file_close(&lfs, &file) => 0; - for (uint32_t i = 0; i < STEPS; i++) { + 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; @@ -1648,7 +1641,7 @@ code = ''' lfsr_file_close(&lfs, &file) => 0; // gc! - lfsr_fs_gc(&lfs, + lfsr_fs_gc(&lfs, GC_STEPS, ((MKCONSISTENT) ? LFS_GC_MKCONSISTENT : 0) | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) | ((COMPACT) ? LFS_GC_COMPACT : 0) @@ -1669,7 +1662,7 @@ code = ''' # pseudo-fuzz test that adding/removing flags doesn't break anything [cases.test_gc_changing_flags] defines.GC_STEPS = [-1, 1, 2, 10, 100, 1000] -defines.STEPS = 100 +defines.N = 100 defines.MKCONSISTENT = [false, true] defines.LOOKAHEAD = [false, true] defines.COMPACT = [false, true] @@ -1703,7 +1696,7 @@ code = ''' lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; lfsr_file_close(&lfs, &file) => 0; - for (uint32_t i = 0; i < STEPS; i++) { + 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; @@ -1723,7 +1716,7 @@ code = ''' ) & TEST_PRNG(&prng); // gc! - lfsr_fs_gc(&lfs, flags) => 0; + lfsr_fs_gc(&lfs, GC_STEPS, flags) => 0; } // check the file contents @@ -1765,7 +1758,7 @@ code = ''' assert(!err || (TEST_PLS && err == LFS_ERR_EXIST)); // gc! - lfsr_fs_gc(&lfs, + lfsr_fs_gc(&lfs, GC_STEPS, ((MKCONSISTENT) ? LFS_GC_MKCONSISTENT : 0) | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) | ((COMPACT) ? LFS_GC_COMPACT : 0) @@ -1943,7 +1936,7 @@ code = ''' } // gc! - lfsr_fs_gc(&lfs, + lfsr_fs_gc(&lfs, GC_STEPS, ((MKCONSISTENT) ? LFS_GC_MKCONSISTENT : 0) | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) | ((COMPACT) ? LFS_GC_COMPACT : 0) @@ -2046,7 +2039,7 @@ code = ''' lfsr_file_close(&lfs, &file) => 0; // gc! - lfsr_fs_gc(&lfs, + lfsr_fs_gc(&lfs, GC_STEPS, ((MKCONSISTENT) ? LFS_GC_MKCONSISTENT : 0) | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) | ((COMPACT) ? LFS_GC_COMPACT : 0) @@ -2245,7 +2238,7 @@ code = ''' } // gc! - lfsr_fs_gc(&lfs, + lfsr_fs_gc(&lfs, GC_STEPS, ((MKCONSISTENT) ? LFS_GC_MKCONSISTENT : 0) | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) | ((COMPACT) ? LFS_GC_COMPACT : 0) @@ -2409,7 +2402,7 @@ code = ''' } // gc! - lfsr_fs_gc(&lfs, + lfsr_fs_gc(&lfs, GC_STEPS, ((MKCONSISTENT) ? LFS_GC_MKCONSISTENT : 0) | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) | ((COMPACT) ? LFS_GC_COMPACT : 0) @@ -2739,7 +2732,7 @@ code = ''' } // gc! - lfsr_fs_gc(&lfs, + lfsr_fs_gc(&lfs, GC_STEPS, ((MKCONSISTENT) ? LFS_GC_MKCONSISTENT : 0) | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) | ((COMPACT) ? LFS_GC_COMPACT : 0) @@ -3157,7 +3150,7 @@ code = ''' } // gc! - lfsr_fs_gc(&lfs, + lfsr_fs_gc(&lfs, GC_STEPS, ((MKCONSISTENT) ? LFS_GC_MKCONSISTENT : 0) | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) | ((COMPACT) ? LFS_GC_COMPACT : 0)