preerase: Added/extended gc preerase tests, fixed a couple more bugs

This gets gc tests working with both LFS3_GC=1 and LFS3_PREERASE=1, and
adds a few more tests that should round out the necessary preerase test
coverage:

- test_gc_preerase_progress - A simple test that checks if
  LFS3_GC_PREERASE clears the LFS3_I_PREERASE flag, as well as some
  checks against emubd's erase counters to see if it actually did
  anything (erased >= cycles - preerased, erased < 1.25*cycles -
  preerased).

- test_gc_preerase_relaxed - A test with a couple different
  GC_PREERASE_COUNTs, and checks against emubd's erase counters to make
  sure they demonstrate different levels of pre-erasing (erased >=
  cycles - preerased, erased < 1.25*cycles - preerased).

- test_gc_preerase_decreasing - A test with increasing
  GC_PREERASE_COUNTs, measuring min/max/avg emubd's erase counters, and
  asserting if the avg delta is worst than ~0.75x.

  This is probably the most valuable one, if only for the extra analysis
  available when debugging.

And, just so we know these tests are working, they found a few more bugs:

- We were calling the implicitly ckpointing variant of lfs3_mdir_commit
  in lfs3_allocclaim, when the block we just allocated is still very
  much in-flight!

  An easy one-character fix (lfs3_mdir_commit -> lfs3_mdir_commit_, the
  non-ckpointing variant), but was a pain to track down. I guess the
  good news is test_gc_nospc has proven to be a very valuable test.

  Added a comment to hopefully discourage a regression.

- Found a wacky catch-22 where the block we just preerased can be
  allocated during the gbmap commit that tries to save the preerased
  ecksum.

  This is somewhat expected during normal operation, the gbmap may need
  a few allocations before the preeraser can get ahead, but we need to
  make sure not to increment the preeraser's known window if the
  preerased block is no longer in the gbmap's known window.

  Fortunately(?), our preeraser state is pretty robust to bugs like this
  due to being reset (forcing ecksum refetches) during gbmap rebuilds.
  However, preeraser state falling out-of-sync risks unnecessary
  erases/surprising latency during block allocation.

- Found a typo where we used lfs3->cfg->block_count instead of
  lfs3->block_count again... Hopefully this becomes impossible after the
  planned config rework...

---

A few other test tweaks:

- Added LFS3_F/M_REVPERTURB flags where necessary to support PREERASE.
  Previously the tests only worked with LFS3_YES_REVPERTURB=1.

- Adopt lfs3_handle_isopen over lfs3.handles == lfs3.gc.t.h. With the
  logic change to use the traversal handle to track its position in the
  open file handles, these simplified isopen checks no longer work.

- Prefer toml lists for multiple ifdefs (hey, these were at least useful
  for testing test.py's ifdef exprs).

Code changes:

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

                    code          stack          ctx
  gbmap before:    38616           2144          776
  gbmap after:     38616 (+0.0%)   2144 (+0.0%)  776 (+0.0%)

                    code          stack          ctx
  preerase before: 39232           2168          796
  preerase after:  39280 (+0.1%)   2168 (+0.0%)  796 (+0.0%)
This commit is contained in:
Christopher Haster
2026-01-03 19:23:21 -06:00
parent 9bd44aec12
commit 29550900f2
5 changed files with 612 additions and 68 deletions
+1 -1
View File
@@ -383,7 +383,7 @@ static inline int32_t lfs3_smax(int32_t a, int32_t b) {
}
// Absolute value of signed numbers
static inline int32_t lfs3_abs(int32_t a) {
static inline uint32_t lfs3_abs(int32_t a) {
return (a < 0) ? -a : a;
}