gbmap: Added mkgbmap and rmgbmap for enabling/disabling the gbmap

These two functions allow changing whether or not the gbmap is in use
after format:

  // Enable the global on-disk block-map
  //
  // Returns a negative error code on failure. Does nothing if a gbmap
  // already exists.
  int lfs3_fs_mkgbmap(lfs3_t *lfs3);

  // Disable the global on-disk block-map
  //
  // Returns a negative error code on failure. Does nothing if no gbmap
  // is found.
  int lfs3_fs_rmgbmap(lfs3_t *lfs3);

rmgbmap was easy enough, but implementing mkgbmap turned out to be
surprisingly tricky due to how gstate permeates the system:

- Even if we zero gstate when we removing the gbmap, mounting the
  image on a driver that doesn't understand the gbmap results in garbage
  gstate over time as mdir compacts drop unknown gdeltas.

  I think this sort of implicit gdelta cleanup is a good thing, but the
  possibility of garbage gstate is a bit annoying.

  Example A: the dbg scripts are currently printing a bunch of warnings
  for corrupt gstate that can be safely ignored.

  To support recovering from garbage gstate in mkgbmap, I changed
  lfs3_fs_commitgdelta to _always_ track p state even when disabled. We
  already needed to do this in lfs3_fs_flush/consumegdelta anyways,
  since we don't know if the gbmap is used until parsing wcompat flags.

- The commit that enables the gbmap is tricky. We need the gbmap enabled
  to calculate the new gdelta, but we also need it disabled so we don't
  traverse the existing gbmap_p (which may be garbage).

  As a workaround I added gbmap.b_p, which is in theory redundant with
  gbmap_p, but (1) avoids needing to decode gbmap_p during traversals,
  and (2) allows the two to temporarily fall out-of-sync in mkgbmap.

  This means we potentially have 5 (!) snaphots flying around when
  rebuilding the gbmap, which is starting to get a bit silly. But this
  was also motivated by gbmap_p decoding adding roughly the same amount
  of RAM to lfs3_mtree_traverse_, so the total RAM usage should in
  theory be roughly the same.

  There might be a better solution, but this at least gets mkgbmap
  working. The gbmap builds are not our most RAM senstive configurations
  anyways.

---

Also added a couple more tests in test_gbmap to test these:

- test_gbmap_files
- test_gbmap_rmgbmap
- test_gbmap_mkgbmap
- test_gbmap_rmmkgbmap
- test_gbmap_mkrmgbmap

And an explicit wraparound test to test_alloc. This was loosely implied
by the nospc tests, but it's probably better to have an explicit test.
The only downside is this implementation is limited to files:

- test_alloc_wraparound_files

---

Note we are currently dealing with three different configurations:
no-gbmap (the default), yes-gbmap (LFS3_YES_GBMAP), and maybe-gbmap
(LFS3_GBMAP + LFS3_F_GBMAP at runtime).

It only makes sense to include these in maybe-gbmap mode, so this is the
only mode with a notable code increase. However these functions are
relatively cheap. The stack/ctx changes also affect yes-gbmap, but
should mostly cancel out, see above:

                       code          stack          ctx
  no-gbmap before:    37168           2352          684
  no-gbmap after:     37168 (+0.0%)   2352 (+0.0%)  684 (+0.0%)

                       code          stack          ctx
  maybe-gbmap before: 39292           2456          800
  maybe-gbmap after:  39688 (+1.0%)   2392 (-2.6%)  852 (+6.5%)

                       code          stack          ctx
  yes-gbmap before:   39116           2456          800
  yes-gbmap after:    39156 (+0.1%)   2392 (-2.6%)  852 (+6.5%)
This commit is contained in:
Christopher Haster
2025-10-11 13:34:30 -05:00
parent 9e45249b29
commit b5a94f3397
4 changed files with 1027 additions and 32 deletions
+17
View File
@@ -849,6 +849,7 @@ typedef struct lfs3_gbmap {
lfs3_block_t window;
lfs3_block_t known;
lfs3_btree_t b;
lfs3_btree_t b_p;
} lfs3_gbmap_t;
@@ -1413,5 +1414,21 @@ int lfs3_fs_unck(lfs3_t *lfs3, uint32_t flags);
int lfs3_fs_grow(lfs3_t *lfs3, lfs3_size_t block_count);
#endif
// Enable the global on-disk block-map
//
// Returns a negative error code on failure. Does nothing if a gbmap
// already exists.
#if !defined(LFs3_RDONLY) && defined(LFS3_GBMAP) && !defined(LFS3_YES_GBMAP)
int lfs3_fs_mkgbmap(lfs3_t *lfs3);
#endif
// Disable the global on-disk block-map
//
// Returns a negative error code on failure. Does nothing if no gbmap
// is found.
#if !defined(LFs3_RDONLY) && defined(LFS3_GBMAP) && !defined(LFS3_YES_GBMAP)
int lfs3_fs_rmgbmap(lfs3_t *lfs3);
#endif
#endif