alloc: Reworked how lookahead/gbmap allocators interact

Before, the gbmap allocator worked by feeding the lookahead allocator,
so all allocation requests went through the lookahead buffer:

  alloc ---> lookahead ---> gbmap

This worked, and was easy to strap on to the existing system, but
limited what we could cache to what fits in our lookahead buffer. This
doesn't have a big effect on runtime analysis, since fragmentation is
always a concern, but it does mean we're not taking advantage of the
gbmap range representation and accessing disk more than we need to.

It also limits in-use range skipping to a lookahead buffer at a time,
which is problematic as the whole reason for the gbmap is to make the
lookahead buffer mostly irrelevant.

On top of the range issues, this design makes it difficult to add
preerase info, which is coming up on the TODO list.

---

To fix this, I reorganized the two allocators to run in parallel, with
the gbmap being queried first before falling back to the lookahead
buffer:

  alloc -+-> gbmap
         '-> lookahead

Instead of relying on the lookahead buffer, the gbmap now stores one
range in the gbmap.free field, using the sign to indicate if it's free
vs in-use (not the best name, but oh well).

This adds a word of storage to the gbmap, but as a tradeoff we can track
a full region in RAM and avoid repeated gbmap lookups.

The lookahead buffer can still be populated by gc work, which may be
useful if the gbmap is exhausted, but will also be cleared during gbmap
allocations to avoid out-of-date state.

---

While reworking this, I also tweaked a number of other allocator things:

- Adjusted lookahead.window to point to next block candidate
  (lookahead.window and gbmap.window should now always match)

- Renamed lfs3_alloc_zerogbmap -> lfs3_gbmap_zero

- Moved some alloc functions around

Code cost was mostly unaffected. Added an extra word to gbmap's ctx, but
as a tradeoff saved a chunk of stack by avoiding nested allocators:

                 code          stack          ctx
  before:       35160           2136          660
  after:        35152 (-0.0%)   2136 (+0.0%)  660 (+0.0%)

                 code          stack          ctx
  gbmap before: 38020           2152          772
  gbmap after:  38076 (+0.1%)   2136 (-0.7%)  776 (+0.5%)
