gc: Moved incremental gc behind ifdef LFS_GC
Incremental gc, being stateful and not gc-able (ironic), was always
going to need to be conditionally compilable.
This moves incremental gc behind the LFS_GC define, so that we can focus
on the "default" costs. This cuts lfs_t in nearly half!
lfs_t with LFS_GC: 308
lfs_t without LFS_C: 168 (-45.5%)
This does save less code than one might expect though. We still need
most of the internal traversal/gc logic for things like block allocation
and orphan cleanup, so most of the savings is limited to the RAM storing
the incremental state:
code stack ctx
before: 37916 2608 768
after with LFS_CFG: 37944 (+0.1%) 2608 (+0.0%) 768 (+0.0%)
after without LFS_CFG: 37796 (-0.3%) 2608 (+0.0%) 620 (-19.3%)
On the flip side, this does mean most of the incremental gc
functionality is still availables in the lfsr_traversal_t APIs.
Applications with more advanced gc use-cases may actually benefit from
_not_ enabling the incremental gc APIs, and instead use the
lfsr_traversal_t APIs directly.
This commit is contained in:
@@ -13155,6 +13155,7 @@ static int lfs_init(lfs_t *lfs, uint32_t flags,
|
||||
// // wear-leveling.
|
||||
// LFS_ASSERT(lfs->cfg->block_cycles != 0);
|
||||
|
||||
#ifdef LFS_GC
|
||||
// unknown gc flags?
|
||||
LFS_ASSERT((lfs->cfg->gc_flags & ~(
|
||||
LFS_GC_MTREEONLY
|
||||
@@ -13163,6 +13164,7 @@ static int lfs_init(lfs_t *lfs, uint32_t flags,
|
||||
| LFS_GC_COMPACT
|
||||
| LFS_GC_CKMETA
|
||||
| LFS_GC_CKDATA)) == 0);
|
||||
#endif
|
||||
|
||||
// check that gc_compact_thresh makes sense
|
||||
//
|
||||
@@ -13361,6 +13363,7 @@ static int lfs_init(lfs_t *lfs, uint32_t flags,
|
||||
lfs_memset(lfs->grm_p, 0, LFSR_GRM_DSIZE);
|
||||
lfs_memset(lfs->grm_d, 0, LFSR_GRM_DSIZE);
|
||||
|
||||
#ifdef LFS_GC
|
||||
// setup gc state, this can be mutated which is why we need a copy
|
||||
if (lfs->cfg->gc_flags) {
|
||||
lfs->gc.flags = lfs->cfg->gc_flags;
|
||||
@@ -13374,6 +13377,7 @@ static int lfs_init(lfs_t *lfs, uint32_t flags,
|
||||
} else {
|
||||
lfs->gc.steps = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -13921,7 +13925,8 @@ static int lfsr_mountinited(lfs_t *lfs) {
|
||||
}
|
||||
|
||||
// needed in lfsr_mount
|
||||
static int lfsr_gc_(lfs_t *lfs, uint32_t flags, lfs_soff_t steps);
|
||||
static int lfsr_fs_gc(lfs_t *lfs, lfsr_traversal_t *t,
|
||||
uint32_t flags, lfs_soff_t steps);
|
||||
|
||||
int lfsr_mount(lfs_t *lfs, uint32_t flags,
|
||||
const struct lfs_config *cfg) {
|
||||
@@ -13977,7 +13982,8 @@ int lfsr_mount(lfs_t *lfs, uint32_t flags,
|
||||
| LFS_M_COMPACT
|
||||
| LFS_M_CKMETA
|
||||
| LFS_M_CKDATA)) {
|
||||
err = lfsr_gc_(lfs,
|
||||
lfsr_traversal_t t;
|
||||
err = lfsr_fs_gc(lfs, &t,
|
||||
flags & (
|
||||
LFS_M_MTREEONLY
|
||||
| LFS_M_MKCONSISTENT
|
||||
@@ -14018,8 +14024,10 @@ int lfsr_unmount(lfs_t *lfs) {
|
||||
// all files/dirs should be closed before lfsr_unmount
|
||||
LFS_ASSERT(lfs->omdirs == NULL
|
||||
// special case for our gc traversal handle
|
||||
|| (lfs->omdirs == &lfs->gc.t.o.o
|
||||
&& lfs->gc.t.o.o.next == NULL));
|
||||
|| LFS_IFDEF_GC(
|
||||
(lfs->omdirs == &lfs->gc.t.o.o
|
||||
&& lfs->gc.t.o.o.next == NULL),
|
||||
false));
|
||||
|
||||
return lfs_deinit(lfs);
|
||||
}
|
||||
@@ -14153,7 +14161,8 @@ int lfsr_format(lfs_t *lfs, uint32_t flags,
|
||||
| LFS_F_COMPACT
|
||||
| LFS_F_CKMETA
|
||||
| LFS_F_CKDATA)) {
|
||||
err = lfsr_gc_(lfs,
|
||||
lfsr_traversal_t t;
|
||||
err = lfsr_fs_gc(lfs, &t,
|
||||
flags & (
|
||||
LFS_F_MTREEONLY
|
||||
| LFS_F_COMPACT
|
||||
@@ -14405,6 +14414,118 @@ int lfsr_fs_ckdata(lfs_t *lfs) {
|
||||
return lfsr_fs_ck(lfs, LFS_T_CKMETA | LFS_T_CKDATA);
|
||||
}
|
||||
|
||||
// low-level filesystem gc
|
||||
//
|
||||
// runs the traversal until all work is completed, which may take
|
||||
// multiple passes
|
||||
static int lfsr_fs_gc(lfs_t *lfs, lfsr_traversal_t *t,
|
||||
uint32_t flags, lfs_soff_t steps) {
|
||||
// unknown gc flags?
|
||||
//
|
||||
// we should have check these earlier, but it doesn't hurt to
|
||||
// double check
|
||||
LFS_ASSERT((flags & ~(
|
||||
LFS_GC_MTREEONLY
|
||||
| LFS_GC_MKCONSISTENT
|
||||
| LFS_GC_LOOKAHEAD
|
||||
| LFS_GC_COMPACT
|
||||
| LFS_GC_CKMETA
|
||||
| LFS_GC_CKDATA)) == 0);
|
||||
// these flags require a writable filesystem
|
||||
LFS_ASSERT(!lfsr_m_isrdonly(lfs->flags) || !lfsr_t_ismkconsistent(flags));
|
||||
LFS_ASSERT(!lfsr_m_isrdonly(lfs->flags) || !lfsr_t_islookahead(flags));
|
||||
LFS_ASSERT(!lfsr_m_isrdonly(lfs->flags) || !lfsr_t_iscompact(flags));
|
||||
// some flags don't make sense when only traversing the mtree
|
||||
LFS_ASSERT(!lfsr_t_ismtreeonly(flags) || !lfsr_t_islookahead(flags));
|
||||
LFS_ASSERT(!lfsr_t_ismtreeonly(flags) || !lfsr_t_isckdata(flags));
|
||||
|
||||
// fix pending grms if requested
|
||||
if (lfsr_t_ismkconsistent(flags)
|
||||
&& lfsr_grm_count(lfs) > 0) {
|
||||
int err = lfsr_fs_fixgrm(lfs);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
// do we have any pending work?
|
||||
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);
|
||||
|
||||
while (pending && (lfs_off_t)steps > 0) {
|
||||
// checkpoint the allocator to maximize any lookahead scans
|
||||
lfs_alloc_ckpoint(lfs);
|
||||
|
||||
// start a new traversal?
|
||||
if (!lfsr_omdir_isopen(lfs, &t->o.o)) {
|
||||
lfsr_traversal_init(t, pending);
|
||||
lfsr_omdir_open(lfs, &t->o.o);
|
||||
}
|
||||
|
||||
// don't bother with lookahead if we've mutated
|
||||
if (lfsr_t_isdirty(t->o.o.flags)
|
||||
|| lfsr_t_ismutated(t->o.o.flags)) {
|
||||
t->o.o.flags &= ~LFS_GC_LOOKAHEAD;
|
||||
}
|
||||
|
||||
// will this traversal still make progress? no? start over
|
||||
if (!(t->o.o.flags & (
|
||||
LFS_GC_MKCONSISTENT
|
||||
| LFS_GC_LOOKAHEAD
|
||||
| LFS_GC_COMPACT
|
||||
| LFS_GC_CKMETA
|
||||
| LFS_GC_CKDATA))) {
|
||||
lfsr_omdir_close(lfs, &t->o.o);
|
||||
continue;
|
||||
}
|
||||
|
||||
// do we really need a full traversal?
|
||||
if (!(t->o.o.flags & (
|
||||
LFS_GC_LOOKAHEAD
|
||||
| LFS_GC_CKMETA
|
||||
| LFS_GC_CKDATA))) {
|
||||
t->o.o.flags |= LFS_T_MTREEONLY;
|
||||
}
|
||||
|
||||
// progress gc
|
||||
int err = lfsr_mtree_gc(lfs, t,
|
||||
NULL, NULL);
|
||||
if (err && err != LFS_ERR_NOENT) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// end of traversal?
|
||||
if (err == LFS_ERR_NOENT) {
|
||||
lfsr_omdir_close(lfs, &t->o.o);
|
||||
|
||||
// clear any pending flags we make progress on
|
||||
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
|
||||
: 0));
|
||||
}
|
||||
|
||||
// decrement steps
|
||||
if (steps > 0) {
|
||||
steps -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// attempt to grow the filesystem
|
||||
int lfsr_fs_grow(lfs_t *lfs, lfs_size_t block_count_) {
|
||||
@@ -14638,6 +14759,7 @@ int lfsr_traversal_rewind(lfs_t *lfs, lfsr_traversal_t *t) {
|
||||
|
||||
/// Incremental gc operations ///
|
||||
|
||||
#ifdef LFS_GC
|
||||
int lfsr_gc_setflags(lfs_t *lfs, uint32_t flags) {
|
||||
// unknown gc flags?
|
||||
LFS_ASSERT((flags & ~(
|
||||
@@ -14656,123 +14778,22 @@ int lfsr_gc_setflags(lfs_t *lfs, uint32_t flags) {
|
||||
lfs->gc.flags = flags;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LFS_GC
|
||||
int lfsr_gc_setsteps(lfs_t *lfs, lfs_soff_t steps) {
|
||||
lfs->gc.steps = steps;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LFS_GC
|
||||
// perform any pending janitorial work
|
||||
static int lfsr_gc_(lfs_t *lfs, uint32_t flags, lfs_soff_t steps) {
|
||||
// unknown gc flags?
|
||||
//
|
||||
// we should have check these earlier, but it doesn't hurt to
|
||||
// double check
|
||||
LFS_ASSERT((flags & ~(
|
||||
LFS_GC_MTREEONLY
|
||||
| LFS_GC_MKCONSISTENT
|
||||
| LFS_GC_LOOKAHEAD
|
||||
| LFS_GC_COMPACT
|
||||
| LFS_GC_CKMETA
|
||||
| LFS_GC_CKDATA)) == 0);
|
||||
// these flags require a writable filesystem
|
||||
LFS_ASSERT(!lfsr_m_isrdonly(lfs->flags) || !lfsr_t_ismkconsistent(flags));
|
||||
LFS_ASSERT(!lfsr_m_isrdonly(lfs->flags) || !lfsr_t_islookahead(flags));
|
||||
LFS_ASSERT(!lfsr_m_isrdonly(lfs->flags) || !lfsr_t_iscompact(flags));
|
||||
// some flags don't make sense when only traversing the mtree
|
||||
LFS_ASSERT(!lfsr_t_ismtreeonly(flags) || !lfsr_t_islookahead(flags));
|
||||
LFS_ASSERT(!lfsr_t_ismtreeonly(flags) || !lfsr_t_isckdata(flags));
|
||||
|
||||
// fix pending grms if requested
|
||||
if (lfsr_t_ismkconsistent(flags)
|
||||
&& lfsr_grm_count(lfs) > 0) {
|
||||
int err = lfsr_fs_fixgrm(lfs);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
// do we have any pending work?
|
||||
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);
|
||||
|
||||
while (pending && (lfs_off_t)steps > 0) {
|
||||
// checkpoint the allocator to maximize any lookahead scans
|
||||
lfs_alloc_ckpoint(lfs);
|
||||
|
||||
// start a new traversal?
|
||||
if (!lfsr_omdir_isopen(lfs, &lfs->gc.t.o.o)) {
|
||||
lfsr_traversal_init(&lfs->gc.t, pending);
|
||||
lfsr_omdir_open(lfs, &lfs->gc.t.o.o);
|
||||
}
|
||||
|
||||
// don't bother with lookahead if we've mutated
|
||||
if (lfsr_t_isdirty(lfs->gc.t.o.o.flags)
|
||||
|| lfsr_t_ismutated(lfs->gc.t.o.o.flags)) {
|
||||
lfs->gc.t.o.o.flags &= ~LFS_GC_LOOKAHEAD;
|
||||
}
|
||||
|
||||
// will this traversal still make progress? no? start over
|
||||
if (!(lfs->gc.t.o.o.flags & (
|
||||
LFS_GC_MKCONSISTENT
|
||||
| LFS_GC_LOOKAHEAD
|
||||
| LFS_GC_COMPACT
|
||||
| LFS_GC_CKMETA
|
||||
| LFS_GC_CKDATA))) {
|
||||
lfsr_omdir_close(lfs, &lfs->gc.t.o.o);
|
||||
continue;
|
||||
}
|
||||
|
||||
// do we really need a full traversal?
|
||||
if (!(lfs->gc.t.o.o.flags & (
|
||||
LFS_GC_LOOKAHEAD
|
||||
| LFS_GC_CKMETA
|
||||
| LFS_GC_CKDATA))) {
|
||||
lfs->gc.t.o.o.flags |= LFS_T_MTREEONLY;
|
||||
}
|
||||
|
||||
// progress gc
|
||||
int err = lfsr_mtree_gc(lfs, &lfs->gc.t,
|
||||
NULL, NULL);
|
||||
if (err && err != LFS_ERR_NOENT) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// end of traversal?
|
||||
if (err == LFS_ERR_NOENT) {
|
||||
lfsr_omdir_close(lfs, &lfs->gc.t.o.o);
|
||||
|
||||
// clear any pending flags we make progress on
|
||||
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(lfs->gc.t.o.o.flags)
|
||||
|| lfsr_t_ismutated(lfs->gc.t.o.o.flags))
|
||||
? LFS_GC_CKMETA | LFS_GC_CKDATA
|
||||
: 0));
|
||||
}
|
||||
|
||||
// decrement steps
|
||||
if (steps > 0) {
|
||||
steps -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lfsr_gc(lfs_t *lfs) {
|
||||
return lfsr_gc_(lfs, lfs->gc.flags, lfs->gc.steps);
|
||||
return lfsr_fs_gc(lfs, &lfs->gc.t,
|
||||
lfs->gc.flags, lfs->gc.steps);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user