From 0b48faf898f9a07e8e47642beff123c6827ece2a Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 2 Dec 2025 21:21:05 -0600 Subject: [PATCH] Started prototyping best-effort fsinfo.known_free/inuse fields The idea here was that we could provide best-effort known in-use/free block info (and eventually pre-erased and bad block info) as a cheaper alternative to lfs3_fs_usage. It's probably still not what users expect from lfs3_fs_stat, but may be useful as debug/diagnostic info: - fsinfo.known_free - Number of known free blocks - fsinfo.known_inuse - Number of known in-use blocks - fsinfo.known_preerased* - Number of pre-erased blocks - fsinfo.known_bad* - Number of bad blocks - fsinfo.block_count-(all of the above) - Number of unknown blocks But while known_free/known_inuse is easy enough to find from the lookahead buffer, it's surprisingly tricky from the gbmap. The best option I can think of requires scanning the gbmap in O(d log_b d) either (1) during mount, (2) during mkconsistent, or (3) during lookahead scans. And that much extra work for debug/diagnostic info seems like a poor tradeoff. Note, though, that after scanning once, in theory the info would be ~free to maintain during gbmap rebuilds. Will revert. Code changes: code stack ctx before: 35160 2136 660 after: 35220 (+0.2%) 2136 (+0.0%) 660 (+0.0%) code stack ctx gbmap before: 38020 2152 772 gbmap after: 38116 (+0.3%) 2152 (+0.0%) 776 (+0.5%) --- lfs3.c | 31 +++++++++++++++++++++++++++++++ lfs3.h | 13 ++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/lfs3.c b/lfs3.c index 327ed0cd..96f7d6a2 100644 --- a/lfs3.c +++ b/lfs3.c @@ -10459,6 +10459,8 @@ eot:; static void lfs3_gbmap_init(lfs3_gbmap_t *gbmap) { gbmap->window = 0; gbmap->known = 0; + // we lazily populate this during lookahead scans + gbmap->known_free = -1; lfs3_btree_init(&gbmap->b); lfs3_btree_init(&gbmap->b_p); } @@ -16069,6 +16071,7 @@ failed:; /// Other filesystem things /// +// note lfs3_fs_stat should never go to disk int lfs3_fs_stat(lfs3_t *lfs3, struct lfs3_fsinfo *fsinfo) { // return various filesystem flags fsinfo->flags = lfs3->flags & ( @@ -16099,6 +16102,34 @@ int lfs3_fs_stat(lfs3_t *lfs3, struct lfs3_fsinfo *fsinfo) { fsinfo->name_limit = lfs3->name_limit; fsinfo->file_limit = lfs3->file_limit; + // return best effort knowledge of block usage + + // if we have a gbmap, we choose whichever allocator has more + // information, otherwise we fall back to the lookahead buffer + if (LFS3_IFDEF_GBMAP( + lfs3_f_isgbmap(lfs3->flags) + // known_free=-1 indicates we don't know the free/inuse + // breakdown yet + && lfs3->gbmap.known_free != -1 + && lfs3->gbmap.known >= lfs3->lookahead.known, + false)) { + #ifdef LFS3_GBMAP + fsinfo->known_free = lfs3->gbmap.known_free; + fsinfo->known_inuse = lfs3->gbmap.known - lfs3->gbmap.known_free; + #endif + + } else { + lfs3_block_t free = 0; + for (lfs3_size_t i = 0; i < lfs3->lookahead.known; i++) { + if (!(lfs3->lookahead.buffer[(lfs3->lookahead.off+i) / 8] + & (1 << ((lfs3->lookahead.off+i) % 8)))) { + free += 1; + } + } + fsinfo->known_free = free; + fsinfo->known_inuse = lfs3->lookahead.known - free; + } + return 0; } diff --git a/lfs3.h b/lfs3.h index b007612d..59d9654a 100644 --- a/lfs3.h +++ b/lfs3.h @@ -687,6 +687,16 @@ struct lfs3_fsinfo { // Upper limit on the size of files in bytes. lfs3_off_t file_limit; + + // Number of known free blocks + // + // Note this is limited by lookahead/gbmap + lfs3_block_t known_free; + + // Number of known in-use blocks + // + // Note this is limited by lookahead/gbmap + lfs3_block_t known_inuse; }; // Traversal info structure @@ -1221,6 +1231,7 @@ typedef struct lfs3_grm { typedef struct lfs3_gbmap { lfs3_block_t window; lfs3_block_t known; + lfs3_sblock_t known_free; lfs3_btree_t b; lfs3_btree_t b_p; } lfs3_gbmap_t; @@ -1276,7 +1287,7 @@ typedef struct lfs3 { struct lfs3_lookahead { lfs3_block_t window; lfs3_block_t off; - lfs3_block_t known; + lfs3_size_t known; lfs3_block_t ckpoint; uint8_t *buffer; } lookahead;