diff --git a/lfs3.c b/lfs3.c index f49e6936..3ece7320 100644 --- a/lfs3.c +++ b/lfs3.c @@ -1915,10 +1915,9 @@ enum lfs3_from { LFS3_FROM_LEB128 = 5, LFS3_FROM_LLEB128 = 6, LFS3_FROM_NAME = 7, + LFS3_FROM_BRANCH = 8, - LFS3_FROM_ECKSUM = 8, - LFS3_FROM_BRANCH = 9, - + LFS3_FROM_ECKSUM = 9, LFS3_FROM_BPTR = 10, LFS3_FROM_BTREE = 11, LFS3_FROM_SHRUB = 12, @@ -2474,13 +2473,50 @@ static int lfs3_bptr_ck(lfs3_t *lfs3, const lfs3_bptr_t *bptr) { /// Erased-state checksum stuff /// -// erased-state checksum #ifndef LFS3_RDONLY -typedef struct lfs3_ecksum { - // cksize=-1 indicates no ecksum - lfs3_ssize_t cksize; - uint32_t cksum; -} lfs3_ecksum_t; +static int lfs3_ecksum_eck(lfs3_t *lfs3, lfs3_ecksum_t *ecksum, + lfs3_block_t block, lfs3_off_t off) { + // keep track of prog size + ecksum->cksize = lfs3->cfg->prog_size; + // checksum erased state + ecksum->cksum = 0; + return lfs3_bd_cksum(lfs3, + block, off, 0, + lfs3->cfg->prog_size, + &ecksum->cksum); +} +#endif + +#ifndef LFS3_RDONLY +static int lfs3_ecksum_ck(lfs3_t *lfs3, const lfs3_ecksum_t *ecksum, + lfs3_block_t block, lfs3_off_t off) { + uint32_t cksum_ = 0; + int err = lfs3_bd_cksum(lfs3, + block, off, 0, + ecksum->cksize, + &cksum_); + if (err) { + return err; + } + + return (cksum_ != ecksum->cksum) ? LFS3_ERR_CORRUPT : 0; +} +#endif + +#ifndef LFS3_RDONLY +static inline int lfs3_ecksum_cmp( + const lfs3_ecksum_t *a, + const lfs3_ecksum_t *b) { + // accept NULL as cksize=-1 + if (((a) ? a->cksize : -1) != ((b) ? b->cksize : -1)) { + return ((a) ? a->cksize : -1) - ((b) ? b->cksize : -1); + // only cmp cksum if cksize!=-1 + } else if (((a) ? a->cksize : -1) != -1) { + return a->cksum - b->cksum; + } else { + return 0; + } +} #endif // erased-state checksum on-disk encoding @@ -2623,17 +2659,8 @@ static int lfs3_rbyd_ckecksum(lfs3_t *lfs3, const lfs3_rbyd_t *rbyd, // check that erased-state matches our checksum, if this fails // most likely a write was interrupted - uint32_t ecksum_ = 0; - err = lfs3_bd_cksum(lfs3, - rbyd->blocks[0], lfs3_rbyd_eoff(rbyd), 0, - ecksum->cksize, - &ecksum_); - if (err) { - return err; - } - - // found erased-state? - return (ecksum_ == ecksum->cksum) ? 0 : LFS3_ERR_CORRUPT; + return lfs3_ecksum_ck(lfs3, ecksum, + rbyd->blocks[0], lfs3_rbyd_eoff(rbyd)); } #endif @@ -3323,16 +3350,15 @@ static int lfs3_rbyd_appendrattr_(lfs3_t *lfs3, lfs3_rbyd_t *rbyd, lfs3_data_t datas[2]; uint8_t buf[LFS3_LEB128_DSIZE]; } name; - - struct { - lfs3_data_t data; - uint8_t buf[LFS3_ECKSUM_DSIZE]; - } ecksum; struct { lfs3_data_t data; uint8_t buf[LFS3_BRANCH_DSIZE]; } branch; + struct { + lfs3_data_t data; + uint8_t buf[LFS3_ECKSUM_DSIZE]; + } ecksum; struct { lfs3_data_t data; uint8_t buf[LFS3_BPTR_DSIZE]; @@ -3418,16 +3444,6 @@ static int lfs3_rbyd_appendrattr_(lfs3_t *lfs3, lfs3_rbyd_t *rbyd, datas = ctx.u.name.datas; data_count = 2; - // ecksum? - } else if (from == LFS3_FROM_ECKSUM) { - ctx.u.ecksum.data = lfs3_data_fromecksum( - &(const lfs3_ecksum_t){ - .cksize = args[0], - .cksum = args[1]}, - ctx.u.ecksum.buf); - datas = &ctx.u.ecksum.data; - data_count = 1; - // branch? } else if (from == LFS3_FROM_BRANCH) { ctx.u.branch.data = lfs3_data_frombranch( @@ -3439,6 +3455,14 @@ static int lfs3_rbyd_appendrattr_(lfs3_t *lfs3, lfs3_rbyd_t *rbyd, datas = &ctx.u.branch.data; data_count = 1; + // ecksum? + } else if (from == LFS3_FROM_ECKSUM) { + ctx.u.ecksum.data = lfs3_data_fromecksum( + (const lfs3_ecksum_t*)args[0], + ctx.u.ecksum.buf); + datas = &ctx.u.ecksum.data; + data_count = 1; + // bptr? } else if (from == LFS3_FROM_BPTR) { ctx.u.bptr.data = lfs3_data_frombptr( @@ -4351,11 +4375,9 @@ static int lfs3_rbyd_appendcksum_(lfs3_t *lfs3, lfs3_rbyd_t *rbyd, perturb = ((e >> 7) == lfs3_parity(cksum)); // calculate the erased-state checksum - uint32_t ecksum = 0; - err = lfs3_bd_cksum(lfs3, - rbyd->blocks[0], off_, lfs3->cfg->prog_size, - lfs3->cfg->prog_size, - &ecksum); + lfs3_ecksum_t ecksum; + err = lfs3_ecksum_eck(lfs3, &ecksum, + rbyd->blocks[0], off_); if (err && err != LFS3_ERR_CORRUPT) { return err; } @@ -4363,8 +4385,7 @@ static int lfs3_rbyd_appendcksum_(lfs3_t *lfs3, lfs3_rbyd_t *rbyd, err = lfs3_rbyd_appendrattr_(lfs3, rbyd, LFS3_TAG_ECKSUM, 0, LFS3_FROM_ECKSUM, 0, LFS3_RATTRS( - LFS3_RATTR_ARG(lfs3->cfg->prog_size), - LFS3_RATTR_ARG(ecksum))); + LFS3_RATTR_ARG(&ecksum))); if (err) { return err; } @@ -7226,6 +7247,17 @@ static inline bool lfs3_t_islookahead(uint32_t flags) { #endif } +static inline bool lfs3_t_ispreerase(uint32_t flags) { + (void)flags; + #if !defined(LFS3_RDONLY) \ + && defined(LFS3_GBMAP) \ + && !defined(LFS3_NO_PREERASE) + return flags & LFS3_T_PREERASE; + #else + return false; + #endif +} + static inline bool lfs3_t_compact(uint32_t flags) { (void)flags; #ifndef LFS3_RDONLY @@ -10481,7 +10513,14 @@ eot:; static void lfs3_gbmap_init(lfs3_gbmap_t *gbmap) { gbmap->window = 0; gbmap->known = 0; + #ifndef LFS3_RDONLY gbmap->free = 0; + #endif + #if !defined(LFS3_RDONLY) && !defined(LFS3_NO_PREERASE) + gbmap->ecksum.cksize = -1; + gbmap->preeraser.known = 0; + gbmap->preeraser.count = 0; + #endif lfs3_btree_init(&gbmap->b); lfs3_btree_init(&gbmap->b_p); } @@ -10554,9 +10593,26 @@ static int lfs3_data_readgbmap(lfs3_t *lfs3, lfs3_data_t *data, #ifdef LFS3_GBMAP static lfs3_stag_t lfs3_gbmap_lookupnext(lfs3_t *lfs3, lfs3_btree_t *gbmap, lfs3_bid_t bid, - lfs3_bid_t *bid_, lfs3_bid_t *weight_) { - return lfs3_btree_lookupnext(lfs3, gbmap, bid, - bid_, weight_, NULL); + lfs3_bid_t *bid_, lfs3_bid_t *weight_, lfs3_ecksum_t *ecksum_) { + lfs3_data_t data; + lfs3_stag_t tag = lfs3_btree_lookupnext(lfs3, gbmap, bid, + bid_, weight_, &data); + if (tag < 0) { + return tag; + } + + if (ecksum_) { + if (tag == LFS3_TAG_BMERASED && lfs3_data_size(&data) > 0) { + int err = lfs3_data_readecksum(lfs3, &data, + ecksum_); + if (err) { + return err; + } + } else { + ecksum_->cksize = -1; + } + } + return tag; } #endif @@ -10574,19 +10630,21 @@ static int lfs3_gbmap_commit(lfs3_t *lfs3, lfs3_btree_t *gbmap, // the purpose of weight is really just to provide a shortcut for bulk // clearing ranges in lfs3_alloc_lookgbmap static int lfs3_gbmap_mark_(lfs3_t *lfs3, lfs3_btree_t *gbmap, - lfs3_block_t block, lfs3_block_t weight, lfs3_tag_t tag) { + lfs3_block_t block, lfs3_block_t weight, + lfs3_tag_t tag, const lfs3_ecksum_t *ecksum) { // lookup gbmap range lfs3_bid_t bid__; lfs3_bid_t weight__; + lfs3_ecksum_t ecksum__; lfs3_stag_t tag__ = lfs3_gbmap_lookupnext(lfs3, gbmap, block, - &bid__, &weight__); + &bid__, &weight__, &ecksum__); if (tag__ < 0) { LFS3_ASSERT(tag__ != LFS3_ERR_NOENT); return tag__; } // wait, already set to expected type? guess we're done - if (tag__ == tag) { + if (tag__ == tag && lfs3_ecksum_cmp(&ecksum__, ecksum) == 0) { return 0; } @@ -10607,15 +10665,16 @@ static int lfs3_gbmap_mark_(lfs3_t *lfs3, lfs3_btree_t *gbmap, && block < lfs3->block_count-1) { lfs3_bid_t r_bid; lfs3_bid_t r_weight; + lfs3_ecksum_t r_ecksum; lfs3_stag_t r_tag = lfs3_gbmap_lookupnext(lfs3, gbmap, block+1, - &r_bid, &r_weight); + &r_bid, &r_weight, &r_ecksum); if (r_tag < 0) { LFS3_ASSERT(r_tag != LFS3_ERR_NOENT); return r_tag; } LFS3_ASSERT(r_weight == r_bid - block); - if (r_tag == tag) { + if (r_tag == tag && lfs3_ecksum_cmp(&r_ecksum, ecksum) == 0) { // delete to prepare merge int err = lfs3_gbmap_commit(lfs3, &gbmap_, r_bid, LFS3_RATTRS( LFS3_RATTR(2, LFS3_tag_RM, -2), @@ -10638,15 +10697,16 @@ static int lfs3_gbmap_mark_(lfs3_t *lfs3, lfs3_btree_t *gbmap, && block-(weight-1) > 0) { lfs3_bid_t l_bid; lfs3_bid_t l_weight; + lfs3_ecksum_t l_ecksum; lfs3_stag_t l_tag = lfs3_gbmap_lookupnext(lfs3, gbmap, block-weight, - &l_bid, &l_weight); + &l_bid, &l_weight, &l_ecksum); if (l_tag < 0) { LFS3_ASSERT(l_tag != LFS3_ERR_NOENT); return l_tag; } LFS3_ASSERT(l_bid == block-weight); - if (l_tag == tag) { + if (l_tag == tag && lfs3_ecksum_cmp(&l_ecksum, ecksum) == 0) { // delete to prepare merge int err = lfs3_gbmap_commit(lfs3, &gbmap_, l_bid, LFS3_RATTRS( LFS3_RATTR(2, LFS3_tag_RM, -2), @@ -10676,12 +10736,18 @@ static int lfs3_gbmap_mark_(lfs3_t *lfs3, lfs3_btree_t *gbmap, ? LFS3_RATTR(2, LFS3_tag_GROW, -2) : LFS3_RATTR(2, LFS3_tag_RM, -2), LFS3_RATTR_WEIGHT(-((bid__+1) - (block-(weight-1)))), - LFS3_RATTR(2, tag, -2), + (ecksum && ecksum->cksize != -1) + ? LFS3_RATTR(3, tag, -2, LFS3_FROM_ECKSUM) + : LFS3_RATTR(3, tag, -2), LFS3_RATTR_WEIGHT(+weight_), + LFS3_RATTR_ARG(ecksum), (bid__ > block) - ? LFS3_RATTR(2, tag__, -2) - : LFS3_RATTR(2, LFS3_TAG_NULL, 0), + ? ((ecksum__.cksize != -1) + ? LFS3_RATTR(3, tag__, -2, LFS3_FROM_ECKSUM) + : LFS3_RATTR(3, tag__, -2)) + : LFS3_RATTR(3, LFS3_TAG_NULL, 0), LFS3_RATTR_WEIGHT(+(bid__ - block)), + LFS3_RATTR_ARG(&ecksum__), LFS3_RATTR_NULL)); if (err) { return err; @@ -10695,8 +10761,8 @@ static int lfs3_gbmap_mark_(lfs3_t *lfs3, lfs3_btree_t *gbmap, #if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP) static int lfs3_gbmap_mark(lfs3_t *lfs3, lfs3_btree_t *gbmap, - lfs3_block_t block, lfs3_tag_t tag) { - return lfs3_gbmap_mark_(lfs3, gbmap, block, 1, tag); + lfs3_block_t block, lfs3_tag_t tag, const lfs3_ecksum_t *ecksum) { + return lfs3_gbmap_mark_(lfs3, gbmap, block, 1, tag, ecksum); } #endif @@ -10725,7 +10791,7 @@ static int lfs3_gbmap_markbptr(lfs3_t *lfs3, lfs3_btree_t *gbmap, } for (lfs3_size_t i = 0; i < block_count; i++) { - int err = lfs3_gbmap_mark(lfs3, gbmap, blocks[i], tag_); + int err = lfs3_gbmap_mark(lfs3, gbmap, blocks[i], tag_, NULL); if (err) { return err; } @@ -10743,7 +10809,7 @@ static int lfs3_gbmap_zero(lfs3_t *lfs3, lfs3_btree_t *gbmap) { while (true) { lfs3_block_t weight__; lfs3_stag_t tag__ = lfs3_gbmap_lookupnext(lfs3, gbmap, block__+1, - &block__, &weight__); + &block__, &weight__, NULL); if (tag__ < 0) { if (tag__ == LFS3_ERR_NOENT) { break; @@ -10753,8 +10819,8 @@ static int lfs3_gbmap_zero(lfs3_t *lfs3, lfs3_btree_t *gbmap) { // mark in-use/erased ranges as free if (tag__ == LFS3_TAG_BMINUSE || tag__ == LFS3_TAG_BMERASED) { - int err = lfs3_gbmap_mark_(lfs3, gbmap, block__, weight__, - LFS3_TAG_BMFREE); + int err = lfs3_gbmap_mark_(lfs3, gbmap, + block__, weight__, LFS3_TAG_BMFREE, NULL); if (err) { return err; } @@ -10826,8 +10892,8 @@ static inline int lfs3_alloc_ckpoint(lfs3_t *lfs3) { #endif // can we repopulate the lookahead buffer? -#ifndef LFS3_RDONLY static inline bool lfs3_alloc_canlookahead(const lfs3_t *lfs3) { + #ifndef LFS3_RDONLY // below gc_lookahead_thresh? return lfs3_max( lfs3->lookahead.known, @@ -10844,13 +10910,16 @@ static inline bool lfs3_alloc_canlookahead(const lfs3_t *lfs3) { lfs3_min( 8*lfs3->cfg->lookahead_size-1, lfs3->block_count-1)); + #else + // TODO adopt this localized void in flag functions? + (void)lfs3; + return false; + #endif } -#endif // can we repopulate the gbmap? -#ifndef LFS3_RDONLY static inline bool lfs3_alloc_canlookgbmap(const lfs3_t *lfs3) { - #ifdef LFS3_GBMAP + #if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP) // do we even have a gbmap? return lfs3_f_isgbmap(lfs3->flags) // below gc_lookgbmap_thresh? @@ -10866,7 +10935,26 @@ static inline bool lfs3_alloc_canlookgbmap(const lfs3_t *lfs3) { return false; #endif } -#endif + +// can we pre-erase? +static inline bool lfs3_alloc_canpreerase(const lfs3_t *lfs3) { + #if !defined(LFS3_RDONLY) \ + && defined(LFS3_GBMAP) \ + && !defined(LFS3_NO_PREERASE) + // do we even have a gbmap? + return lfs3_f_isgbmap(lfs3->flags) + // have we pre-erased enough blocks? + && lfs3->gbmap.preeraser.count + < lfs3->cfg->gc_preerase_count + // are there any more blocks in our known window? + && lfs3->gbmap.preeraser.known + < lfs3->gbmap.known; + #else + // TODO adopt this localized void in flag functions? + (void)lfs3; + return false; + #endif +} // discard any lookahead/gbmap windows, this is necessary if block_count // changes @@ -11030,7 +11118,7 @@ static lfs3_sblock_t lfs3_alloc_findfree(lfs3_t *lfs3) { lfs3_block_t block; lfs3_stag_t tag = lfs3_gbmap_lookupnext(lfs3, &lfs3->gbmap.b, lfs3->gbmap.window, - &block, NULL); + &block, NULL, NULL); if (tag < 0) { return tag; } @@ -11172,6 +11260,7 @@ static lfs3_sblock_t lfs3_alloc(lfs3_t *lfs3, uint32_t flags) { } #endif +// rebuild the gbmap #if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP) static int lfs3_alloc_lookgbmap(lfs3_t *lfs3) { LFS3_INFO("Repopulating gbmap (gbmap %"PRId32"/%"PRId32")", @@ -11225,6 +11314,66 @@ static int lfs3_alloc_lookgbmap(lfs3_t *lfs3) { } #endif +// try to pre-erase _one_ block +#if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP) && !defined(LFS3_NO_PREERASE) +static int lfs3_alloc_preerase(lfs3_t *lfs3) { + while (lfs3->gbmap.preeraser.known < lfs3->gbmap.known) { + // lookup next known block + lfs3_block_t block = (lfs3->gbmap.window + lfs3->gbmap.preeraser.known) + % lfs3->cfg->block_count; + lfs3_bid_t block__; + lfs3_stag_t tag__ = lfs3_gbmap_lookupnext(lfs3, &lfs3->gbmap.b, block, + &block__, NULL, NULL); + if (tag__ < 0) { + LFS3_ASSERT(tag__ != LFS3_ERR_NOENT); + return tag__; + } + + // not free? + if (tag__ != LFS3_TAG_BMFREE) { + lfs3_sblock_t d = lfs3_min( + block__+1 - block, + lfs3->gbmap.known - lfs3->gbmap.preeraser.known); + // wait, already erased? + if (tag__ == LFS3_TAG_BMERASED) { + lfs3->gbmap.preeraser.count += d; + } + lfs3->gbmap.preeraser.known += d; + continue; + } + + // erase! + int err = lfs3_bd_erase(lfs3, block); + if (err) { + return err; + } + + // calculate erased-state checksum + lfs3_ecksum_t ecksum; + err = lfs3_ecksum_eck(lfs3, &ecksum, block, 0); + if (err) { + return err; + } + + // commit into gbmap + // + // this relies on lfs3_gbmap_commit being atomic + err = lfs3_gbmap_mark(lfs3, &lfs3->gbmap.b, + block, LFS3_TAG_BMERASED, &ecksum); + if (err) { + return err; + } + + // successful pre-erase + lfs3->gbmap.preeraser.count += 1; + lfs3->gbmap.preeraser.known += 1; + return 0; + } + + return LFS3_ERR_NOENT; +} +#endif + @@ -14870,6 +15019,9 @@ static int lfs3_init(lfs3_t *lfs3, uint32_t flags, LFS3_ASSERT((lfs3->cfg->gc_flags & ~( LFS3_IFDEF_RDONLY(0, LFS3_GC_MKCONSISTENT) | LFS3_IFDEF_RDONLY(0, LFS3_GC_LOOKAHEAD) + | LFS3_IFDEF_RDONLY(0, + LFS3_IFDEF_GBMAP( + LFS3_IFDEF_PREERASE(LFS3_GC_PREERASE, 0), 0)) | LFS3_IFDEF_RDONLY(0, LFS3_GC_COMPACT) | LFS3_GC_CKMETA | LFS3_GC_CKDATA)) == 0); @@ -14898,8 +15050,9 @@ static int lfs3_init(lfs3_t *lfs3, uint32_t flags, | LFS3_IFDEF_RDONLY(0, LFS3_I_MKCONSISTENT) // default to lookaheadable | LFS3_IFDEF_RDONLY(0, LFS3_I_LOOKAHEAD) - // default to assuming we need compaction somewhere, worst case - // this just makes lfs3_fs_gc read more than is strictly needed + // default to assuming we need compaction somewhere, worst + // case this just makes lfs3_fs_gc read more than is + // strictly needed | LFS3_IFDEF_RDONLY(0, LFS3_I_COMPACT) // default to needing a ckmeta/ckdata scan | LFS3_I_CKMETA @@ -15728,6 +15881,9 @@ int lfs3_mount(lfs3_t *lfs3, uint32_t flags, #ifdef LFS3_YES_LOOKAHEAD flags |= LFS3_M_LOOKAHEAD; #endif + #ifdef LFS3_YES_PREERASE + flags |= LFS3_M_PREERASE; + #endif #ifdef LFS3_YES_COMPACT flags |= LFS3_M_COMPACT; #endif @@ -15752,12 +15908,16 @@ int lfs3_mount(lfs3_t *lfs3, uint32_t flags, | LFS3_IFDEF_CKDATACKSUMS(LFS3_M_CKDATACKSUMS, 0) | LFS3_IFDEF_RDONLY(0, LFS3_M_MKCONSISTENT) | LFS3_IFDEF_RDONLY(0, LFS3_M_LOOKAHEAD) + | LFS3_IFDEF_RDONLY(0, + LFS3_IFDEF_GBMAP( + LFS3_IFDEF_PREERASE(LFS3_M_PREERASE, 0), 0)) | LFS3_IFDEF_RDONLY(0, LFS3_M_COMPACT) | LFS3_M_CKMETA | LFS3_M_CKDATA)) == 0); // these flags require a writable filesystem LFS3_ASSERT(!lfs3_m_isrdonly(flags) || !lfs3_t_ismkconsistent(flags)); LFS3_ASSERT(!lfs3_m_isrdonly(flags) || !lfs3_t_islookahead(flags)); + LFS3_ASSERT(!lfs3_m_isrdonly(flags) || !lfs3_t_ispreerase(flags)); LFS3_ASSERT(!lfs3_m_isrdonly(flags) || !lfs3_t_compact(flags)); int err = lfs3_init(lfs3, @@ -15786,12 +15946,18 @@ int lfs3_mount(lfs3_t *lfs3, uint32_t flags, if (flags & ( LFS3_IFDEF_RDONLY(0, LFS3_GC_MKCONSISTENT) | LFS3_IFDEF_RDONLY(0, LFS3_GC_LOOKAHEAD) + | LFS3_IFDEF_RDONLY(0, + LFS3_IFDEF_GBMAP( + LFS3_IFDEF_PREERASE(LFS3_GC_PREERASE, 0), 0)) | LFS3_IFDEF_RDONLY(0, LFS3_GC_COMPACT) | LFS3_GC_CKMETA | LFS3_GC_CKDATA)) { err = lfs3_fs_ck(lfs3, flags & ( LFS3_IFDEF_RDONLY(0, LFS3_GC_MKCONSISTENT) | LFS3_IFDEF_RDONLY(0, LFS3_GC_LOOKAHEAD) + | LFS3_IFDEF_RDONLY(0, + LFS3_IFDEF_GBMAP( + LFS3_IFDEF_PREERASE(LFS3_GC_PREERASE, 0), 0)) | LFS3_IFDEF_RDONLY(0, LFS3_GC_COMPACT) | LFS3_GC_CKMETA | LFS3_GC_CKDATA)); @@ -16036,6 +16202,9 @@ int lfs3_format(lfs3_t *lfs3, uint32_t flags, #ifdef LFS3_YES_LOOKAHEAD flags |= LFS3_F_LOOKAHEAD; #endif + #ifdef LFS3_YES_PREERASE + flags |= LFS3_F_PREERASE; + #endif #ifdef LFS3_YES_COMPACT flags |= LFS3_F_COMPACT; #endif @@ -16058,6 +16227,9 @@ int lfs3_format(lfs3_t *lfs3, uint32_t flags, | LFS3_IFDEF_CKDATACKSUMS(LFS3_F_CKDATACKSUMS, 0) | LFS3_F_MKCONSISTENT | LFS3_F_LOOKAHEAD + | LFS3_IFDEF_RDONLY(0, + LFS3_IFDEF_GBMAP( + LFS3_IFDEF_PREERASE(LFS3_F_PREERASE, 0), 0)) | LFS3_F_COMPACT | LFS3_F_CKMETA | LFS3_F_CKDATA)) == 0); @@ -16098,12 +16270,18 @@ int lfs3_format(lfs3_t *lfs3, uint32_t flags, if (flags & ( LFS3_IFDEF_RDONLY(0, LFS3_GC_MKCONSISTENT) | LFS3_IFDEF_RDONLY(0, LFS3_GC_LOOKAHEAD) + | LFS3_IFDEF_RDONLY(0, + LFS3_IFDEF_GBMAP( + LFS3_IFDEF_PREERASE(LFS3_GC_PREERASE, 0), 0)) | LFS3_IFDEF_RDONLY(0, LFS3_GC_COMPACT) | LFS3_GC_CKMETA | LFS3_GC_CKDATA)) { err = lfs3_fs_ck(lfs3, flags & ( LFS3_IFDEF_RDONLY(0, LFS3_GC_MKCONSISTENT) | LFS3_IFDEF_RDONLY(0, LFS3_GC_LOOKAHEAD) + | LFS3_IFDEF_RDONLY(0, + LFS3_IFDEF_GBMAP( + LFS3_IFDEF_PREERASE(LFS3_GC_PREERASE, 0), 0)) | LFS3_IFDEF_RDONLY(0, LFS3_GC_COMPACT) | LFS3_GC_CKMETA | LFS3_GC_CKDATA)); @@ -16150,7 +16328,16 @@ int lfs3_fs_stat(lfs3_t *lfs3, struct lfs3_fsinfo *fsinfo) { | LFS3_IFDEF_RDONLY(0, (lfs3_grm_count(lfs3) > 0) ? LFS3_I_MKCONSISTENT - : 0); + : 0) + // LFS3_I_PREERASE we're just lazy about, since it depends + // on both gc_preerase_count and preerase vs free known + // windows + | LFS3_IFDEF_RDONLY(0, + LFS3_IFDEF_GBMAP( + LFS3_IFDEF_PREERASE( + (lfs3_alloc_canpreerase(lfs3)) + ? LFS3_I_PREERASE + : 0, 0), 0)); // return filesystem config, this may come from disk fsinfo->block_size = lfs3->cfg->block_size; @@ -16354,7 +16541,7 @@ int lfs3_fs_mkconsistent(lfs3_t *lfs3) { static int lfs3_fs_gc_(lfs3_t *lfs3, lfs3_mgc_t *mgc, uint32_t flags, lfs3_soff_t steps) { while ((lfs3_off_t)steps > 0) { - // do we have any pending work? + // do we have any pending gc work? uint32_t pending = flags & ( (lfs3->flags & ( LFS3_IFDEF_RDONLY(0, LFS3_GC_MKCONSISTENT) @@ -16367,73 +16554,95 @@ static int lfs3_fs_gc_(lfs3_t *lfs3, lfs3_mgc_t *mgc, (lfs3_grm_count(lfs3) > 0) ? LFS3_GC_MKCONSISTENT : 0)); - if (!pending) { + if (pending) { + // prioritize lookahead/gbmap before any work that may need + // to allocate + #ifndef LFS3_RDONLY + if (lfs3_t_islookahead(pending)) { + pending &= ~( + LFS3_GC_MKCONSISTENT + | LFS3_GC_COMPACT); + } + #endif + + // start a new traversal? + if (!lfs3_handle_isopen(lfs3, &mgc->t.h)) { + lfs3_mgc_init(mgc, pending); + lfs3_handle_open(lfs3, &mgc->t.h); + } + // mask out any flags that changed + // + // note that even though our current API prevents flags from + // changing mid-traversal, lfs3->flags can be updated by + // other operations + mgc->t.h.flags &= ~(pending ^ ( + LFS3_IFDEF_RDONLY(0, LFS3_GC_MKCONSISTENT) + | LFS3_IFDEF_RDONLY(0, LFS3_GC_LOOKAHEAD) + | LFS3_IFDEF_RDONLY(0, LFS3_GC_COMPACT) + | LFS3_GC_CKMETA + | LFS3_GC_CKDATA)); + + // will this traversal still make progress? no? start over + if (!(mgc->t.h.flags + & (LFS3_IFDEF_RDONLY(0, LFS3_GC_MKCONSISTENT) + | LFS3_IFDEF_RDONLY(0, LFS3_GC_LOOKAHEAD) + | LFS3_IFDEF_RDONLY(0, LFS3_GC_COMPACT) + | LFS3_GC_CKMETA + | LFS3_GC_CKDATA) + // don't bother with lookahead/gbmap if we've + // ckpointed + & ~LFS3_IFDEF_RDONLY( + 0, + (lfs3_t_isckpointed(mgc->t.h.flags)) + ? LFS3_GC_LOOKAHEAD + : 0))) { + lfs3_handle_close(lfs3, &mgc->t.h); + continue; + } + + // do we really need a full traversal? + if (!(mgc->t.h.flags & ( + LFS3_IFDEF_RDONLY(0, LFS3_GC_LOOKAHEAD) + | LFS3_GC_CKMETA + | LFS3_GC_CKDATA))) { + mgc->t.h.flags |= LFS3_T_MTREEONLY; + } + + // progress gc + lfs3_bptr_t bptr; + lfs3_stag_t tag = lfs3_mtree_gc(lfs3, mgc, + &bptr); + if (tag < 0 && tag != LFS3_ERR_NOENT) { + lfs3_handle_close(lfs3, &mgc->t.h); + return tag; + } + + // end of traversal? + if (tag == LFS3_ERR_NOENT) { + lfs3_handle_close(lfs3, &mgc->t.h); + } + + // if we have no pending gc work, can we preerase blocks? + } else if (lfs3_alloc_canpreerase(lfs3)) { + #if !defined(LFS3_RDONLY) \ + && defined(LFS3_GBMAP) \ + && !defined(LFS3_NO_PREERASE) + int err = lfs3_alloc_preerase(lfs3); + if (err && err != LFS3_ERR_NOENT) { + return err; + } + #endif + + // TODO do this + // // if we have nothing else to do, try to commit the gbmap to + // // disk so it's recoverable if we lose power + // } else if (lfs3_btree_cmp(&lfs3->gbmap.b, &lfs3->gbmap.b_p) != 0) { + // // TODO + + // nothing to do at all? guess we're done + } else { break; } - // prioritize lookahead/gbmap before any work that may need to - // allocate - #ifndef LFS3_RDONLY - if (lfs3_t_islookahead(pending)) { - pending &= ~( - LFS3_GC_MKCONSISTENT - | LFS3_GC_COMPACT); - } - #endif - - // start a new traversal? - if (!lfs3_handle_isopen(lfs3, &mgc->t.h)) { - lfs3_mgc_init(mgc, pending); - lfs3_handle_open(lfs3, &mgc->t.h); - } - // mask out any flags that changed - // - // note that even though our current API prevents flags from - // changing mid-traversal, lfs3->flags can be updated by other - // operations - mgc->t.h.flags &= ~(pending ^ ( - LFS3_IFDEF_RDONLY(0, LFS3_GC_MKCONSISTENT) - | LFS3_IFDEF_RDONLY(0, LFS3_GC_LOOKAHEAD) - | LFS3_IFDEF_RDONLY(0, LFS3_GC_COMPACT) - | LFS3_GC_CKMETA - | LFS3_GC_CKDATA)); - - // will this traversal still make progress? no? start over - if (!(mgc->t.h.flags - & (LFS3_IFDEF_RDONLY(0, LFS3_GC_MKCONSISTENT) - | LFS3_IFDEF_RDONLY(0, LFS3_GC_LOOKAHEAD) - | LFS3_IFDEF_RDONLY(0, LFS3_GC_COMPACT) - | LFS3_GC_CKMETA - | LFS3_GC_CKDATA) - // don't bother with lookahead/gbmap if we've ckpointed - & ~LFS3_IFDEF_RDONLY( - 0, - (lfs3_t_isckpointed(mgc->t.h.flags)) - ? LFS3_GC_LOOKAHEAD - : 0))) { - lfs3_handle_close(lfs3, &mgc->t.h); - continue; - } - - // do we really need a full traversal? - if (!(mgc->t.h.flags & ( - LFS3_IFDEF_RDONLY(0, LFS3_GC_LOOKAHEAD) - | LFS3_GC_CKMETA - | LFS3_GC_CKDATA))) { - mgc->t.h.flags |= LFS3_T_MTREEONLY; - } - - // progress gc - lfs3_bptr_t bptr; - lfs3_stag_t tag = lfs3_mtree_gc(lfs3, mgc, - &bptr); - if (tag < 0 && tag != LFS3_ERR_NOENT) { - lfs3_handle_close(lfs3, &mgc->t.h); - return tag; - } - // end of traversal? - if (tag == LFS3_ERR_NOENT) { - lfs3_handle_close(lfs3, &mgc->t.h); - } // decrement steps if (steps > 0) { @@ -16452,6 +16661,9 @@ int lfs3_fs_ck(lfs3_t *lfs3, uint32_t flags) { LFS3_ASSERT((flags & ~( LFS3_IFDEF_RDONLY(0, LFS3_CK_MKCONSISTENT) | LFS3_IFDEF_RDONLY(0, LFS3_CK_LOOKAHEAD) + | LFS3_IFDEF_RDONLY(0, + LFS3_IFDEF_GBMAP( + LFS3_IFDEF_PREERASE(LFS3_CK_PREERASE, 0), 0)) | LFS3_IFDEF_RDONLY(0, LFS3_CK_COMPACT) | LFS3_CK_CKMETA | LFS3_CK_CKDATA)) == 0); @@ -16460,6 +16672,8 @@ int lfs3_fs_ck(lfs3_t *lfs3, uint32_t flags) { || !lfs3_t_ismkconsistent(flags)); LFS3_ASSERT(!lfs3_m_isrdonly(lfs3->flags) || !lfs3_t_islookahead(flags)); + LFS3_ASSERT(!lfs3_m_isrdonly(lfs3->flags) + || !lfs3_t_ispreerase(flags)); LFS3_ASSERT(!lfs3_m_isrdonly(lfs3->flags) || !lfs3_t_compact(flags)); @@ -16481,6 +16695,9 @@ int lfs3_fs_gc(lfs3_t *lfs3) { LFS3_ASSERT((lfs3->cfg->gc_flags & ~( LFS3_IFDEF_RDONLY(0, LFS3_GC_MKCONSISTENT) | LFS3_IFDEF_RDONLY(0, LFS3_GC_LOOKAHEAD) + | LFS3_IFDEF_RDONLY(0, + LFS3_IFDEF_GBMAP( + LFS3_IFDEF_PREERASE(LFS3_GC_PREERASE, 0), 0)) | LFS3_IFDEF_RDONLY(0, LFS3_GC_COMPACT) | LFS3_GC_CKMETA | LFS3_GC_CKDATA)) == 0); @@ -16489,6 +16706,8 @@ int lfs3_fs_gc(lfs3_t *lfs3) { || !lfs3_t_ismkconsistent(lfs3->cfg->gc_flags)); LFS3_ASSERT(!lfs3_m_isrdonly(lfs3->flags) || !lfs3_t_islookahead(lfs3->cfg->gc_flags)); + LFS3_ASSERT(!lfs3_m_isrdonly(lfs3->flags) + || !lfs3_t_ispreerase(lfs3->cfg->gc_flags)); LFS3_ASSERT(!lfs3_m_isrdonly(lfs3->flags) || !lfs3_t_compact(lfs3->cfg->gc_flags)); @@ -16507,6 +16726,9 @@ int lfs3_fs_unck(lfs3_t *lfs3, uint32_t flags) { LFS3_ASSERT((flags & ~( LFS3_IFDEF_RDONLY(0, LFS3_GC_MKCONSISTENT) | LFS3_IFDEF_RDONLY(0, LFS3_GC_LOOKAHEAD) + | LFS3_IFDEF_RDONLY(0, + LFS3_IFDEF_GBMAP( + LFS3_IFDEF_PREERASE(LFS3_GC_PREERASE, 0), 0)) | LFS3_IFDEF_RDONLY(0, LFS3_GC_COMPACT) | LFS3_GC_CKMETA | LFS3_GC_CKDATA)) == 0); @@ -16571,7 +16793,7 @@ int lfs3_fs_grow(lfs3_t *lfs3, lfs3_size_t block_count_) { // need a new range lfs3_stag_t tag = lfs3_gbmap_lookupnext(lfs3, &lfs3->gbmap.b, block_count-1, - NULL, NULL); + NULL, NULL, NULL); if (tag < 0) { LFS3_ASSERT(tag != LFS3_ERR_NOENT); err = tag; @@ -16745,6 +16967,9 @@ int lfs3_trv_open(lfs3_t *lfs3, lfs3_trv_t *trv, uint32_t flags) { | LFS3_T_EXCL | LFS3_IFDEF_RDONLY(0, LFS3_T_MKCONSISTENT) | LFS3_IFDEF_RDONLY(0, LFS3_T_LOOKAHEAD) + | LFS3_IFDEF_RDONLY(0, + LFS3_IFDEF_GBMAP( + LFS3_IFDEF_PREERASE(LFS3_T_PREERASE, 0), 0)) | LFS3_IFDEF_RDONLY(0, LFS3_T_COMPACT) | LFS3_T_CKMETA | LFS3_T_CKDATA)) == 0); @@ -16753,6 +16978,7 @@ int lfs3_trv_open(lfs3_t *lfs3, lfs3_trv_t *trv, uint32_t flags) { // these flags require a writable traversal LFS3_ASSERT(!lfs3_t_isrdonly(flags) || !lfs3_t_ismkconsistent(flags)); LFS3_ASSERT(!lfs3_t_isrdonly(flags) || !lfs3_t_islookahead(flags)); + LFS3_ASSERT(!lfs3_t_isrdonly(flags) || !lfs3_t_ispreerase(flags)); LFS3_ASSERT(!lfs3_t_isrdonly(flags) || !lfs3_t_compact(flags)); // some flags don't make sense when only traversing the mtree LFS3_ASSERT(!lfs3_t_ismtreeonly(flags) || !lfs3_t_islookahead(flags)); diff --git a/lfs3.h b/lfs3.h index 27980b41..4f807f84 100644 --- a/lfs3.h +++ b/lfs3.h @@ -211,6 +211,9 @@ enum lfs3_type { #define LFS3_F_LOOKAHEAD \ 0x00000200 // Repopulate lookahead/gbmap #endif +#if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP) && !defined(LFS3_NO_PREERASE) +#define LFS3_F_PREERASE 0x00000400 // Try to pre-erase free blocks +#endif #ifndef LFS3_RDONLY #define LFS3_F_COMPACT 0x00000800 // Compact metadata logs #endif @@ -228,6 +231,9 @@ enum lfs3_type { #define LFS3_F_GC ( \ LFS3_IFDEF_RDONLY(0, LFS3_F_MKCONSISTENT) \ | LFS3_IFDEF_RDONLY(0, LFS3_F_LOOKAHEAD) \ + | LFS3_IFDEF_RDONLY(0, \ + LFS3_IFDEF_GBMAP( \ + LFS3_IFDEF_PREERASE(LFS3_F_PREERASE, 0), 0)) \ | LFS3_IFDEF_RDONLY(0, LFS3_F_COMPACT) \ | LFS3_F_CKMETA \ | LFS3_F_CKDATA) @@ -269,6 +275,9 @@ enum lfs3_type { #define LFS3_M_LOOKAHEAD \ 0x00000200 // Repopulate lookahead/gbmap #endif +#if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP) && !defined(LFS3_NO_PREERASE) +#define LFS3_M_PREERASE 0x00000400 // Try to pre-erase free blocks +#endif #ifndef LFS3_RDONLY #define LFS3_M_COMPACT 0x00000800 // Compact metadata logs #endif @@ -282,6 +291,9 @@ enum lfs3_type { #define LFS3_M_GC ( \ LFS3_IFDEF_RDONLY(0, LFS3_M_MKCONSISTENT) \ | LFS3_IFDEF_RDONLY(0, LFS3_M_LOOKAHEAD) \ + | LFS3_IFDEF_RDONLY(0, \ + LFS3_IFDEF_GBMAP( \ + LFS3_IFDEF_PREERASE(LFS3_M_PREERASE, 0), 0)) \ | LFS3_IFDEF_RDONLY(0, LFS3_M_COMPACT) \ | LFS3_M_CKMETA \ | LFS3_M_CKDATA) @@ -322,6 +334,9 @@ enum lfs3_type { #define LFS3_I_LOOKAHEAD \ 0x00000200 // Lookahead/gbmap is not full #endif +#if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP) && !defined(LFS3_NO_PREERASE) +#define LFS3_I_PREERASE 0x00000400 // Blocks can be pre-erased +#endif #ifndef LFS3_RDONLY #define LFS3_I_COMPACT 0x00000800 // Filesystem may have uncompacted metadata #endif @@ -352,6 +367,9 @@ enum lfs3_btype { #define LFS3_T_LOOKAHEAD \ 0x00000200 // Repopulate lookahead/gbmap #endif +#if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP) && !defined(LFS3_NO_PREERASE) +#define LFS3_T_PREERASE 0x00000400 // Try to pre-erase free blocks +#endif #ifndef LFS3_RDONLY #define LFS3_T_COMPACT 0x00000800 // Compact metadata logs #endif @@ -374,6 +392,9 @@ enum lfs3_btype { #define LFS3_T_GC ( \ LFS3_IFDEF_RDONLY(0, LFS3_T_MKCONSISTENT) \ | LFS3_IFDEF_RDONLY(0, LFS3_T_LOOKAHEAD) \ + | LFS3_IFDEF_RDONLY(0, \ + LFS3_IFDEF_GBMAP( \ + LFS3_IFDEF_PREERASE(LFS3_T_PREERASE, 0), 0)) \ | LFS3_IFDEF_RDONLY(0, LFS3_T_COMPACT) \ | LFS3_T_CKMETA \ | LFS3_T_CKDATA) @@ -387,6 +408,10 @@ enum lfs3_btype { #define LFS3_CK_LOOKAHEAD \ 0x00000200 // Repopulate lookahead/gbmap #endif +#if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP) && !defined(LFS3_NO_PREERASE) +#define LFS3_CK_PREERASE \ + 0x00000400 // Try to pre-erase free blocks +#endif #ifndef LFS3_RDONLY #define LFS3_CK_COMPACT 0x00000800 // Compact metadata logs #endif @@ -400,6 +425,9 @@ enum lfs3_btype { #define LFS3_CK_GC ( \ LFS3_IFDEF_RDONLY(0, LFS3_CK_MKCONSISTENT) \ | LFS3_IFDEF_RDONLY(0, LFS3_CK_LOOKAHEAD) \ + | LFS3_IFDEF_RDONLY(0, \ + LFS3_IFDEF_GBMAP( \ + LFS3_IFDEF_PREERASE(LFS3_CK_PREERASE, 0), 0)) \ | LFS3_IFDEF_RDONLY(0, LFS3_CK_COMPACT) \ | LFS3_CK_CKMETA \ | LFS3_CK_CKDATA) @@ -413,6 +441,10 @@ enum lfs3_btype { #define LFS3_GC_LOOKAHEAD \ 0x00000200 // Repopulate lookahead/gbmap #endif +#if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP) && !defined(LFS3_NO_PREERASE) +#define LFS3_GC_PREERASE \ + 0x00000400 // Try to pre-erase free blocks +#endif #ifndef LFS3_RDONLY #define LFS3_GC_COMPACT 0x00000800 // Compact metadata logs #endif @@ -426,6 +458,9 @@ enum lfs3_btype { #define LFS3_GC_GC ( \ LFS3_IFDEF_RDONLY(0, LFS3_GC_MKCONSISTENT) \ | LFS3_IFDEF_RDONLY(0, LFS3_GC_LOOKAHEAD) \ + | LFS3_IFDEF_RDONLY(0, \ + LFS3_IFDEF_GBMAP( \ + LFS3_IFDEF_PREERASE(LFS3_GC_PREERASE, 0), 0)) \ | LFS3_IFDEF_RDONLY(0, LFS3_GC_COMPACT) \ | LFS3_GC_CKMETA \ | LFS3_GC_CKDATA) @@ -577,6 +612,22 @@ struct lfs3_cfg { lfs3_block_t gc_lookgbmap_thresh; #endif + // Number of blocks to try to pre-erase during gc. When erase is + // expensive (flash), pre-erasing blocks can help reduce the latency + // of block allocation. + // + // Requires the gbmap to track pre-erased blocks. + // + // 0 only erases blocks immediately before prog, while -1 or any + // value >= block_count attempts to pre-erase all known free blocks + // during gc. + // + #if !defined(LFS3_RDONLY) \ + && defined(LFS3_GBMAP) \ + && !defined(LFS3_NO_PREERASE) + lfs3_block_t gc_preerase_count; + #endif + // Threshold for metadata compaction during gc in bytes. // // Metadata logs that exceed this threshold will be compacted during @@ -1083,6 +1134,15 @@ typedef struct lfs3_bptr { #endif } lfs3_bptr_t; +// erased-state checksum +#ifndef LFS3_RDONLY +typedef struct lfs3_ecksum { + // cksize=-1 indicates no ecksum + lfs3_ssize_t cksize; + uint32_t cksum; +} lfs3_ecksum_t; +#endif + // littlefs's core metadata log type typedef struct lfs3_rbyd { lfs3_rid_t weight; @@ -1317,7 +1377,16 @@ typedef struct lfs3 { struct lfs3_gbmap { lfs3_block_t window; lfs3_block_t known; + #if !defined(LFS3_RDONLY) lfs3_sblock_t free; + #endif + #if !defined(LFS3_RDONLY) && !defined(LFS3_NO_PREERASE) + lfs3_ecksum_t ecksum; + struct lfs3_preeraser { + lfs3_block_t known; + lfs3_block_t count; + } preeraser; + #endif lfs3_btree_t b; lfs3_btree_t b_p; } gbmap; diff --git a/lfs3_util.h b/lfs3_util.h index 068dc8d0..f9621121 100644 --- a/lfs3_util.h +++ b/lfs3_util.h @@ -276,6 +276,12 @@ #define LFS3_IFDEF_YES_GBMAP(a, b) (b) #endif +#ifndef LFS3_NO_PREERASE +#define LFS3_IFDEF_PREERASE(a, b) (a) +#else +#define LFS3_IFDEF_PREERASE(a, b) (b) +#endif + #ifdef LFS3_BLEAFCACHE #define LFS3_IFDEF_BLEAFCACHE(a, b) (a) #else diff --git a/runners/bench_runner.h b/runners/bench_runner.h index 075b6cd6..dba06bbb 100644 --- a/runners/bench_runner.h +++ b/runners/bench_runner.h @@ -118,6 +118,7 @@ void bench_permutation(size_t i, uint32_t *buffer, size_t size); BENCH_DEFINE(GC_STEPS, 0 ) \ BENCH_DEFINE(GC_LOOKAHEAD_THRESH, -1 ) \ BENCH_DEFINE(GC_LOOKGBMAP_THRESH, -1 ) \ + BENCH_DEFINE(GC_PREERASE_COUNT, -1 ) \ BENCH_DEFINE(GC_COMPACT_THRESH, 0 ) \ BENCH_DEFINE(SHRUB_SIZE, BLOCK_SIZE/4 ) \ BENCH_DEFINE(FRAGMENT_SIZE, LFS3_MIN(BLOCK_SIZE/8, 512) ) \ @@ -148,6 +149,7 @@ void bench_permutation(size_t i, uint32_t *buffer, size_t size); .fcache_size = FCACHE_SIZE, \ .lookahead_size = LOOKAHEAD_SIZE, \ BENCH_GBMAP_CFG \ + BENCH_PREERASE_CFG \ BENCH_GC_CFG \ .gc_lookahead_thresh = GC_LOOKAHEAD_THRESH, \ .gc_compact_thresh = GC_COMPACT_THRESH, \ @@ -163,6 +165,13 @@ void bench_permutation(size_t i, uint32_t *buffer, size_t size); #define BENCH_GBMAP_CFG #endif +#if defined(LFS3_GBMAP) && !defined(LFS3_NO_PREERASE) +#define BENCH_PREERASE_CFG \ + .gc_preerase_count = GC_PREERASE_COUNT, +#else +#define BENCH_PREERASE_CFG +#endif + #ifdef LFS3_GC #define BENCH_GC_CFG \ .gc_flags = GC_FLAGS, \ diff --git a/runners/test_runner.h b/runners/test_runner.h index db48f4ba..488abb06 100644 --- a/runners/test_runner.h +++ b/runners/test_runner.h @@ -109,6 +109,7 @@ void test_permutation(size_t i, uint32_t *buffer, size_t size); TEST_DEFINE(GC_STEPS, 0 ) \ TEST_DEFINE(GC_LOOKAHEAD_THRESH, -1 ) \ TEST_DEFINE(GC_LOOKGBMAP_THRESH, -1 ) \ + TEST_DEFINE(GC_PREERASE_COUNT, -1 ) \ TEST_DEFINE(GC_COMPACT_THRESH, 0 ) \ TEST_DEFINE(SHRUB_SIZE, BLOCK_SIZE/4 ) \ TEST_DEFINE(FRAGMENT_SIZE, LFS3_MIN(BLOCK_SIZE/8, 512) ) \ @@ -139,6 +140,7 @@ void test_permutation(size_t i, uint32_t *buffer, size_t size); .fcache_size = FCACHE_SIZE, \ .lookahead_size = LOOKAHEAD_SIZE, \ TEST_GBMAP_CFG \ + TEST_PREERASE_CFG \ TEST_GC_CFG \ .gc_lookahead_thresh = GC_LOOKAHEAD_THRESH, \ .gc_compact_thresh = GC_COMPACT_THRESH, \ @@ -154,6 +156,13 @@ void test_permutation(size_t i, uint32_t *buffer, size_t size); #define TEST_GBMAP_CFG #endif +#if defined(LFS3_GBMAP) && !defined(LFS3_NO_PREERASE) +#define TEST_PREERASE_CFG \ + .gc_preerase_count = GC_PREERASE_COUNT, +#else +#define TEST_PREERASE_CFG +#endif + #ifdef LFS3_GC #define TEST_GC_CFG \ .gc_flags = GC_FLAGS, \ diff --git a/scripts/dbgflags.py b/scripts/dbgflags.py index 19172cbf..db0490bd 100755 --- a/scripts/dbgflags.py +++ b/scripts/dbgflags.py @@ -84,11 +84,12 @@ F_CKDATACKSUMS = 0x01000000 # y- Check data checksums on reads F_MKCONSISTENT = 0x00000100 # y- Make the filesystem consistent F_LOOKAHEAD = 0x00000200 # y- Repopulate lookahead buffer +F_PREERASE = 0x00000400 # y- Try to pre-erase free blocks F_COMPACT = 0x00000800 # y- Compact metadata logs F_CKMETA = 0x00001000 # y- Check metadata checksums F_CKDATA = 0x00002000 # y- Check metadata + data checksums F_CK = 0x00003000 # a- Alias for all check work -F_GC = 0x00003b00 # a- Alias for all gc work +F_GC = 0x00003f00 # a- Alias for all gc work # Filesystem mount flags M_MODE = 1 # -m Mount's access mode @@ -105,29 +106,32 @@ M_CKDATACKSUMS = 0x01000000 # y- Check data checksums on reads M_MKCONSISTENT = 0x00000100 # y- Make the filesystem consistent M_LOOKAHEAD = 0x00000200 # y- Repopulate lookahead buffer +M_PREERASE = 0x00000400 # y- Try to pre-erase free blocks M_COMPACT = 0x00000800 # y- Compact metadata logs M_CKMETA = 0x00001000 # y- Check metadata checksums M_CKDATA = 0x00002000 # y- Check metadata + data checksums M_CK = 0x00003000 # a- Alias for all check work -M_GC = 0x00003b00 # a- Alias for all gc work +M_GC = 0x00003f00 # a- Alias for all gc work # File/filesystem check flags CK_MKCONSISTENT = 0x00000100 # -- Make the filesystem consistent CK_LOOKAHEAD = 0x00000200 # -- Repopulate lookahead buffer +CK_PREERASE = 0x00000400 # -- Try to pre-erase free blocks CK_COMPACT = 0x00000800 # -- Compact metadata logs CK_CKMETA = 0x00001000 # -- Check metadata checksums CK_CKDATA = 0x00002000 # -- Check metadata + data checksums CK_CK = 0x00003000 # a- Alias for all check work -CK_GC = 0x00003b00 # a- Alias for all gc work +CK_GC = 0x00003f00 # a- Alias for all gc work # GC flags GC_MKCONSISTENT = 0x00000100 # -- Make the filesystem consistent GC_LOOKAHEAD = 0x00000200 # -- Repopulate lookahead buffer +GC_PREERASE = 0x00000400 # -- Try to pre-erase free blocks GC_COMPACT = 0x00000800 # -- Compact metadata logs GC_CKMETA = 0x00001000 # -- Check metadata checksums GC_CKDATA = 0x00002000 # -- Check metadata + data checksums GC_CK = 0x00003000 # a- Alias for all check work -GC_GC = 0x00003b00 # a- Alias for all gc work +GC_GC = 0x00003f00 # a- Alias for all gc work # Filesystem info flags I_RDONLY = 0x00000001 # -- Mounted read only @@ -144,6 +148,7 @@ I_CKDATACKSUMS = 0x01000000 # -- Mounted with LFS3_M_CKDATACKSUMS I_MKCONSISTENT = 0x00000100 # -- Filesystem needs mkconsistent to write I_LOOKAHEAD = 0x00000200 # -- Lookahead buffer is not full +I_PREERASE = 0x00000400 # -- Blocks can be pre-erased I_COMPACT = 0x00000800 # -- Filesystem may have uncompacted metadata I_CKMETA = 0x00001000 # -- Metadata checksums not checked recently I_CKDATA = 0x00002000 # -- Data checksums not checked recently @@ -156,11 +161,12 @@ T_MTREEONLY = 0x00000002 # -- Only traverse the mtree T_EXCL = 0x00000008 # -- Error if filesystem modified T_MKCONSISTENT = 0x00000100 # -- Make the filesystem consistent T_LOOKAHEAD = 0x00000200 # -- Repopulate lookahead buffer +T_PREERASE = 0x00000400 # -- Try to pre-erase free blocks T_COMPACT = 0x00000800 # -- Compact metadata logs T_CKMETA = 0x00001000 # -- Check metadata checksums T_CKDATA = 0x00002000 # -- Check metadata + data checksums T_CK = 0x00003000 # a- Alias for all check work -T_GC = 0x00003b00 # a- Alias for all gc work +T_GC = 0x00003f00 # a- Alias for all gc work t_TYPE = 0xf0000000 # im The traversal's type t_REG = 0x10000000 # i^ Type = regular-file diff --git a/tests/test_gbmap.toml b/tests/test_gbmap.toml index 1c42b13f..f3565159 100644 --- a/tests/test_gbmap.toml +++ b/tests/test_gbmap.toml @@ -29,9 +29,9 @@ code = ''' LFS3_RATTR_NULL)) => 0; // set some blocks as in-use, avoid any weird merges for now - lfs3_gbmap_mark(&lfs3, &gbmap, 6, LFS3_TAG_BMINUSE) => 0; - lfs3_gbmap_mark(&lfs3, &gbmap, 8, LFS3_TAG_BMINUSE) => 0; - lfs3_gbmap_mark(&lfs3, &gbmap, 10, LFS3_TAG_BMINUSE) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 6, LFS3_TAG_BMINUSE, NULL) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 8, LFS3_TAG_BMINUSE, NULL) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 10, LFS3_TAG_BMINUSE, NULL) => 0; printf("gbmap: w%d 0x%x.%x\n", gbmap.r.weight, gbmap.r.blocks[0], @@ -44,31 +44,31 @@ code = ''' lfs3_bid_t bid_; lfs3_bid_t weight_; lfs3_gbmap_lookupnext(&lfs3, &gbmap, 0, - &bid_, &weight_) => LFS3_TAG_BMFREE; + &bid_, &weight_, NULL) => LFS3_TAG_BMFREE; assert(bid_ == 5); assert(weight_ == 6); lfs3_gbmap_lookupnext(&lfs3, &gbmap, 6, - &bid_, &weight_) => LFS3_TAG_BMINUSE; + &bid_, &weight_, NULL) => LFS3_TAG_BMINUSE; assert(bid_ == 6); assert(weight_ == 1); lfs3_gbmap_lookupnext(&lfs3, &gbmap, 7, - &bid_, &weight_) => LFS3_TAG_BMFREE; + &bid_, &weight_, NULL) => LFS3_TAG_BMFREE; assert(bid_ == 7); assert(weight_ == 1); lfs3_gbmap_lookupnext(&lfs3, &gbmap, 8, - &bid_, &weight_) => LFS3_TAG_BMINUSE; + &bid_, &weight_, NULL) => LFS3_TAG_BMINUSE; assert(bid_ == 8); assert(weight_ == 1); lfs3_gbmap_lookupnext(&lfs3, &gbmap, 9, - &bid_, &weight_) => LFS3_TAG_BMFREE; + &bid_, &weight_, NULL) => LFS3_TAG_BMFREE; assert(bid_ == 9); assert(weight_ == 1); lfs3_gbmap_lookupnext(&lfs3, &gbmap, 10, - &bid_, &weight_) => LFS3_TAG_BMINUSE; + &bid_, &weight_, NULL) => LFS3_TAG_BMINUSE; assert(bid_ == 10); assert(weight_ == 1); lfs3_gbmap_lookupnext(&lfs3, &gbmap, 11, - &bid_, &weight_) => LFS3_TAG_BMFREE; + &bid_, &weight_, NULL) => LFS3_TAG_BMFREE; assert(bid_ == BLOCK_COUNT-1); assert(weight_ == BLOCK_COUNT-11); @@ -92,13 +92,13 @@ code = ''' LFS3_RATTR_NULL)) => 0; // set some blocks as in-use, avoid any weird merges for now - lfs3_gbmap_mark(&lfs3, &gbmap, 6, LFS3_TAG_BMINUSE) => 0; - lfs3_gbmap_mark(&lfs3, &gbmap, 8, LFS3_TAG_BMINUSE) => 0; - lfs3_gbmap_mark(&lfs3, &gbmap, 10, LFS3_TAG_BMINUSE) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 6, LFS3_TAG_BMINUSE, NULL) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 8, LFS3_TAG_BMINUSE, NULL) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 10, LFS3_TAG_BMINUSE, NULL) => 0; // replace those blocks as bad, this tests deleting ranges - lfs3_gbmap_mark(&lfs3, &gbmap, 6, LFS3_TAG_BMBAD) => 0; - lfs3_gbmap_mark(&lfs3, &gbmap, 8, LFS3_TAG_BMBAD) => 0; - lfs3_gbmap_mark(&lfs3, &gbmap, 10, LFS3_TAG_BMBAD) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 6, LFS3_TAG_BMBAD, NULL) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 8, LFS3_TAG_BMBAD, NULL) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 10, LFS3_TAG_BMBAD, NULL) => 0; printf("gbmap: w%d 0x%x.%x\n", gbmap.r.weight, gbmap.r.blocks[0], @@ -111,31 +111,31 @@ code = ''' lfs3_bid_t bid_; lfs3_bid_t weight_; lfs3_gbmap_lookupnext(&lfs3, &gbmap, 0, - &bid_, &weight_) => LFS3_TAG_BMFREE; + &bid_, &weight_, NULL) => LFS3_TAG_BMFREE; assert(bid_ == 5); assert(weight_ == 6); lfs3_gbmap_lookupnext(&lfs3, &gbmap, 6, - &bid_, &weight_) => LFS3_TAG_BMBAD; + &bid_, &weight_, NULL) => LFS3_TAG_BMBAD; assert(bid_ == 6); assert(weight_ == 1); lfs3_gbmap_lookupnext(&lfs3, &gbmap, 7, - &bid_, &weight_) => LFS3_TAG_BMFREE; + &bid_, &weight_, NULL) => LFS3_TAG_BMFREE; assert(bid_ == 7); assert(weight_ == 1); lfs3_gbmap_lookupnext(&lfs3, &gbmap, 8, - &bid_, &weight_) => LFS3_TAG_BMBAD; + &bid_, &weight_, NULL) => LFS3_TAG_BMBAD; assert(bid_ == 8); assert(weight_ == 1); lfs3_gbmap_lookupnext(&lfs3, &gbmap, 9, - &bid_, &weight_) => LFS3_TAG_BMFREE; + &bid_, &weight_, NULL) => LFS3_TAG_BMFREE; assert(bid_ == 9); assert(weight_ == 1); lfs3_gbmap_lookupnext(&lfs3, &gbmap, 10, - &bid_, &weight_) => LFS3_TAG_BMBAD; + &bid_, &weight_, NULL) => LFS3_TAG_BMBAD; assert(bid_ == 10); assert(weight_ == 1); lfs3_gbmap_lookupnext(&lfs3, &gbmap, 11, - &bid_, &weight_) => LFS3_TAG_BMFREE; + &bid_, &weight_, NULL) => LFS3_TAG_BMFREE; assert(bid_ == BLOCK_COUNT-1); assert(weight_ == BLOCK_COUNT-11); @@ -159,14 +159,14 @@ code = ''' LFS3_RATTR_NULL)) => 0; // set some blocks as in-use, avoid any weird merges for now - lfs3_gbmap_mark(&lfs3, &gbmap, 6, LFS3_TAG_BMINUSE) => 0; - lfs3_gbmap_mark(&lfs3, &gbmap, 8, LFS3_TAG_BMINUSE) => 0; - lfs3_gbmap_mark(&lfs3, &gbmap, 10, LFS3_TAG_BMINUSE) => 0; - lfs3_gbmap_mark(&lfs3, &gbmap, 12, LFS3_TAG_BMINUSE) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 6, LFS3_TAG_BMINUSE, NULL) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 8, LFS3_TAG_BMINUSE, NULL) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 10, LFS3_TAG_BMINUSE, NULL) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 12, LFS3_TAG_BMINUSE, NULL) => 0; // set neighboring blocks as in-use, triggering merges - lfs3_gbmap_mark(&lfs3, &gbmap, 5, LFS3_TAG_BMINUSE) => 0; - lfs3_gbmap_mark(&lfs3, &gbmap, 9, LFS3_TAG_BMINUSE) => 0; - lfs3_gbmap_mark(&lfs3, &gbmap, 13, LFS3_TAG_BMINUSE) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 5, LFS3_TAG_BMINUSE, NULL) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 9, LFS3_TAG_BMINUSE, NULL) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 13, LFS3_TAG_BMINUSE, NULL) => 0; printf("gbmap: w%d 0x%x.%x\n", gbmap.r.weight, gbmap.r.blocks[0], @@ -179,31 +179,31 @@ code = ''' lfs3_bid_t bid_; lfs3_bid_t weight_; lfs3_gbmap_lookupnext(&lfs3, &gbmap, 0, - &bid_, &weight_) => LFS3_TAG_BMFREE; + &bid_, &weight_, NULL) => LFS3_TAG_BMFREE; assert(bid_ == 4); assert(weight_ == 5); lfs3_gbmap_lookupnext(&lfs3, &gbmap, 5, - &bid_, &weight_) => LFS3_TAG_BMINUSE; + &bid_, &weight_, NULL) => LFS3_TAG_BMINUSE; assert(bid_ == 6); assert(weight_ == 2); lfs3_gbmap_lookupnext(&lfs3, &gbmap, 7, - &bid_, &weight_) => LFS3_TAG_BMFREE; + &bid_, &weight_, NULL) => LFS3_TAG_BMFREE; assert(bid_ == 7); assert(weight_ == 1); lfs3_gbmap_lookupnext(&lfs3, &gbmap, 8, - &bid_, &weight_) => LFS3_TAG_BMINUSE; + &bid_, &weight_, NULL) => LFS3_TAG_BMINUSE; assert(bid_ == 10); assert(weight_ == 3); lfs3_gbmap_lookupnext(&lfs3, &gbmap, 11, - &bid_, &weight_) => LFS3_TAG_BMFREE; + &bid_, &weight_, NULL) => LFS3_TAG_BMFREE; assert(bid_ == 11); assert(weight_ == 1); lfs3_gbmap_lookupnext(&lfs3, &gbmap, 12, - &bid_, &weight_) => LFS3_TAG_BMINUSE; + &bid_, &weight_, NULL) => LFS3_TAG_BMINUSE; assert(bid_ == 13); assert(weight_ == 2); lfs3_gbmap_lookupnext(&lfs3, &gbmap, 14, - &bid_, &weight_) => LFS3_TAG_BMFREE; + &bid_, &weight_, NULL) => LFS3_TAG_BMFREE; assert(bid_ == BLOCK_COUNT-1); assert(weight_ == BLOCK_COUNT-14); @@ -227,15 +227,15 @@ code = ''' LFS3_RATTR_NULL)) => 0; // set some blocks as in-use, avoid any weird merges for now - lfs3_gbmap_mark(&lfs3, &gbmap, 6, LFS3_TAG_BMINUSE) => 0; - lfs3_gbmap_mark(&lfs3, &gbmap, 8, LFS3_TAG_BMINUSE) => 0; - lfs3_gbmap_mark(&lfs3, &gbmap, 10, LFS3_TAG_BMINUSE) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 6, LFS3_TAG_BMINUSE, NULL) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 8, LFS3_TAG_BMINUSE, NULL) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 10, LFS3_TAG_BMINUSE, NULL) => 0; // test a bunch of noops - lfs3_gbmap_mark(&lfs3, &gbmap, 6, LFS3_TAG_BMINUSE) => 0; - lfs3_gbmap_mark(&lfs3, &gbmap, 7, LFS3_TAG_BMFREE) => 0; - lfs3_gbmap_mark(&lfs3, &gbmap, 8, LFS3_TAG_BMINUSE) => 0; - lfs3_gbmap_mark(&lfs3, &gbmap, 9, LFS3_TAG_BMFREE) => 0; - lfs3_gbmap_mark(&lfs3, &gbmap, 10, LFS3_TAG_BMINUSE) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 6, LFS3_TAG_BMINUSE, NULL) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 7, LFS3_TAG_BMFREE, NULL) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 8, LFS3_TAG_BMINUSE, NULL) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 9, LFS3_TAG_BMFREE, NULL) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 10, LFS3_TAG_BMINUSE, NULL) => 0; printf("gbmap: w%d 0x%x.%x\n", gbmap.r.weight, gbmap.r.blocks[0], @@ -248,31 +248,31 @@ code = ''' lfs3_bid_t bid_; lfs3_bid_t weight_; lfs3_gbmap_lookupnext(&lfs3, &gbmap, 0, - &bid_, &weight_) => LFS3_TAG_BMFREE; + &bid_, &weight_, NULL) => LFS3_TAG_BMFREE; assert(bid_ == 5); assert(weight_ == 6); lfs3_gbmap_lookupnext(&lfs3, &gbmap, 6, - &bid_, &weight_) => LFS3_TAG_BMINUSE; + &bid_, &weight_, NULL) => LFS3_TAG_BMINUSE; assert(bid_ == 6); assert(weight_ == 1); lfs3_gbmap_lookupnext(&lfs3, &gbmap, 7, - &bid_, &weight_) => LFS3_TAG_BMFREE; + &bid_, &weight_, NULL) => LFS3_TAG_BMFREE; assert(bid_ == 7); assert(weight_ == 1); lfs3_gbmap_lookupnext(&lfs3, &gbmap, 8, - &bid_, &weight_) => LFS3_TAG_BMINUSE; + &bid_, &weight_, NULL) => LFS3_TAG_BMINUSE; assert(bid_ == 8); assert(weight_ == 1); lfs3_gbmap_lookupnext(&lfs3, &gbmap, 9, - &bid_, &weight_) => LFS3_TAG_BMFREE; + &bid_, &weight_, NULL) => LFS3_TAG_BMFREE; assert(bid_ == 9); assert(weight_ == 1); lfs3_gbmap_lookupnext(&lfs3, &gbmap, 10, - &bid_, &weight_) => LFS3_TAG_BMINUSE; + &bid_, &weight_, NULL) => LFS3_TAG_BMINUSE; assert(bid_ == 10); assert(weight_ == 1); lfs3_gbmap_lookupnext(&lfs3, &gbmap, 11, - &bid_, &weight_) => LFS3_TAG_BMFREE; + &bid_, &weight_, NULL) => LFS3_TAG_BMFREE; assert(bid_ == BLOCK_COUNT-1); assert(weight_ == BLOCK_COUNT-11); @@ -298,8 +298,8 @@ code = ''' // test that setting the first and last blocks don't break anything // // though in theory blocks 0x{0,1} are immutable... - lfs3_gbmap_mark(&lfs3, &gbmap, 0, LFS3_TAG_BMINUSE) => 0; - lfs3_gbmap_mark(&lfs3, &gbmap, BLOCK_COUNT-1, LFS3_TAG_BMINUSE) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 0, LFS3_TAG_BMINUSE, NULL) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, BLOCK_COUNT-1, LFS3_TAG_BMINUSE, NULL) => 0; printf("gbmap: w%d 0x%x.%x\n", gbmap.r.weight, gbmap.r.blocks[0], @@ -312,15 +312,15 @@ code = ''' lfs3_bid_t bid_; lfs3_bid_t weight_; lfs3_gbmap_lookupnext(&lfs3, &gbmap, 0, - &bid_, &weight_) => LFS3_TAG_BMINUSE; + &bid_, &weight_, NULL) => LFS3_TAG_BMINUSE; assert(bid_ == 0); assert(weight_ == 1); lfs3_gbmap_lookupnext(&lfs3, &gbmap, 1, - &bid_, &weight_) => LFS3_TAG_BMFREE; + &bid_, &weight_, NULL) => LFS3_TAG_BMFREE; assert(bid_ == BLOCK_COUNT-2); assert(weight_ == BLOCK_COUNT-2); lfs3_gbmap_lookupnext(&lfs3, &gbmap, BLOCK_COUNT-1, - &bid_, &weight_) => LFS3_TAG_BMINUSE; + &bid_, &weight_, NULL) => LFS3_TAG_BMINUSE; assert(bid_ == BLOCK_COUNT-1); assert(weight_ == 1); @@ -361,7 +361,7 @@ code = ''' lfs3_tag_t tag = LFS3_TAG_BMRANGE + (TEST_PRNG(&prng) % TYPES); // set in gbmap - lfs3_gbmap_mark(&lfs3, &gbmap, block, tag) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, block, tag, NULL) => 0; // and set in sim sim[block] = tag; @@ -386,7 +386,7 @@ code = ''' lfs3_bid_t bid_; lfs3_bid_t weight_; lfs3_gbmap_lookupnext(&lfs3, &gbmap, i, - &bid_, &weight_) => tag; + &bid_, &weight_, NULL) => tag; assert(bid_ == i+(weight_-1)); assert(weight_ == d); @@ -398,6 +398,413 @@ code = ''' ''' +# set operations with ecksums are a bit more complicated +[cases.test_gbmap_mark_ecksum_split] +in = 'lfs3.c' +code = ''' + lfs3_t lfs3; + lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; + lfs3_alloc_ckpoint(&lfs3); + + // create an initial gbmap + lfs3_btree_t gbmap; + lfs3_btree_init(&gbmap); + lfs3_gbmap_commit(&lfs3, &gbmap, 0, LFS3_RATTRS( + LFS3_RATTR(2, LFS3_TAG_BMFREE, -2), + LFS3_RATTR_WEIGHT(+BLOCK_COUNT), + LFS3_RATTR_NULL)) => 0; + + // set some blocks as erased with ecksums, avoid any weird merges + // for now + lfs3_ecksum_t ecksum = {.cksize=CFG->prog_size, .cksum=0x12345678}; + lfs3_gbmap_mark(&lfs3, &gbmap, 6, LFS3_TAG_BMERASED, &ecksum) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 8, LFS3_TAG_BMERASED, &ecksum) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 10, LFS3_TAG_BMERASED, &ecksum) => 0; + printf("gbmap: w%d 0x%x.%x\n", + gbmap.r.weight, + gbmap.r.blocks[0], + gbmap.r.trunk); + + // weight should stay the same + assert(gbmap.r.weight == BLOCK_COUNT); + + // check ranges + lfs3_bid_t bid_; + lfs3_bid_t weight_; + lfs3_ecksum_t ecksum_; + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 0, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE; + assert(bid_ == 5); + assert(weight_ == 6); + assert(lfs3_ecksum_cmp(&ecksum_, NULL) == 0); + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 6, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED; + assert(bid_ == 6); + assert(weight_ == 1); + assert(lfs3_ecksum_cmp(&ecksum_, &ecksum) == 0); + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 7, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE; + assert(bid_ == 7); + assert(weight_ == 1); + assert(lfs3_ecksum_cmp(&ecksum_, NULL) == 0); + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 8, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED; + assert(bid_ == 8); + assert(weight_ == 1); + assert(lfs3_ecksum_cmp(&ecksum_, &ecksum) == 0); + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 9, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE; + assert(bid_ == 9); + assert(weight_ == 1); + assert(lfs3_ecksum_cmp(&ecksum_, NULL) == 0); + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 10, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED; + assert(bid_ == 10); + assert(weight_ == 1); + assert(lfs3_ecksum_cmp(&ecksum_, &ecksum) == 0); + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 11, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE; + assert(bid_ == BLOCK_COUNT-1); + assert(weight_ == BLOCK_COUNT-11); + assert(lfs3_ecksum_cmp(&ecksum_, NULL) == 0); + + lfs3_unmount(&lfs3) => 0; +''' + +[cases.test_gbmap_mark_ecksum_replace] +in = 'lfs3.c' +code = ''' + lfs3_t lfs3; + lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; + lfs3_alloc_ckpoint(&lfs3); + + // create an initial gbmap + lfs3_btree_t gbmap; + lfs3_btree_init(&gbmap); + lfs3_gbmap_commit(&lfs3, &gbmap, 0, LFS3_RATTRS( + LFS3_RATTR(2, LFS3_TAG_BMFREE, -2), + LFS3_RATTR_WEIGHT(+BLOCK_COUNT), + LFS3_RATTR_NULL)) => 0; + + // set some blocks as erased with ecksums, avoid any weird merges + // for now + lfs3_ecksum_t ecksum_a = {.cksize=CFG->prog_size, .cksum=0x12345678}; + lfs3_gbmap_mark(&lfs3, &gbmap, 6, LFS3_TAG_BMERASED, &ecksum_a) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 8, LFS3_TAG_BMERASED, &ecksum_a) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 10, LFS3_TAG_BMERASED, &ecksum_a) => 0; + // replace those blocks with different ecksums + lfs3_ecksum_t ecksum_b = {.cksize=CFG->prog_size, .cksum=0x9abcdef0}; + lfs3_gbmap_mark(&lfs3, &gbmap, 6, LFS3_TAG_BMERASED, &ecksum_b) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 8, LFS3_TAG_BMERASED, &ecksum_b) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 10, LFS3_TAG_BMERASED, &ecksum_b) => 0; + printf("gbmap: w%d 0x%x.%x\n", + gbmap.r.weight, + gbmap.r.blocks[0], + gbmap.r.trunk); + + // weight should stay the same + assert(gbmap.r.weight == BLOCK_COUNT); + + // check ranges + lfs3_bid_t bid_; + lfs3_bid_t weight_; + lfs3_ecksum_t ecksum_; + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 0, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE; + assert(bid_ == 5); + assert(weight_ == 6); + assert(lfs3_ecksum_cmp(&ecksum_, NULL) == 0); + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 6, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED; + assert(bid_ == 6); + assert(weight_ == 1); + assert(lfs3_ecksum_cmp(&ecksum_, &ecksum_b) == 0); + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 7, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE; + assert(bid_ == 7); + assert(weight_ == 1); + assert(lfs3_ecksum_cmp(&ecksum_, NULL) == 0); + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 8, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED; + assert(bid_ == 8); + assert(weight_ == 1); + assert(lfs3_ecksum_cmp(&ecksum_, &ecksum_b) == 0); + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 9, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE; + assert(bid_ == 9); + assert(weight_ == 1); + assert(lfs3_ecksum_cmp(&ecksum_, NULL) == 0); + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 10, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED; + assert(bid_ == 10); + assert(weight_ == 1); + assert(lfs3_ecksum_cmp(&ecksum_, &ecksum_b) == 0); + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 11, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE; + assert(bid_ == BLOCK_COUNT-1); + assert(weight_ == BLOCK_COUNT-11); + assert(lfs3_ecksum_cmp(&ecksum_, NULL) == 0); + + lfs3_unmount(&lfs3) => 0; +''' + +[cases.test_gbmap_mark_ecksum_merge] +in = 'lfs3.c' +code = ''' + lfs3_t lfs3; + lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; + lfs3_alloc_ckpoint(&lfs3); + + // create an initial gbmap + lfs3_btree_t gbmap; + lfs3_btree_init(&gbmap); + lfs3_gbmap_commit(&lfs3, &gbmap, 0, LFS3_RATTRS( + LFS3_RATTR(2, LFS3_TAG_BMFREE, -2), + LFS3_RATTR_WEIGHT(+BLOCK_COUNT), + LFS3_RATTR_NULL)) => 0; + + // set some blocks as erased with ecksums, avoid any weird merges + // for now + lfs3_ecksum_t ecksum = {.cksize=CFG->prog_size, .cksum=0x12345678}; + lfs3_gbmap_mark(&lfs3, &gbmap, 6, LFS3_TAG_BMERASED, &ecksum) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 8, LFS3_TAG_BMERASED, &ecksum) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 10, LFS3_TAG_BMERASED, &ecksum) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 12, LFS3_TAG_BMERASED, &ecksum) => 0; + // set neighboring blocks as erased with ecksums, triggering merges + lfs3_gbmap_mark(&lfs3, &gbmap, 5, LFS3_TAG_BMERASED, &ecksum) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 9, LFS3_TAG_BMERASED, &ecksum) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 13, LFS3_TAG_BMERASED, &ecksum) => 0; + printf("gbmap: w%d 0x%x.%x\n", + gbmap.r.weight, + gbmap.r.blocks[0], + gbmap.r.trunk); + + // weight should stay the same + assert(gbmap.r.weight == BLOCK_COUNT); + + // check ranges + lfs3_bid_t bid_; + lfs3_bid_t weight_; + lfs3_ecksum_t ecksum_; + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 0, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE; + assert(bid_ == 4); + assert(weight_ == 5); + assert(lfs3_ecksum_cmp(&ecksum_, NULL) == 0); + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 5, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED; + assert(bid_ == 6); + assert(weight_ == 2); + assert(lfs3_ecksum_cmp(&ecksum_, &ecksum) == 0); + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 7, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE; + assert(bid_ == 7); + assert(weight_ == 1); + assert(lfs3_ecksum_cmp(&ecksum_, NULL) == 0); + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 8, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED; + assert(bid_ == 10); + assert(weight_ == 3); + assert(lfs3_ecksum_cmp(&ecksum_, &ecksum) == 0); + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 11, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE; + assert(bid_ == 11); + assert(weight_ == 1); + assert(lfs3_ecksum_cmp(&ecksum_, NULL) == 0); + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 12, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED; + assert(bid_ == 13); + assert(weight_ == 2); + assert(lfs3_ecksum_cmp(&ecksum_, &ecksum) == 0); + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 14, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE; + assert(bid_ == BLOCK_COUNT-1); + assert(weight_ == BLOCK_COUNT-14); + assert(lfs3_ecksum_cmp(&ecksum_, NULL) == 0); + + lfs3_unmount(&lfs3) => 0; +''' + +[cases.test_gbmap_mark_ecksum_nomerge] +in = 'lfs3.c' +code = ''' + lfs3_t lfs3; + lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; + lfs3_alloc_ckpoint(&lfs3); + + // create an initial gbmap + lfs3_btree_t gbmap; + lfs3_btree_init(&gbmap); + lfs3_gbmap_commit(&lfs3, &gbmap, 0, LFS3_RATTRS( + LFS3_RATTR(2, LFS3_TAG_BMFREE, -2), + LFS3_RATTR_WEIGHT(+BLOCK_COUNT), + LFS3_RATTR_NULL)) => 0; + + // set some blocks as erased with ecksums, avoid any weird merges + // for now + lfs3_ecksum_t ecksum_a = {.cksize=CFG->prog_size, .cksum=0x12345678}; + lfs3_gbmap_mark(&lfs3, &gbmap, 6, LFS3_TAG_BMERASED, &ecksum_a) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 8, LFS3_TAG_BMERASED, &ecksum_a) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 10, LFS3_TAG_BMERASED, &ecksum_a) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 12, LFS3_TAG_BMERASED, &ecksum_a) => 0; + // set neighboring blocks as erased with _different_ ecksums, this + // should _not_ trigger a merge! + lfs3_ecksum_t ecksum_b = {.cksize=CFG->prog_size, .cksum=0x9abcdef0}; + lfs3_gbmap_mark(&lfs3, &gbmap, 5, LFS3_TAG_BMERASED, &ecksum_b) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 9, LFS3_TAG_BMERASED, &ecksum_b) => 0; + lfs3_gbmap_mark(&lfs3, &gbmap, 13, LFS3_TAG_BMERASED, &ecksum_b) => 0; + printf("gbmap: w%d 0x%x.%x\n", + gbmap.r.weight, + gbmap.r.blocks[0], + gbmap.r.trunk); + + // weight should stay the same + assert(gbmap.r.weight == BLOCK_COUNT); + + // check ranges + lfs3_bid_t bid_; + lfs3_bid_t weight_; + lfs3_ecksum_t ecksum_; + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 0, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE; + assert(bid_ == 4); + assert(weight_ == 5); + assert(lfs3_ecksum_cmp(&ecksum_, NULL) == 0); + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 5, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED; + assert(bid_ == 5); + assert(weight_ == 1); + assert(lfs3_ecksum_cmp(&ecksum_, &ecksum_b) == 0); + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 6, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED; + assert(bid_ == 6); + assert(weight_ == 1); + assert(lfs3_ecksum_cmp(&ecksum_, &ecksum_a) == 0); + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 7, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE; + assert(bid_ == 7); + assert(weight_ == 1); + assert(lfs3_ecksum_cmp(&ecksum_, NULL) == 0); + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 8, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED; + assert(bid_ == 8); + assert(weight_ == 1); + assert(lfs3_ecksum_cmp(&ecksum_, &ecksum_a) == 0); + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 9, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED; + assert(bid_ == 9); + assert(weight_ == 1); + assert(lfs3_ecksum_cmp(&ecksum_, &ecksum_b) == 0); + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 10, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED; + assert(bid_ == 10); + assert(weight_ == 1); + assert(lfs3_ecksum_cmp(&ecksum_, &ecksum_a) == 0); + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 11, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE; + assert(bid_ == 11); + assert(weight_ == 1); + assert(lfs3_ecksum_cmp(&ecksum_, NULL) == 0); + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 12, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED; + assert(bid_ == 12); + assert(weight_ == 1); + assert(lfs3_ecksum_cmp(&ecksum_, &ecksum_a) == 0); + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 13, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED; + assert(bid_ == 13); + assert(weight_ == 1); + assert(lfs3_ecksum_cmp(&ecksum_, &ecksum_b) == 0); + lfs3_gbmap_lookupnext(&lfs3, &gbmap, 14, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE; + assert(bid_ == BLOCK_COUNT-1); + assert(weight_ == BLOCK_COUNT-14); + assert(lfs3_ecksum_cmp(&ecksum_, NULL) == 0); + + lfs3_unmount(&lfs3) => 0; +''' + +[cases.test_gbmap_mark_ecksum_fuzz] +defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] +defines.MASK = [0x1, 0x3, 0xf, 0xffff] +defines.SEED = 'range(20)' +fuzz = 'SEED' +in = 'lfs3.c' +code = ''' + lfs3_t lfs3; + lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; + lfs3_alloc_ckpoint(&lfs3); + + // create an initial gbmap + lfs3_ecksum_t ecksum = {.cksize=CFG->prog_size, .cksum=0x00000000}; + lfs3_btree_t gbmap; + lfs3_btree_init(&gbmap); + lfs3_gbmap_commit(&lfs3, &gbmap, 0, LFS3_RATTRS( + LFS3_RATTR(3, LFS3_TAG_BMERASED, -2, LFS3_FROM_ECKSUM), + LFS3_RATTR_WEIGHT(+BLOCK_COUNT), + LFS3_RATTR_ARG(&ecksum), + LFS3_RATTR_NULL)) => 0; + + // create a simulation to compare against + lfs3_tag_t *sim = malloc(BLOCK_COUNT*sizeof(uint32_t)); + for (lfs3_size_t i = 0; i < BLOCK_COUNT; i++) { + sim[i] = 0x00000000; + } + + uint32_t prng = SEED; + for (lfs3_size_t i = 0; i < N; i++) { + // choose a pseudo-random block + lfs3_block_t block = TEST_PRNG(&prng) % BLOCK_COUNT; + // and pseudo-random ecksum + ecksum.cksum = TEST_PRNG(&prng) & MASK; + + // set in gbmap + lfs3_gbmap_mark(&lfs3, &gbmap, + block, LFS3_TAG_BMERASED, &ecksum) => 0; + + // and set in sim + sim[block] = ecksum.cksum; + } + printf("gbmap: w%d 0x%x.%x\n", + gbmap.r.weight, + gbmap.r.blocks[0], + gbmap.r.trunk); + + // check if gbmap matches sim + lfs3_size_t i = 0; + while (i < BLOCK_COUNT) { + // we need to convert our sim's raw blocks to compressed ranges, + // in theory our gbmap is optimal + uint32_t cksum = sim[i]; + lfs3_size_t d = 1; + while (i+d < BLOCK_COUNT && sim[i+d] == cksum) { + d += 1; + } + + // does our gbmap contain the optimal range? + lfs3_bid_t bid_; + lfs3_bid_t weight_; + lfs3_ecksum_t ecksum_; + lfs3_gbmap_lookupnext(&lfs3, &gbmap, i, + &bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED; + assert(bid_ == i+(weight_-1)); + assert(weight_ == d); + assert(ecksum_.cksize == CFG->prog_size); + assert(ecksum_.cksum == cksum); + + i += d; + } + + free(sim); + lfs3_unmount(&lfs3) => 0; +''' + + # test high-level gbmap operations @@ -499,6 +906,122 @@ code = ''' lfs3_unmount(&lfs3) => 0; ''' +# test that the gbmap with gc generally works +[cases.test_gbmap_gc_files] +defines.COUNT = [ + 'BLOCK_COUNT', + 'BLOCK_COUNT-1', + 'BLOCK_COUNT/2', + 'BLOCK_COUNT/4', + '5', + '3', +] +defines.SIZE = [ + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '8*BLOCK_SIZE', +] +defines.N = ['5', 'BLOCK_COUNT/2'] +defines.WRAPAROUND = 3 +defines.PREERASE = [false, true] +defines.GC_FLAGS = ''' + LFS3_GC_LOOKAHEAD + | ((PREERASE) ? LFS3_GC_PREERASE : 0) +''' +defines.GC_STEPS = -1 +defines.GC_PREERASE_COUNT = ['0', '4', 'COUNT/2', 'COUNT-4', '-1'] +ifdef = 'LFS3_GC' +if = [ + 'COUNT >= 3', + 'COUNT >= 2*N*SIZE/BLOCK_SIZE', + 'LFS3_IFDEF_PREERASE(true, !PREERASE)', + # this is just to reduce useless permutations + 'PREERASE || GC_PREERASE_COUNT == 0', +] +code = ''' + // test various block counts + struct lfs3_cfg cfg = *CFG; + cfg.block_count = COUNT; + lfs3_t lfs3; + // note the gbmap flag + lfs3_format(&lfs3, LFS3_F_RDWR | LFS3_F_GBMAP, &cfg) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0; + + // check that we were formatted with the gbmap + struct lfs3_fsinfo fsinfo; + lfs3_fs_stat(&lfs3, &fsinfo) => 0; + assert(fsinfo.flags & LFS3_I_GBMAP); + + // run gc to prebuild gbmap, preerase blocks, etc + lfs3_fs_gc(&lfs3) => 0; + + // create n files repeatedly until we're sure we've wrapped around + // a few times + uint32_t prng = 42; + uint32_t prng_ = prng; + for (lfs3_size_t i = 0; + i < (WRAPAROUND*COUNT) + / (N*(SIZE/BLOCK_SIZE)); + i++) { + prng = prng_; + for (lfs3_size_t n = 0; n < N; n++) { + char name[256]; + sprintf(name, "file%08d", n); + + uint8_t wbuf[SIZE]; + for (lfs3_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); + } + + lfs3_file_t file; + lfs3_file_open(&lfs3, &file, name, + LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0; + lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; + lfs3_file_close(&lfs3, &file) => 0; + } + } + + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfs3_unmount(&lfs3) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0; + } + + // check that our file writes worked + prng_ = prng; + for (lfs3_size_t i = 0; i < N; i++) { + // check with stat + char name[256]; + sprintf(name, "file%08d", i); + struct lfs3_info info; + lfs3_stat(&lfs3, name, &info) => 0; + assert(strcmp(info.name, name) == 0); + assert(info.type == LFS3_TYPE_REG); + assert(info.size == SIZE); + + // try reading the file, note we reset prng above + uint8_t wbuf[SIZE]; + for (lfs3_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); + } + + lfs3_file_t file; + uint8_t rbuf[SIZE]; + lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0; + lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + lfs3_file_close(&lfs3, &file) => 0; + } + } + + lfs3_unmount(&lfs3) => 0; +''' + + + +# test high-high-level gbmap operations + # test that we can remove the gbmap and things still work [cases.test_gbmap_rmgbmap] ifndef = 'LFS3_YES_GBMAP'