gc: Made CKMETA/CKDATA progressable, added lfsr_gc_unck

LFS_GC_CKMETA and LFS_GC_CKDATA are a bit unique in that their work is
never really done.

Where LFS_GC_MKCONSISTENT/COMPACT can prove things about the system,
LFS_GC_CKMETA/CKDATA can't, because it's always possible for new
bit-errors to develop. Even _during_ an LFS_GC_CKMETA/CKDATA traversal.

But while this is technically true, it's not a very useful state of
things for our lfsr_gc API...

---

What we really want is some way to know if ckmeta/ckdata has completed
"recently" (for some definition of recently), and to let users indicate
when they need another ckmeta/ckdata scan.

To try to solve this:

1. Added LFS_I_CANCKMETA and LFS_I_CANCKDATA to indicate when lfsr_gc
   has not checked metadata/data.

   These are set during mount (unless mounting with
   LFS_M_CKMETA/CKDATA), and cleared when either lfsr_gc completes or
   lfsr_fs_ckmeta/data is called. Once cleared, littlefs will not reset
   them on its own.

2. Added lfsr_gc_unck to allow users to explicitly reset LFS_I_CKMETA
   and/or LFS_I_CKDATA, which will tell lfsr_gc to check metadata/data
   again on the next call.

   There is some subtlety around clobbering ongoing traversals, but a
   mask and some tests should prevent this from being a problem.

   Currently, lfsr_gc_unck also allows clearing of other gc flags, but
   I'm not sure there's any real use-case for this...

Note that you can still get the previous behavior if you just call
lfsr_gc_unck after every lfsr_gc call.

This also changes info flag behavior slightly in default mode, with
LFS_I_CANCKMETA/CANCKDATA telling you if metadata/data has been checked
since mount. Which does seem useful? Maybe these flags deserve a better
name?

Code changes:

                   code          stack          ctx
  default before: 37796 (+0.0%)   2608 (+0.0%)  620 (+0.0%)
  default after:  37792 (+0.0%)   2608 (+0.0%)  620 (+0.0%)

  gc before:      37896           2608          768
  gc after:       37938 (+0.1%)   2608 (+0.0%)  768 (+0.0.%)