This commit is contained in:
Christopher Haster
2025-12-04 00:02:24 -06:00
parent 465e9fbe9d
commit 7ccdee255b
2 changed files with 191 additions and 178 deletions
+190 -178
View File
@@ -7665,9 +7665,7 @@ static int lfs3_rbyd_appendgdelta(lfs3_t *lfs3, lfs3_rbyd_t *rbyd) {
// //
// we probably don't need the duplicate fields, but it certainly // we probably don't need the duplicate fields, but it certainly
// makes the code simpler // makes the code simpler
LFS3_ASSERT(lfs3->gbmap.window LFS3_ASSERT(lfs3->gbmap.window == lfs3->lookahead.window);
== (lfs3->lookahead.window + lfs3->lookahead.off)
% lfs3->block_count);
uint8_t gbmapdelta_[LFS3_GBMAP_DSIZE]; uint8_t gbmapdelta_[LFS3_GBMAP_DSIZE];
lfs3_data_fromgbmap(&lfs3->gbmap, gbmapdelta_); lfs3_data_fromgbmap(&lfs3->gbmap, gbmapdelta_);
@@ -10249,11 +10247,11 @@ eot:;
static int lfs3_mdir_mkconsistent(lfs3_t *lfs3, lfs3_mdir_t *mdir); static int lfs3_mdir_mkconsistent(lfs3_t *lfs3, lfs3_mdir_t *mdir);
static inline void lfs3_alloc_ckpoint_(lfs3_t *lfs3); static inline void lfs3_alloc_ckpoint_(lfs3_t *lfs3);
static void lfs3_alloc_adopt(lfs3_t *lfs3, lfs3_block_t known); static void lfs3_alloc_adopt(lfs3_t *lfs3, lfs3_block_t known);
static int lfs3_alloc_zerogbmap(lfs3_t *lfs3, lfs3_btree_t *gbmap); static int lfs3_gbmap_zero(lfs3_t *lfs3, lfs3_btree_t *gbmap);
static int lfs3_gbmap_markbptr(lfs3_t *lfs3, lfs3_btree_t *gbmap, static int lfs3_gbmap_markbptr(lfs3_t *lfs3, lfs3_btree_t *gbmap,
lfs3_tag_t tag, const lfs3_bptr_t *bptr, lfs3_tag_t tag, const lfs3_bptr_t *bptr,
lfs3_tag_t tag_); lfs3_tag_t tag_);
static void lfs3_alloc_adoptgbmap(lfs3_t *lfs3, static int lfs3_alloc_adoptgbmap(lfs3_t *lfs3,
const lfs3_btree_t *gbmap, lfs3_block_t known); const lfs3_btree_t *gbmap, lfs3_block_t known);
// high-level mutating traversal, handle extra features that require // high-level mutating traversal, handle extra features that require
@@ -10303,7 +10301,7 @@ static lfs3_stag_t lfs3_mtree_gc(lfs3_t *lfs3, lfs3_mgc_t *mgc,
// we do this instead of creating a new gbmap to (1) preserve any // we do this instead of creating a new gbmap to (1) preserve any
// erased/bad info and (2) try to best use any available // erased/bad info and (2) try to best use any available
// erased-state // erased-state
int err = lfs3_alloc_zerogbmap(lfs3, &mgc->gbmap_); int err = lfs3_gbmap_zero(lfs3, &mgc->gbmap_);
if (err) { if (err) {
return err; return err;
} }
@@ -10423,7 +10421,11 @@ eot:;
&& !lfs3_t_isckpointed(mgc->t.h.flags), && !lfs3_t_isckpointed(mgc->t.h.flags),
false)) { false)) {
#ifdef LFS3_GBMAP #ifdef LFS3_GBMAP
lfs3_alloc_adoptgbmap(lfs3, &mgc->gbmap_, lfs3->lookahead.ckpoint); int err = lfs3_alloc_adoptgbmap(lfs3, &mgc->gbmap_,
lfs3->lookahead.ckpoint);
if (err) {
return err;
}
#endif #endif
// was lookahead scan successful? // was lookahead scan successful?
@@ -10459,6 +10461,7 @@ eot:;
static void lfs3_gbmap_init(lfs3_gbmap_t *gbmap) { static void lfs3_gbmap_init(lfs3_gbmap_t *gbmap) {
gbmap->window = 0; gbmap->window = 0;
gbmap->known = 0; gbmap->known = 0;
gbmap->free = 0;
lfs3_btree_init(&gbmap->b); lfs3_btree_init(&gbmap->b);
lfs3_btree_init(&gbmap->b_p); lfs3_btree_init(&gbmap->b_p);
} }
@@ -10514,11 +10517,13 @@ static int lfs3_data_readgbmap(lfs3_t *lfs3, lfs3_data_t *data,
return err; return err;
} }
// we don't save free, so assume zero at first
gbmap->free = 0;
#ifdef LFS3_BLEAFCACHE #ifdef LFS3_BLEAFCACHE
// make sure to zero btree leaf // make sure to zero btree leaf
lfs3_btree_discardleaf(&gbmap->b); lfs3_btree_discardleaf(&gbmap->b);
#endif #endif
// and keep track of the committed gbmap for traversals // keep track of the committed gbmap for traversals
gbmap->b_p = gbmap->b; gbmap->b_p = gbmap->b;
return 0; return 0;
} }
@@ -10546,8 +10551,8 @@ static int lfs3_gbmap_commit(lfs3_t *lfs3, lfs3_btree_t *gbmap,
// note while this does takes a weight, it's limited to only a single // note while this does takes a weight, it's limited to only a single
// range, cross-range sets are not currently not supported // range, cross-range sets are not currently not supported
// //
// really this just provides a shortcut for bulk clearing ranges in // the purpose of weight is really just to provide a shortcut for bulk
// lfs3_alloc_lookgbmap // clearing ranges in lfs3_alloc_lookgbmap
static int lfs3_gbmap_mark_(lfs3_t *lfs3, lfs3_btree_t *gbmap, 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) {
// lookup gbmap range // lookup gbmap range
@@ -10710,6 +10715,36 @@ static int lfs3_gbmap_markbptr(lfs3_t *lfs3, lfs3_btree_t *gbmap,
} }
#endif #endif
#if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP)
// note this is not completely atomic, but worst case we just end up with
// only some ranges zeroed
static int lfs3_gbmap_zero(lfs3_t *lfs3, lfs3_btree_t *gbmap) {
lfs3_block_t block__ = -1;
while (true) {
lfs3_block_t weight__;
lfs3_stag_t tag__ = lfs3_gbmap_lookupnext(lfs3, gbmap, block__+1,
&block__, &weight__);
if (tag__ < 0) {
if (tag__ == LFS3_ERR_NOENT) {
break;
}
return tag__;
}
// 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);
if (err) {
return err;
}
}
}
return 0;
}
#endif
/// Block allocator /// /// Block allocator ///
@@ -10726,7 +10761,7 @@ static inline void lfs3_alloc_ckpoint_(lfs3_t *lfs3) {
lfs3->lookahead.ckpoint = lfs3->block_count; lfs3->lookahead.ckpoint = lfs3->block_count;
// ckpoint traversals, marking them as ckpointed + dirty and // ckpoint traversals, marking them as ckpointed + dirty and
// reseting any btrv state // resetting any btrv state
for (lfs3_handle_t *h = lfs3->handles; h; h = h->next) { for (lfs3_handle_t *h = lfs3->handles; h; h = h->next) {
if (lfs3_o_type(h->flags) == LFS3_type_TRV) { if (lfs3_o_type(h->flags) == LFS3_type_TRV) {
lfs3_mgc_ckpoint((lfs3_mgc_t*)h); lfs3_mgc_ckpoint((lfs3_mgc_t*)h);
@@ -10770,6 +10805,31 @@ static inline int lfs3_alloc_ckpoint(lfs3_t *lfs3) {
} }
#endif #endif
// can we repopulate the lookahead buffer?
#if !defined(LFS3_RDONLY)
static inline bool lfs3_alloc_canlookahead(const lfs3_t *lfs3) {
return lfs3->lookahead.known
<= lfs3_min(
lfs3->cfg->gc_lookahead_thresh,
lfs3_min(
8*lfs3->cfg->lookahead_size-1,
lfs3->block_count-1));
}
#endif
// can we repopulate the gbmap?
#if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP)
static inline bool lfs3_alloc_canlookgbmap(const lfs3_t *lfs3) {
return lfs3_f_isgbmap(lfs3->flags)
&& lfs3->gbmap.known
<= lfs3_min(
lfs3_max(
lfs3->cfg->gc_lookgbmap_thresh,
lfs3->cfg->lookgbmap_thresh),
lfs3->block_count-1);
}
#endif
// discard any lookahead/gbmap windows, this is necessary if block_count // discard any lookahead/gbmap windows, this is necessary if block_count
// changes // changes
#ifndef LFS3_RDONLY #ifndef LFS3_RDONLY
@@ -10778,12 +10838,10 @@ static inline void lfs3_alloc_discard(lfs3_t *lfs3) {
lfs3->lookahead.known = 0; lfs3->lookahead.known = 0;
lfs3_memset(lfs3->lookahead.buffer, 0, lfs3->cfg->lookahead_size); lfs3_memset(lfs3->lookahead.buffer, 0, lfs3->cfg->lookahead_size);
// discard/resync the gbmap window, the resync is necessary to avoid // discard the gbmap window
// disk changes breaking our mod math
#ifdef LFS3_GBMAP #ifdef LFS3_GBMAP
lfs3->gbmap.window = (lfs3->lookahead.window + lfs3->lookahead.off)
% lfs3->block_count;
lfs3->gbmap.known = 0; lfs3->gbmap.known = 0;
lfs3->gbmap.free = 0;
#endif #endif
} }
#endif #endif
@@ -10791,10 +10849,11 @@ static inline void lfs3_alloc_discard(lfs3_t *lfs3) {
// mark a block as in-use // mark a block as in-use
#ifndef LFS3_RDONLY #ifndef LFS3_RDONLY
static void lfs3_alloc_markinuse(lfs3_t *lfs3, lfs3_block_t block) { static void lfs3_alloc_markinuse(lfs3_t *lfs3, lfs3_block_t block) {
// TODO can this be simplified?
// translate to lookahead-relative // translate to lookahead-relative
lfs3_block_t block_ = (( lfs3_block_t block_ = ((
(lfs3_sblock_t)(block (lfs3_sblock_t)(block - lfs3->lookahead.window)
- (lfs3->lookahead.window + lfs3->lookahead.off))
// we only need this mess because C's mod is actually rem, and // we only need this mess because C's mod is actually rem, and
// we want real mod in case block_ goes negative // we want real mod in case block_ goes negative
% (lfs3_sblock_t)lfs3->block_count) % (lfs3_sblock_t)lfs3->block_count)
@@ -10833,37 +10892,6 @@ static void lfs3_alloc_markinusebptr(lfs3_t *lfs3,
} }
#endif #endif
// mark lookahead buffer to match gbmap
#if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP)
static int lfs3_alloc_markinusegbmap(lfs3_t *lfs3, lfs3_btree_t *gbmap,
lfs3_block_t known) {
lfs3_block_t block = lfs3->lookahead.window + lfs3->lookahead.off;
while (block < lfs3->lookahead.window + lfs3->lookahead.off + known) {
lfs3_block_t block_ = block % lfs3->block_count;
lfs3_block_t block__;
lfs3_stag_t tag = lfs3_gbmap_lookupnext(lfs3, gbmap, block_,
&block__, NULL);
if (tag < 0) {
return tag;
}
lfs3_block_t d = (block__+1) - block_;
// in-use? bad? etc? mark as in-use
if (tag != LFS3_TAG_BMFREE) {
// this could probably be more efficient, but cpu usage is
// not a big priority at the moment
for (lfs3_block_t i = 0; i < d; i++) {
lfs3_alloc_markinuse(lfs3, block_+i);
}
}
block += d;
}
return 0;
}
#endif
// needed in lfs3_alloc_adopt // needed in lfs3_alloc_adopt
static lfs3_sblock_t lfs3_alloc_findfree(lfs3_t *lfs3); static lfs3_sblock_t lfs3_alloc_findfree(lfs3_t *lfs3);
@@ -10875,97 +10903,136 @@ static void lfs3_alloc_adopt(lfs3_t *lfs3, lfs3_block_t known) {
8*lfs3->cfg->lookahead_size, 8*lfs3->cfg->lookahead_size,
known); known);
// signal that lookahead is full
lfs3->flags &= ~LFS3_I_LOOKAHEAD;
// eagerly find the next free block so lookahead scans can make // eagerly find the next free block so lookahead scans can make
// the most progress // the most progress
lfs3_alloc_findfree(lfs3); lfs3_sblock_t block = lfs3_alloc_findfree(lfs3);
if (block < 0 && block != LFS3_ERR_NOSPC) {
// scanning the lookahead buffer shouldn't error
LFS3_UNREACHABLE();
}
// signal that lookahead is full
lfs3->flags &= ~LFS3_I_LOOKAHEAD;
} }
#endif #endif
// can we repopulate the lookahead buffer?
#if !defined(LFS3_RDONLY)
static inline bool lfs3_alloc_islookahead(const lfs3_t *lfs3) {
return lfs3->lookahead.known
<= lfs3_min(
lfs3->cfg->gc_lookahead_thresh,
lfs3_min(
8*lfs3->cfg->lookahead_size-1,
lfs3->block_count-1));
}
#endif
// can we repopulate the gbmap?
#if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP) #if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP)
static inline bool lfs3_alloc_islookgbmap(const lfs3_t *lfs3) { static int lfs3_alloc_adoptgbmap(lfs3_t *lfs3,
return lfs3->gbmap.known const lfs3_btree_t *gbmap, lfs3_block_t known) {
<= lfs3_min( // adopt new gbmap
lfs3_max( lfs3->gbmap.known = known;
lfs3->cfg->gc_lookgbmap_thresh, lfs3->gbmap.b = *gbmap;
lfs3->cfg->lookgbmap_thresh),
lfs3->block_count-1); // eagerly find the next free block so lookgbmap scans can make
// the most progress
lfs3_sblock_t block = lfs3_alloc_findfree(lfs3);
if (block < 0 && block != LFS3_ERR_NOSPC) {
return block;
}
// signal that gbmap is full
lfs3->flags &= ~LFS3_I_LOOKGBMAP;
return 0;
} }
#endif #endif
// increment lookahead buffer // increment lookahead buffer
#ifndef LFS3_RDONLY #ifndef LFS3_RDONLY
static void lfs3_alloc_inc(lfs3_t *lfs3) { static void lfs3_alloc_inc(lfs3_t *lfs3) {
LFS3_ASSERT(lfs3->lookahead.known > 0);
// clear lookahead as we increment // clear lookahead as we increment
lfs3->lookahead.buffer[lfs3->lookahead.off / 8] lfs3->lookahead.buffer[lfs3->lookahead.off / 8]
&= ~(1 << (lfs3->lookahead.off % 8)); &= ~(1 << (lfs3->lookahead.off % 8));
// increment next/off // increment window/off
lfs3->lookahead.off += 1; lfs3->lookahead.window = (lfs3->lookahead.window + 1) % lfs3->block_count;
if (lfs3->lookahead.off == 8*lfs3->cfg->lookahead_size) { lfs3->lookahead.off = (lfs3->lookahead.off + 1)
lfs3->lookahead.off = 0; % (8*lfs3->cfg->lookahead_size);
lfs3->lookahead.window
= (lfs3->lookahead.window + 8*lfs3->cfg->lookahead_size)
% lfs3->block_count;
}
// decrement size // decrement size
lfs3->lookahead.known -= 1; lfs3->lookahead.known -= lfs3_min(1, lfs3->lookahead.known);
// decrement ckpoint // decrement ckpoint
lfs3->lookahead.ckpoint -= 1; lfs3->lookahead.ckpoint -= 1;
// signal that lookahead is no longer full
if (lfs3_alloc_islookahead(lfs3)) {
lfs3->flags |= LFS3_I_LOOKAHEAD;
}
// decrement gbmap known window // decrement gbmap known window
#ifdef LFS3_GBMAP #ifdef LFS3_GBMAP
if (lfs3_f_isgbmap(lfs3->flags)) { if (lfs3_f_isgbmap(lfs3->flags)) {
lfs3->gbmap.window = (lfs3->gbmap.window + 1) % lfs3->block_count; lfs3->gbmap.window = (lfs3->gbmap.window + 1) % lfs3->block_count;
lfs3->gbmap.known = lfs3_smax(lfs3->gbmap.known-1, 0); lfs3->gbmap.known -= lfs3_min(1, lfs3->gbmap.known);
if (lfs3->gbmap.free > 0) {
// signal that the gbmap is no longer full lfs3->gbmap.free -= 1;
if (lfs3_alloc_islookgbmap(lfs3)) { } else if (lfs3->gbmap.free < 0) {
lfs3->flags |= LFS3_I_LOOKGBMAP; lfs3->gbmap.free += 1;
} }
} }
#endif #endif
// signal that lookahead is no longer full
if (lfs3_alloc_canlookahead(lfs3)) {
lfs3->flags |= LFS3_I_LOOKAHEAD;
}
#ifdef LFS3_GBMAP
// signal that gbmap is no longer full
if (lfs3_alloc_canlookgbmap(lfs3)) {
lfs3->flags |= LFS3_I_LOOKGBMAP;
}
#endif
} }
#endif #endif
// find next free block in lookahead buffer, if there is one // find next free block in lookahead/gbmap, if there is one
#ifndef LFS3_RDONLY #ifndef LFS3_RDONLY
static lfs3_sblock_t lfs3_alloc_findfree(lfs3_t *lfs3) { static lfs3_sblock_t lfs3_alloc_findfree(lfs3_t *lfs3) {
while (lfs3->lookahead.known > 0) { while (true) {
if (!(lfs3->lookahead.buffer[lfs3->lookahead.off / 8] // known block in our gbmap?
& (1 << (lfs3->lookahead.off % 8)))) { if (LFS3_IFDEF_GBMAP(
// found a free block lfs3_f_isgbmap(lfs3->flags) && lfs3->gbmap.known > 0,
return (lfs3->lookahead.window + lfs3->lookahead.off) false)) {
% lfs3->block_count; #ifdef LFS3_GBMAP
// need to look up known block info
if (lfs3->gbmap.free == 0) {
lfs3_block_t block;
lfs3_stag_t tag = lfs3_gbmap_lookupnext(lfs3, &lfs3->gbmap.b,
lfs3->gbmap.window,
&block, NULL);
if (tag < 0) {
return tag;
}
// free?
if (tag == LFS3_TAG_BMFREE) {
lfs3->gbmap.free = lfs3_min(
(block+1) - lfs3->gbmap.window,
lfs3->gbmap.known);
// in-use? bad? erased? treat as in-use
} else {
lfs3->gbmap.free = -lfs3_min(
(block+1) - lfs3->gbmap.window,
lfs3->gbmap.known);
}
}
// free block in our gbmap?
if (lfs3->gbmap.free > 0) {
// found a free block
return lfs3->gbmap.window;
}
#endif
// known block in our lookahead buffer?
} else if (lfs3->lookahead.known > 0) {
// free block in our lookahead buffer?
if (!(lfs3->lookahead.buffer[lfs3->lookahead.off / 8]
& (1 << (lfs3->lookahead.off % 8)))) {
// found a free block
return lfs3->lookahead.window;
}
// out of known blocks
} else {
return LFS3_ERR_NOSPC;
} }
lfs3_alloc_inc(lfs3); lfs3_alloc_inc(lfs3);
} }
return LFS3_ERR_NOSPC;
} }
#endif #endif
@@ -10976,7 +11043,7 @@ static inline lfs3_size_t lfs3_graft_count(lfs3_size_t graft_count);
#ifndef LFS3_RDONLY #ifndef LFS3_RDONLY
static lfs3_sblock_t lfs3_alloc(lfs3_t *lfs3, uint32_t flags) { static lfs3_sblock_t lfs3_alloc(lfs3_t *lfs3, uint32_t flags) {
while (true) { while (true) {
// scan our lookahead buffer for free blocks // scan our lookahead/gbmap for free blocks
lfs3_sblock_t block = lfs3_alloc_findfree(lfs3); lfs3_sblock_t block = lfs3_alloc_findfree(lfs3);
if (block < 0 && block != LFS3_ERR_NOSPC) { if (block < 0 && block != LFS3_ERR_NOSPC) {
return block; return block;
@@ -11002,7 +11069,10 @@ static lfs3_sblock_t lfs3_alloc(lfs3_t *lfs3, uint32_t flags) {
// eagerly find the next free block to maximize how many blocks // eagerly find the next free block to maximize how many blocks
// lfs3_alloc_ckpoint makes available for scanning // lfs3_alloc_ckpoint makes available for scanning
lfs3_alloc_inc(lfs3); lfs3_alloc_inc(lfs3);
lfs3_alloc_findfree(lfs3); lfs3_sblock_t block_ = lfs3_alloc_findfree(lfs3);
if (block_ < 0 && block_ != LFS3_ERR_NOSPC) {
return block_;
}
#ifdef LFS3_DBGALLOCS #ifdef LFS3_DBGALLOCS
LFS3_DEBUG("Allocated block 0x%"PRIx32", " LFS3_DEBUG("Allocated block 0x%"PRIx32", "
@@ -11029,28 +11099,6 @@ static lfs3_sblock_t lfs3_alloc(lfs3_t *lfs3, uint32_t flags) {
return LFS3_ERR_NOSPC; return LFS3_ERR_NOSPC;
} }
// no blocks in our lookahead buffer?
// known blocks in our gbmap?
#ifdef LFS3_GBMAP
if (lfs3_f_isgbmap(lfs3->flags) && lfs3->gbmap.known > 0) {
int err = lfs3_alloc_markinusegbmap(lfs3,
&lfs3->gbmap.b, lfs3_min(
lfs3->gbmap.known,
lfs3->lookahead.ckpoint));
if (err) {
return err;
}
// with known blocks we can trust the block state to be
// exact
lfs3_alloc_adopt(lfs3, lfs3_min(
lfs3->gbmap.known,
lfs3->lookahead.ckpoint));
continue;
}
#endif
// no known blocks? fallback to scanning the filesystem // no known blocks? fallback to scanning the filesystem
// //
// traverse the filesystem, building up knowledge of what blocks are // traverse the filesystem, building up knowledge of what blocks are
@@ -11086,48 +11134,6 @@ static lfs3_sblock_t lfs3_alloc(lfs3_t *lfs3, uint32_t flags) {
} }
#endif #endif
#if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP)
// note this is not completely atomic, but worst case we just end up with
// only some ranges zeroed
static int lfs3_alloc_zerogbmap(lfs3_t *lfs3, lfs3_btree_t *gbmap) {
lfs3_block_t block__ = -1;
while (true) {
lfs3_block_t weight__;
lfs3_stag_t tag__ = lfs3_gbmap_lookupnext(lfs3, gbmap, block__+1,
&block__, &weight__);
if (tag__ < 0) {
if (tag__ == LFS3_ERR_NOENT) {
break;
}
return tag__;
}
// mark in-use ranges as free
if (tag__ == LFS3_TAG_BMINUSE) {
int err = lfs3_gbmap_mark_(lfs3, gbmap, block__, weight__,
LFS3_TAG_BMFREE);
if (err) {
return err;
}
}
}
return 0;
}
#endif
#if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP)
static void lfs3_alloc_adoptgbmap(lfs3_t *lfs3,
const lfs3_btree_t *gbmap, lfs3_block_t known) {
// adopt new gbmap
lfs3->gbmap.known = known;
lfs3->gbmap.b = *gbmap;
// signal that gbmap is full
lfs3->flags &= ~LFS3_I_LOOKGBMAP;
}
#endif
#if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP) #if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP)
static int lfs3_alloc_lookgbmap(lfs3_t *lfs3) { static int lfs3_alloc_lookgbmap(lfs3_t *lfs3) {
LFS3_INFO("Repopulating gbmap (gbmap %"PRId32"/%"PRId32")", LFS3_INFO("Repopulating gbmap (gbmap %"PRId32"/%"PRId32")",
@@ -11142,7 +11148,7 @@ static int lfs3_alloc_lookgbmap(lfs3_t *lfs3) {
// we do this instead of creating a new gbmap to (1) preserve any // we do this instead of creating a new gbmap to (1) preserve any
// erased/bad info and (2) try to best use any available // erased/bad info and (2) try to best use any available
// erased-state // erased-state
int err = lfs3_alloc_zerogbmap(lfs3, &gbmap_); int err = lfs3_gbmap_zero(lfs3, &gbmap_);
if (err) { if (err) {
return err; return err;
} }
@@ -11177,8 +11183,7 @@ static int lfs3_alloc_lookgbmap(lfs3_t *lfs3) {
// this avoids extra writing at a risk of needing to repopulate the // this avoids extra writing at a risk of needing to repopulate the
// gbmap if we lose power // gbmap if we lose power
// //
lfs3_alloc_adoptgbmap(lfs3, &gbmap_, lfs3->lookahead.ckpoint); return lfs3_alloc_adoptgbmap(lfs3, &gbmap_, lfs3->lookahead.ckpoint);
return 0;
} }
#endif #endif
@@ -14851,7 +14856,7 @@ static int lfs3_init(lfs3_t *lfs3, uint32_t flags,
lfs3->flags = flags lfs3->flags = flags
// assume we contain orphans until proven otherwise // assume we contain orphans until proven otherwise
| LFS3_IFDEF_RDONLY(0, LFS3_I_MKCONSISTENT) | LFS3_IFDEF_RDONLY(0, LFS3_I_MKCONSISTENT)
// default to an empty lookahead // default to lookaheadable
| LFS3_IFDEF_RDONLY(0, LFS3_I_LOOKAHEAD) | LFS3_IFDEF_RDONLY(0, LFS3_I_LOOKAHEAD)
// default to assuming we need compaction somewhere, worst case // default to assuming we need compaction somewhere, worst case
// this just makes lfs3_fs_gc read more than is strictly needed // this just makes lfs3_fs_gc read more than is strictly needed
@@ -15628,7 +15633,7 @@ static int lfs3_mountinited(lfs3_t *lfs3) {
// //
// unfortunately the dependency of the gbmap on block allocation // unfortunately the dependency of the gbmap on block allocation
// means this rarely includes the entire disk // means this rarely includes the entire disk
if (lfs3_alloc_islookgbmap(lfs3)) { if (lfs3_alloc_canlookgbmap(lfs3)) {
lfs3->flags |= LFS3_I_LOOKGBMAP; lfs3->flags |= LFS3_I_LOOKGBMAP;
} }
#endif #endif
@@ -16072,7 +16077,7 @@ failed:;
// note lfs3_fs_stat should never go to disk // note lfs3_fs_stat should never go to disk
int lfs3_fs_stat(lfs3_t *lfs3, struct lfs3_fsinfo *fsinfo) { int lfs3_fs_stat(lfs3_t *lfs3, struct lfs3_fsinfo *fsinfo) {
// return various filesystem flags // return various filesystem flags
fsinfo->flags = lfs3->flags & ( uint32_t flags = lfs3->flags & (
LFS3_I_RDONLY LFS3_I_RDONLY
| LFS3_I_FLUSH | LFS3_I_FLUSH
| LFS3_I_SYNC | LFS3_I_SYNC
@@ -16089,11 +16094,19 @@ int lfs3_fs_stat(lfs3_t *lfs3, struct lfs3_fsinfo *fsinfo) {
| LFS3_I_CKMETA | LFS3_I_CKMETA
| LFS3_I_CKDATA | LFS3_I_CKDATA
| LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, 0)); | LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, 0));
// some flags we calculate on demand // some flags we calculate on demand
// internally, LFS3_I_MKCONSISTENT just means we may have orphaned
// stickynotes, need to also include any grms
#ifndef LFS3_RDONLY #ifndef LFS3_RDONLY
fsinfo->flags |= (lfs3_grm_count(lfs3) > 0) ? LFS3_I_MKCONSISTENT : 0; if (lfs3_t_ismkconsistent(lfs3->flags) || lfs3_grm_count(lfs3) > 0) {
flags |= LFS3_I_MKCONSISTENT;
}
#endif #endif
fsinfo->flags = flags;
// return filesystem config, this may come from disk // return filesystem config, this may come from disk
fsinfo->block_size = lfs3->cfg->block_size; fsinfo->block_size = lfs3->cfg->block_size;
fsinfo->block_count = lfs3->block_count; fsinfo->block_count = lfs3->block_count;
@@ -16595,8 +16608,7 @@ int lfs3_fs_mkgbmap(lfs3_t *lfs3) {
// sync gbmap/lookahead windows, note this needs to happen after any // sync gbmap/lookahead windows, note this needs to happen after any
// block allocation in lfs3_gbmap_commit // block allocation in lfs3_gbmap_commit
lfs3->gbmap.window = (lfs3->lookahead.window + lfs3->lookahead.off) lfs3->gbmap.window = lfs3->lookahead.window;
% lfs3->block_count;
// checkpoint the allocator again, this should trigger a // checkpoint the allocator again, this should trigger a
// repopulation scan // repopulation scan
+1
View File
@@ -1221,6 +1221,7 @@ typedef struct lfs3_grm {
typedef struct lfs3_gbmap { typedef struct lfs3_gbmap {
lfs3_block_t window; lfs3_block_t window;
lfs3_block_t known; lfs3_block_t known;
lfs3_sblock_t free;
lfs3_btree_t b; lfs3_btree_t b;
lfs3_btree_t b_p; lfs3_btree_t b_p;
} lfs3_gbmap_t; } lfs3_gbmap_t;