gbmap: Added LFS3_T_REBUILDGBMAP and friends
This adds LFS3_T_REBUILDGBMAP and friends, and enables incremental gbmap
rebuilds as a part of gc/traversal work:
LFS3_M_REBUILDGBMAP 0x00000400 Rebuild the gbmap
LFS3_GC_REBUILDGBMAP 0x00000400 Rebuild the gbmap
LFS3_I_REBUILDGBMAP 0x00000400 The gbmap is not full
LFS3_T_REBUILDGBMAP 0x00000400 Rebuild the gbmap
On paper, this is more or less identical to repopulating the lookahead
buffer -- traverse the filesystem, mark blocks as in-use, adopt the new
gbmap/lookahead buffer on success -- but a couple nuances make
rebuilding the gbmap a bit trickier:
- Unlike the lookahead buffer, which eagerly zeros in allocation, we
need an explicit zeroing pass before we start marking blocks as
in-use. This means multiple traversals can potentially conflict with
each other, risking the adoption of a clobbered gbmap.
- The gbmap, which stores information on disk, relies on block
allocation and the temporary "in-flight window" defined by allocator
ckpoints to avoid circular block states during gbmap rebuilds. This
makes gbmap rebuilds sensitive to allocator ckpoints, which we
consider more-or-less a noop in other parts of the system.
Though now that I'm writing this, it might have been possible to
instead include gbmap rebuild snapshots in fs traversals... but that
would probably have been much more complicated.
- Rebuilding the gbmap requires writing to disk and is generally much
more expensive/destructive. We want to avoid trying to rebuild the
gbmap when it's not possible to actually make progress.
On top of this, the current trv-clobber system is a delicate,
error-prone mess.
---
To simplify everything related to gbmap rebuilds, I added a new
internal traversal flag: LFS3_t_CKPOINTED:
LFS3_t_CKPOINTED 0x04000000 Filesystem ckpointed during traversal
LFS3_t_CKPOINTED is set, unconditionally, on all open traversals in
lfs3_alloc_ckpoint, and provides a simple, robust mechanism for checking
if _any_ allocator checkpoints have occured since a traversal was
started. Since lfs3_alloc_ckpoint is required before any block
allocation, this provides a strong guarantee that nothing funny happened
to any allocator state during a traversal.
This makes lfs3_alloc_ckpoint a bit less cheap, but the strong
guarantees that allocator state is unmodified during traversal are well
worth it.
This makes both lookahead and gbmap passes simpler, safer, and easier to
reason about.
I'd like to adopt something similar+stronger for LFs3_t_MUTATED, and
reduce this back to two flags, but that can be a future commit.
---
Unfortunately due to the potential for recursion, this ended up reusing
less logic between lfs3_alloc_rebuildgbmap and lfs3_mtree_gc than I had
hoped, but at like the main chunks (lfs3_alloc_remap,
lfs3_gbmap_setbptr, lfs3_alloc_adoptgbmap) could be split out into
common functions.
The result is a decent chunk of code and stack, but the value is high as
incremental gbmap rebuilds are the only option to reduce the latency
spikes introduced by the gbmap allocator (it's not significantly worse
than the lookahead buffer, but both do require traversing the entire
filesystem):
code stack ctx
before: 37164 2352 684
after: 37208 (+0.1%) 2360 (+0.3%) 684 (+0.0%)
code stack ctx
gbmap before: 39708 2376 848
gbmap after: 40100 (+1.0%) 2432 (+2.4%) 848 (+0.0%)
Note the gbmap build is now measured with LFS3_GBMAP=1, instead of
LFS3_YES_GBMAP=1 (maybe-gbmap) as before. This includes the cost of
mkgbmap, lfs3_f_isgbmap, etc.
This commit is contained in:
@@ -236,6 +236,10 @@ enum lfs3_type {
|
||||
#define LFS3_M_LOOKAHEAD \
|
||||
0x00000200 // Populate lookahead buffer
|
||||
#endif
|
||||
#if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP)
|
||||
#define LFS3_M_REBUILDGBMAP \
|
||||
0x00000400 // Rebuild the gbmap
|
||||
#endif
|
||||
#ifndef LFS3_RDONLY
|
||||
#define LFS3_M_COMPACT 0x00000800 // Compact metadata logs
|
||||
#endif
|
||||
@@ -277,6 +281,10 @@ enum lfs3_type {
|
||||
#define LFS3_I_LOOKAHEAD \
|
||||
0x00000200 // Lookahead buffer is not full
|
||||
#endif
|
||||
#if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP)
|
||||
#define LFS3_I_REBUILDGBMAP \
|
||||
0x00000400 // The gbmap is not full
|
||||
#endif
|
||||
#ifndef LFS3_RDONLY
|
||||
#define LFS3_I_COMPACT 0x00000800 // Filesystem may have uncompacted metadata
|
||||
#endif
|
||||
@@ -318,6 +326,10 @@ enum lfs3_btype {
|
||||
#define LFS3_T_LOOKAHEAD \
|
||||
0x00000200 // Populate lookahead buffer
|
||||
#endif
|
||||
#if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP)
|
||||
#define LFS3_T_REBUILDGBMAP \
|
||||
0x00000400 // Rebuild the gbmap
|
||||
#endif
|
||||
#ifndef LFS3_RDONLY
|
||||
#define LFS3_T_COMPACT 0x00000800 // Compact metadata logs
|
||||
#endif
|
||||
@@ -329,6 +341,8 @@ enum lfs3_btype {
|
||||
#define LFS3_t_TSTATE 0x000f0000 // The current traversal state
|
||||
#define LFS3_t_BTYPE 0x00f00000 // The current block type
|
||||
#define LFS3_t_ZOMBIE 0x08000000 // File has been removed
|
||||
#define LFS3_t_CKPOINTED \
|
||||
0x04000000 // Filesystem ckpointed during traversal
|
||||
#define LFS3_t_DIRTY 0x02000000 // Filesystem modified during traversal
|
||||
#define LFS3_t_MUTATED 0x01000000 // Filesystem modified by traversal
|
||||
|
||||
@@ -341,6 +355,10 @@ enum lfs3_btype {
|
||||
#define LFS3_GC_LOOKAHEAD \
|
||||
0x00000200 // Populate lookahead buffer
|
||||
#endif
|
||||
#if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP)
|
||||
#define LFS3_GC_REBUILDGBMAP \
|
||||
0x00000400 // Rebuild the gbmap
|
||||
#endif
|
||||
#ifndef LFS3_RDONLY
|
||||
#define LFS3_GC_COMPACT 0x00000800 // Compact metadata logs
|
||||
#endif
|
||||
@@ -810,6 +828,10 @@ typedef struct lfs3_trv {
|
||||
// bshrub/btree traversal state
|
||||
lfs3_sbid_t bid;
|
||||
|
||||
// rebuild gbmap when traversing with rebuildgbmap
|
||||
#ifdef LFS3_GBMAP
|
||||
lfs3_btree_t gbmap_;
|
||||
#endif
|
||||
// recalculate gcksum when traversing with ckmeta
|
||||
uint32_t gcksum;
|
||||
// pending blocks, only used in lfs3_trv_read
|
||||
|
||||
Reference in New Issue
Block a user