trv: Split lfs3_trv_t -> lfs3_trv_t, lfs3_mgc_t, and lfs3_mtrv_t

A big downside of LFS3_T_REBUILDGBMAP is the addition of an lfs3_btree_t
struct to _every_ traversal object.

Unfortunately, I don't see a way around this. We need to track the new
gbmap snapshot _somewhere_, and other options (such as a global gbmap.b_
snapshot) just move the RAM around without actually saving anything.

To at least mitigate this internally, this splits lfs3_trv_t into
distinct lfs3_trv_t, lfs3_mgc_t, and lfs3_mtrv_t structs that capture
only the relevant state for internal traversal layers:

- lfs3_mtree_traverse <- lfs3_mtrv_t
- lfs3_mtree_gc       <- lfs3_mgc_t (contains lfs3_mtrv_t)
- lfs3_trv_read       <- lfs3_trv_t (contains lfs3_mgc_t)

This minimizes the impact of the gbmap rebuild snapshots, and saves a
big chunk of RAM. As a plus it also saves RAM in the default build by
limiting the 2-block block queue to the high-level lfs3_trv_read API:

                 code          stack          ctx
  before:       37176           2360          684
  after:        37176 (+0.0%)   2352 (-0.3%)  684 (+0.0%)

                 code          stack          ctx
  gbmap before: 40060           2432          848
  gbmap after:  40024 (-0.1%)   2368 (-2.6%)  848 (+0.0%)

The main downside? Our field names are continuing in their
ridiculousness:

  lfs3.gc.gc.t.b.h.flags // where else would the global gc flags be?
This commit is contained in:
Christopher Haster
2025-10-16 00:10:21 -05:00
parent 06bc4dff04
commit fb90bf976c
6 changed files with 299 additions and 285 deletions
+18 -18
View File
@@ -57,7 +57,7 @@ code = '''
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags & LFS3_I_LOOKAHEAD);
assert(lfs3.handles != &lfs3.gc.trv.b.h);
assert(lfs3.handles != &lfs3.gc.gc.t.b.h);
// run GC until we make progress
for (lfs3_block_t i = 0;; i++) {
@@ -127,11 +127,11 @@ code = '''
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags & LFS3_I_LOOKAHEAD);
assert(lfs3.handles != &lfs3.gc.trv.b.h);
assert(lfs3.handles != &lfs3.gc.gc.t.b.h);
// run GC one step
lfs3_fs_gc(&lfs3) => 0;
assert(lfs3.handles == &lfs3.gc.trv.b.h);
assert(lfs3.handles == &lfs3.gc.gc.t.b.h);
// mutate the filesystem
lfs3_file_open(&lfs3, &file, "spider",
@@ -143,7 +143,7 @@ code = '''
lfs3_file_close(&lfs3, &file) => 0;
// run GC until our traversal is done
while (lfs3.handles == &lfs3.gc.trv.b.h) {
while (lfs3.handles == &lfs3.gc.gc.t.b.h) {
lfs3_fs_gc(&lfs3) => 0;
}
@@ -207,7 +207,7 @@ code = '''
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags & LFS3_I_REBUILDGBMAP);
assert(lfs3.handles != &lfs3.gc.trv.b.h);
assert(lfs3.handles != &lfs3.gc.gc.t.b.h);
// run GC until we make progress
for (lfs3_block_t i = 0;; i++) {
@@ -280,11 +280,11 @@ code = '''
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags & LFS3_I_REBUILDGBMAP);
assert(lfs3.handles != &lfs3.gc.trv.b.h);
assert(lfs3.handles != &lfs3.gc.gc.t.b.h);
// run GC one step
lfs3_fs_gc(&lfs3) => 0;
assert(lfs3.handles == &lfs3.gc.trv.b.h);
assert(lfs3.handles == &lfs3.gc.gc.t.b.h);
// mutate the filesystem
lfs3_file_open(&lfs3, &file, "spider",
@@ -296,7 +296,7 @@ code = '''
lfs3_file_close(&lfs3, &file) => 0;
// run GC until our traversal is done
while (lfs3.handles == &lfs3.gc.trv.b.h) {
while (lfs3.handles == &lfs3.gc.gc.t.b.h) {
lfs3_fs_gc(&lfs3) => 0;
}
@@ -368,7 +368,7 @@ code = '''
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags & LFS3_I_COMPACT);
assert(lfs3.handles != &lfs3.gc.trv.b.h);
assert(lfs3.handles != &lfs3.gc.gc.t.b.h);
// run GC until we make progress
for (lfs3_block_t i = 0;; i++) {
@@ -460,19 +460,19 @@ code = '''
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags & LFS3_I_COMPACT);
assert(lfs3.handles != &lfs3.gc.trv.b.h);
assert(lfs3.handles != &lfs3.gc.gc.t.b.h);
// run GC one traversal + one step
while (true) {
lfs3_fs_gc(&lfs3) => 0;
// internal traversal done?
if (lfs3.handles != &lfs3.gc.trv.b.h) {
if (lfs3.handles != &lfs3.gc.gc.t.b.h) {
break;
}
}
lfs3_fs_gc(&lfs3) => 0;
assert(lfs3.handles == &lfs3.gc.trv.b.h);
assert(lfs3.handles == &lfs3.gc.gc.t.b.h);
// mutate the filesystem
lfs3_file_rewind(&lfs3, &file) => 0;
@@ -483,7 +483,7 @@ code = '''
lfs3_file_sync(&lfs3, &file) => 0;
// run GC until our traversal is done (twice for compact)
while (lfs3.handles == &lfs3.gc.trv.b.h) {
while (lfs3.handles == &lfs3.gc.gc.t.b.h) {
lfs3_fs_gc(&lfs3) => 0;
}
@@ -587,7 +587,7 @@ code = '''
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags & LFS3_I_MKCONSISTENT);
assert(lfs3.handles != &lfs3.gc.trv.b.h);
assert(lfs3.handles != &lfs3.gc.gc.t.b.h);
// run GC until we make progress
for (lfs3_block_t i = 0;; i++) {
@@ -693,7 +693,7 @@ code = '''
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags & LFS3_I_MKCONSISTENT);
#ifdef LFS3_GC
assert(lfs3.handles != &lfs3.gc.trv.b.h);
assert(lfs3.handles != &lfs3.gc.gc.t.b.h);
#endif
// call lfs3_fs_mkconsistent
@@ -797,9 +797,9 @@ code = '''
}
// run GC one step
assert(lfs3.handles != &lfs3.gc.trv.b.h);
assert(lfs3.handles != &lfs3.gc.gc.t.b.h);
lfs3_fs_gc(&lfs3) => 0;
assert(lfs3.handles == &lfs3.gc.trv.b.h);
assert(lfs3.handles == &lfs3.gc.gc.t.b.h);
// create the rest of the orphans after GC has started
for (lfs3_size_t i = 0; i < ORPHANS; i++) {
@@ -821,7 +821,7 @@ code = '''
assert(fsinfo.flags & LFS3_I_MKCONSISTENT);
// run GC until our traversal is done
while (lfs3.handles == &lfs3.gc.trv.b.h) {
while (lfs3.handles == &lfs3.gc.gc.t.b.h) {
lfs3_fs_gc(&lfs3) => 0;
}