trv: Added LFS3_t_NOSPC, avoid ENOSPC errors in traversals

This relaxes error encountered during lfs3_mtree_gc to _not_ propagate,
but instead just log a warning and prevent the relevant work from being
checked off during EOT.

The idea is this allows other work to make progress in low-space
conditions.

I originally meant to limit this to gbmap repopulations, to match the
behavior of lfs3_alloc_repopgbmap, but I think extending the idea to all
filesystem mutating operations makes sense (LFS3_T_MKCONSISTENT +
LFS3_T_REPOPGBMAP + LFS3_T_COMPACTMETA).

---

To avoid incorrectly marking traversal work as completed, we need to
track if we hit any ENOSPC errors, thus the new LFS3_t_NOSPC flag:

  LFS3_t_NOSPC  0x00800000  Optional gc work ran out of space

Not the happiest just throwing flags at problems, but I can't think of a
better solution at the moment.

This doesn't differentiate between ENOSPC errors during the different
types of work, but in theory if we're hitting ENOSPC errors whatever
work returns the error is a toss-up anyways.

---

Adds a bit of code:

                 code          stack          ctx
  before:       37208           2352          688
  after:        37248 (+0.1%)   2352 (+0.0%)  688 (+0.0%)

                 code          stack          ctx
  gbmap before: 40120           2368          856
  gbmap after:  40204 (+0.2%)   2368 (+0.0%)  856 (+0.0%)
This commit is contained in:
Christopher Haster
2025-10-18 18:18:09 -05:00
parent 0a8ac0994c
commit f892d299dd
3 changed files with 57 additions and 10 deletions
+53 -8
View File
@@ -7449,7 +7449,7 @@ static inline void lfs3_t_settstate(uint32_t *flags, uint8_t tstate) {
}
static inline uint8_t lfs3_t_btype(uint32_t flags) {
return (flags >> 20) & 0x0f;
return (flags >> 20) & 0x7;
}
static inline uint32_t lfs3_t_btypeflags(uint8_t btype) {
@@ -7472,6 +7472,10 @@ static inline bool lfs3_t_isckpointed(uint32_t flags) {
return flags & LFS3_t_CKPOINTED;
}
static inline bool lfs3_t_isnospc(uint32_t flags) {
return flags & LFS3_t_NOSPC;
}
// mount flags
static inline bool lfs3_m_isrdonly(uint32_t flags) {
(void)flags;
@@ -10668,10 +10672,19 @@ static lfs3_stag_t lfs3_mtree_gc(lfs3_t *lfs3, lfs3_mgc_t *mgc,
// erased/bad info and (2) try to best use any available
// erased-state
int err = lfs3_alloc_zerogbmap(lfs3, &mgc->gbmap_);
if (err) {
if (err && err != LFS3_ERR_NOSPC) {
return err;
}
// not having enough space isn't really an error
if (err == LFS3_ERR_NOSPC) {
LFS3_WARN("Not enough space for gbmap "
"(lookahead %"PRId32"/%"PRId32")",
lfs3->lookahead.known,
lfs3->block_count);
mgc->t.b.h.flags |= LFS3_t_NOSPC;
}
// keep our own ckpointed flag clear
mgc->t.b.h.flags &= ~LFS3_t_CKPOINTED;
}
@@ -10708,10 +10721,19 @@ dropped:;
&& !lfs3_t_isckpointed(mgc->t.b.h.flags)) {
int err = lfs3_gbmap_markbptr(lfs3, &mgc->gbmap_, tag, bptr_,
LFS3_TAG_BMINUSE);
if (err) {
if (err && err != LFS3_ERR_NOSPC) {
return err;
}
// not having enough space isn't really an error
if (err == LFS3_ERR_NOSPC) {
LFS3_WARN("Not enough space for gbmap "
"(lookahead %"PRId32"/%"PRId32")",
lfs3->lookahead.known,
lfs3->block_count);
mgc->t.b.h.flags |= LFS3_t_NOSPC;
}
// keep our own ckpointed flag clear
mgc->t.b.h.flags &= ~LFS3_t_CKPOINTED;
}
@@ -10724,9 +10746,19 @@ dropped:;
lfs3_mdir_t *mdir = (lfs3_mdir_t*)bptr_->d.u.buffer;
uint32_t dirty = mgc->t.b.h.flags;
int err = lfs3_mdir_mkconsistent(lfs3, mdir);
if (err) {
if (err && err != LFS3_ERR_NOSPC) {
return err;
}
// not having enough space isn't really an error
if (err == LFS3_ERR_NOSPC) {
LFS3_WARN("Not enough space for mkconsistent "
"(lookahead %"PRId32"/%"PRId32")",
lfs3->lookahead.known,
lfs3->block_count);
mgc->t.b.h.flags |= LFS3_t_NOSPC;
}
// reset dirty flag
mgc->t.b.h.flags &= ~LFS3_t_DIRTY | dirty;
// make sure we clear any zombie flags
@@ -10763,9 +10795,19 @@ dropped:;
// compact the mdir
uint32_t dirty = mgc->t.b.h.flags;
int err = lfs3_mdir_compact(lfs3, mdir);
if (err) {
if (err && err != LFS3_ERR_NOSPC) {
return err;
}
// not having enough space isn't really an error
if (err == LFS3_ERR_NOSPC) {
LFS3_WARN("Not enough space for compactmeta "
"(lookahead %"PRId32"/%"PRId32")",
lfs3->lookahead.known,
lfs3->block_count);
mgc->t.b.h.flags |= LFS3_t_NOSPC;
}
// reset dirty flag
mgc->t.b.h.flags &= ~LFS3_t_DIRTY | dirty;
}
@@ -10790,21 +10832,24 @@ eot:;
&& lfs3_f_isgbmap(lfs3->flags)
&& lfs3_t_isrepopgbmap(lfs3->flags)
&& !lfs3_t_ismtreeonly(mgc->t.b.h.flags)
&& !lfs3_t_isckpointed(mgc->t.b.h.flags)) {
&& !lfs3_t_isckpointed(mgc->t.b.h.flags)
&& !lfs3_t_isnospc(mgc->t.b.h.flags)) {
lfs3_alloc_adoptgbmap(lfs3, &mgc->gbmap_, lfs3->lookahead.ckpoint);
}
#endif
// was mkconsistent successful?
if (lfs3_t_ismkconsistent(mgc->t.b.h.flags)
&& !lfs3_t_isdirty(mgc->t.b.h.flags)) {
&& !lfs3_t_isdirty(mgc->t.b.h.flags)
&& !lfs3_t_isnospc(mgc->t.b.h.flags)) {
lfs3->flags &= ~LFS3_I_MKCONSISTENT;
}
// was compaction successful? note we may need multiple passes if
// we want to be sure everything is compacted
if (lfs3_t_compactmeta(mgc->t.b.h.flags)
&& !lfs3_t_ismutated(mgc->t.b.h.flags)) {
&& !lfs3_t_ismutated(mgc->t.b.h.flags)
&& !lfs3_t_isnospc(mgc->t.b.h.flags)) {
lfs3->flags &= ~LFS3_I_COMPACTMETA;
}
#endif