preerase: Implemented the gc side of preerase

Allocating pre-erased blocks gets quite complicated due to our
restricted flash model, but at least the actual pre-erasing is
relatively straightforward:

- We keep track of known preerased state in lfs3->gbmap.preeraser.

- If LFS3_GC_PREERASE is provided during gc work, we increment the
  preeraser's known window by scanning the gbmap.

- Any BMFREE ranges we find, we erase a block at a time, and store the
  resulting ecksum in a BMERASED range in the gbmap.

- We keep track of how many blocks we erased, and stop early if this
  exceeds cfg.gc_preerase_count. This just lets users tune how many
  blocks to preerase in case something (?) prevents preerased blocks
  from being used.

Some notes:

- We don't really do anything with ranges in lfs3_alloc_preerase. In
  theory we could bulk in erase to minimize the number of commits to the
  gbmap, but we expect erase to dominate, so this probably isn't worth
  it.

  And if erase doesn't dominate, why would you bother pre-erasing
  blocks?

- Preerasing isn't really a traversal operation, and is managed by a
  sort of secondary state machine in lfs3_fs_gc_.

  This also means lfs3_trv_read with LFS3_T_PREERASE does nothing, but I
  guess that is ok? It's tempting to try to make lfs3_trv_read also
  preerase, but it's unclear what block it should return -- it's
  probably the wrong API.

- Introducing ecksums actually went quite a bit smoother than I
  expected. Though it helps ecksums are the only optional payload, no
  type punning or anything.

  Ecksums do muddy the gbmap's design a bit, unfortunately. The main
  issue being that we can only merge BMERASED ranges with equal ecksums.
  This makes BMERASED ranges less compressable than the others, and may
  be one reason to limit cfg.gc_preerase_count.

  However:

  1. This is where I think it's useful to emphasize that the gbmap's
     responsibility is to track _free_ blocks, in-use blocks are
     secondary.

     When allocating, we're going to stop at the first BMFREE/BMERASED,
     but may need to skip over an unbounded number of BMINUSE/BMBAD
     blocks. So the compressability of BMFREE/BMERASED ranges should
     have less of an impact on block allocation.

  2. In practice, most flash uses consistent erase values, so the
     resulting ecksums will probably be compressable. The exceptions are
     noop-erases (SD/eMMC, RAM, NVRAM, etc), and encryption with block
     address permutation?

     Though noop-erases are a pretty big exception.

Code changes:

                    code          stack          ctx
  before:          35116           2136          660
  after:           35116 (+0.0%)   2136 (+0.0%)  660 (+0.0%)

                    code          stack          ctx
  gbmap+np before: 38040           2136          776
  gbmap+np after:  38188 (+0.4%)   2144 (+0.4%)  776 (+0.0%)

                    code          stack          ctx
  gbmap+yp before: 38040           2136          776
  gbmap+yp after:  38608 (+1.5%)   2144 (+0.4%)  796 (+2.6%)
This commit is contained in:
Christopher Haster
2025-12-08 01:51:41 -06:00
parent f05be19d0e
commit 843412cc79
7 changed files with 1049 additions and 201 deletions
+363 -137
View File
@@ -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));
+69
View File
@@ -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;
+6
View File
@@ -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
+9
View File
@@ -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, \
+9
View File
@@ -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, \
+11 -5
View File
@@ -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
+582 -59
View File
@@ -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'