This commit is contained in:
Christopher Haster
2025-01-07 13:44:04 -06:00
parent 0617244aa3
commit 39d488a1ef
5 changed files with 764 additions and 125 deletions
+66 -13
View File
@@ -6728,6 +6728,14 @@ static inline bool lfsr_i_isuncompacted(uint32_t flags) {
return flags & LFS_I_UNCOMPACTED;
}
static inline bool lfsr_i_canckmeta(uint32_t flags) {
return flags & LFS_I_CANCKMETA;
}
static inline bool lfsr_i_canckdata(uint32_t flags) {
return flags & LFS_I_CANCKDATA;
}
// on-demand flags
// needed in lfsr_fs_isinconsistent
@@ -9621,6 +9629,7 @@ failed:;
eot:;
// was lookahead scan successful?
if (lfsr_t_islookahead(t->o.o.flags)
&& !lfsr_t_ismtreeonly(t->o.o.flags)
&& !lfsr_t_isdirty(t->o.o.flags)
&& !lfsr_t_ismutated(t->o.o.flags)) {
lfs_alloc_markfree(lfs);
@@ -9640,6 +9649,21 @@ eot:;
lfs->flags &= ~LFS_I_UNCOMPACTED;
}
// was ckmeta/ckdata successful? we only consider our filesystem
// checked if we weren't mutated
if (lfsr_t_isckmeta(t->o.o.flags)
&& !lfsr_t_ismtreeonly(t->o.o.flags)
&& !lfsr_t_isdirty(t->o.o.flags)
&& !lfsr_t_ismutated(t->o.o.flags)) {
lfs->flags &= ~LFS_I_CANCKMETA;
}
if (lfsr_t_isckdata(t->o.o.flags)
&& !lfsr_t_ismtreeonly(t->o.o.flags)
&& !lfsr_t_isdirty(t->o.o.flags)
&& !lfsr_t_ismutated(t->o.o.flags)) {
lfs->flags &= ~LFS_I_CANCKDATA;
}
return LFS_ERR_NOENT;
}
@@ -13188,7 +13212,10 @@ static int lfs_init(lfs_t *lfs, uint32_t flags,
| LFS_I_UNTIDY
// default to assuming we need compaction somewhere, worst case
// this just makes lfsr_gc read more than is strictly needed
| LFS_I_UNCOMPACTED;
| LFS_I_UNCOMPACTED
// default to needing a ckmeta/ckdata scan
| LFS_I_CANCKMETA
| LFS_I_CANCKDATA;
// copy block_count so we can mutate it
lfs->block_count = lfs->cfg->block_count;
@@ -14196,7 +14223,9 @@ int lfsr_fs_stat(lfs_t *lfs, struct lfs_fsinfo *fsinfo) {
| LFS_IFDEF_CKFETCHES(LFS_I_CKFETCHES, 0)
| LFS_IFDEF_CKPARITY(LFS_I_CKPARITY, 0)
| LFS_IFDEF_CKDATACKSUMS(LFS_I_CKDATACKSUMS, 0)
| LFS_I_UNCOMPACTED);
| LFS_I_UNCOMPACTED
| LFS_I_CANCKMETA
| LFS_I_CANCKDATA);
// some flags we calculate on demand
fsinfo->flags |= (lfsr_fs_isinconsistent(lfs)) ? LFS_I_INCONSISTENT : 0;
fsinfo->flags |= (lfsr_fs_canlookahead(lfs)) ? LFS_I_CANLOOKAHEAD : 0;
@@ -14403,6 +14432,8 @@ static int lfsr_fs_ck(lfs_t *lfs, uint32_t flags) {
}
}
// clear relevant ck flags
lfs->flags &= ~flags;
return 0;
}
@@ -14452,10 +14483,10 @@ static int lfsr_fs_gc(lfs_t *lfs, lfsr_traversal_t *t,
uint32_t pending = flags & (
(lfs->flags & (
LFS_I_UNTIDY
| LFS_I_UNCOMPACTED))
| ((lfsr_fs_canlookahead(lfs)) ? LFS_GC_LOOKAHEAD : 0)
| LFS_GC_CKMETA
| LFS_GC_CKDATA);
| LFS_I_UNCOMPACTED
| LFS_I_CANCKMETA
| LFS_I_CANCKDATA))
| ((lfsr_fs_canlookahead(lfs)) ? LFS_GC_LOOKAHEAD : 0));
while (pending && (lfs_off_t)steps > 0) {
// checkpoint the allocator to maximize any lookahead scans
@@ -14507,13 +14538,11 @@ static int lfsr_fs_gc(lfs_t *lfs, lfsr_traversal_t *t,
pending &= (
(lfs->flags & (
LFS_I_UNTIDY
| LFS_I_UNCOMPACTED))
| ((lfsr_fs_canlookahead(lfs)) ? LFS_GC_LOOKAHEAD : 0)
// only consider our filesystem checked if we
// weren't mutated
| ((lfsr_t_isdirty(t->o.o.flags)
|| lfsr_t_ismutated(t->o.o.flags))
? LFS_GC_CKMETA | LFS_GC_CKDATA
| LFS_I_UNCOMPACTED
| LFS_I_CANCKMETA
| LFS_I_CANCKDATA))
| ((lfsr_fs_canlookahead(lfs))
? LFS_GC_LOOKAHEAD
: 0));
}
@@ -14767,6 +14796,30 @@ int lfsr_gc(lfs_t *lfs) {
}
#endif
#ifdef LFS_GC
// unperform janitorial work
int lfsr_gc_unck(lfs_t *lfs, uint32_t flags) {
// unknown flags?
LFS_ASSERT((flags & ~(
LFS_I_INCONSISTENT
| LFS_I_CANLOOKAHEAD
| LFS_I_UNCOMPACTED
| LFS_I_CANCKMETA
| LFS_I_CANCKDATA)) == 0);
// reset the requested flags
lfs->flags |= flags;
// and clear from any ongoing traversals
//
// lfsr_fs_gc will terminate early if it discovers it can no longer
// make progress
lfs->gc.t.o.o.flags &= ~flags;
return 0;
}
#endif