(Re)implemented lfsr_fs_gc
This just provides a simple, easy-to-call, wrapper over the new
traversal API:
int lfsr_fs_gc(lfs_t *lfs, uint32_t flags);
The main difference from its previous incarnation, is that lfsr_fs_gc
now takes a flags argument to indicate exactly what gc operations to
perform. This gives the user more control, and may also make the API
more robust towards adding new features:
LFS_GC_MTREEONLY = 0x0010, // Only traverse the mtree
LFS_GC_MKCONSISTENT = 0x0020, // Make the filesystem consistent
LFS_GC_LOOKAHEAD = 0x0040, // Populate lookahead buffer
LFS_GC_COMPACT = 0x0080, // Compact metadata logs
LFS_GC_CKMETA = 0x0100, // Check metadata checksums
LFS_GC_CKDATA = 0x0200, // Check metadata + data checksums
LFS_GC_REPAIRMETA+ = 0x0400, // Repair metadata blocks
LFS_GC_REPAIRDATA+ = 0x0800, // Repair metadata + data blocks
+ Planned
Alternatively, gc_flags could have been added as a config option. But
making gc_flags a function argument matches other flag APIs (open
mainly), and is slightly more flexible in that it allows a system to do
different gc operations in different system states (though this could
also be accomplished with the hypothetical lfsr_fs_gccfg, which would
probably be good to add anyways).
Worst case, defining a system-wide define that you always pass to
lfsr_fs_gc accomplishes roughly the same thing.
---
This adds a bit more code, mainly to check if we actually need to
traverse, and to make sure traversals accomplish all of the requested
work.
code stack
before: 35448 2680
after: 35708 (+0.7%) 2672 (-0.3%)
Curiously it also saved a bit of stack, which is a bit silly given this
commit is purely code addition. Apparently something in lfs_alloc and
lfsr_fs_gc is shared, getting uninlined, and messing with the stack
measurement. lfs_alloc is quite sensitive to stack changes after all.
This commit is contained in:
@@ -8229,6 +8229,7 @@ enum {
|
|||||||
|
|
||||||
#define LFSR_TRAVERSAL(_flags) \
|
#define LFSR_TRAVERSAL(_flags) \
|
||||||
((lfsr_traversal_t){ \
|
((lfsr_traversal_t){ \
|
||||||
|
.o.o.type=LFS_TYPE_TRAVERSAL, \
|
||||||
.o.o.state=LFSR_TSTATE_MROOTANCHOR, \
|
.o.o.state=LFSR_TSTATE_MROOTANCHOR, \
|
||||||
.o.o.flags=_flags, \
|
.o.o.flags=_flags, \
|
||||||
.o.o.mdir.mid=-1, \
|
.o.o.mdir.mid=-1, \
|
||||||
@@ -12815,6 +12816,96 @@ int lfsr_fs_mkconsistent(lfs_t *lfs) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int lfsr_fs_gc(lfs_t *lfs, uint32_t 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));
|
||||||
|
// these flags are internal and shouldn't be provided by the user
|
||||||
|
LFS_ASSERT(!lfsr_f_isdirty(flags));
|
||||||
|
LFS_ASSERT(!lfsr_f_ismutated(flags));
|
||||||
|
|
||||||
|
// fix pending grms if requested
|
||||||
|
if (lfsr_t_ismkconsistent(flags)
|
||||||
|
&& lfsr_grm_count(lfs) > 0) {
|
||||||
|
if (lfsr_grm_count(lfs) == 2) {
|
||||||
|
LFS_DEBUG("Fixing grm %"PRId32".%"PRId32" %"PRId32".%"PRId32,
|
||||||
|
lfsr_mid_bid(lfs, lfs->grm.mids[0]) >> lfs->mdir_bits,
|
||||||
|
lfsr_mid_rid(lfs, lfs->grm.mids[0]),
|
||||||
|
lfsr_mid_bid(lfs, lfs->grm.mids[1]) >> lfs->mdir_bits,
|
||||||
|
lfsr_mid_rid(lfs, lfs->grm.mids[1]));
|
||||||
|
} else if (lfsr_grm_count(lfs) == 1) {
|
||||||
|
LFS_DEBUG("Fixing grm %"PRId32".%"PRId32,
|
||||||
|
lfsr_mid_bid(lfs, lfs->grm.mids[0]) >> lfs->mdir_bits,
|
||||||
|
lfsr_mid_rid(lfs, lfs->grm.mids[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
int err = lfsr_fs_fixgrm(lfs);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we need multiple passes because of potential mutation issues
|
||||||
|
for (int i = 0;; i++) {
|
||||||
|
// do we need to do anything?
|
||||||
|
if (!((lfsr_t_ismkconsistent(flags) && lfs->hasorphans)
|
||||||
|
|| (lfsr_t_islookahead(flags)
|
||||||
|
&& (lfs->lookahead.next > 0 || lfs->lookahead.size == 0))
|
||||||
|
|| (lfsr_t_iscompact(flags) && i == 0)
|
||||||
|
|| (lfsr_t_isckmeta(flags) && i == 0)
|
||||||
|
|| (lfsr_t_isckdata(flags) && i == 0))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lfsr_t_islookahead(flags)) {
|
||||||
|
lfs_alloc_shift(lfs);
|
||||||
|
}
|
||||||
|
|
||||||
|
lfsr_traversal_t t = LFSR_TRAVERSAL(flags);
|
||||||
|
// note we need to be tracked for bshrub commits to work
|
||||||
|
lfsr_omdir_open(lfs, &t.o.o);
|
||||||
|
while (true) {
|
||||||
|
// let lfsr_mtree_gc do most of the work
|
||||||
|
int err = lfsr_mtree_gc(lfs, &t,
|
||||||
|
NULL, NULL);
|
||||||
|
if (err) {
|
||||||
|
if (err == LFS_ERR_NOENT) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lfsr_omdir_close(lfs, &t.o.o);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lfsr_omdir_close(lfs, &t.o.o);
|
||||||
|
|
||||||
|
// no more orphans?
|
||||||
|
if (lfsr_t_ismkconsistent(t.o.o.flags)) {
|
||||||
|
LFS_ASSERT(!lfsr_f_isdirty(t.o.o.flags));
|
||||||
|
lfs->hasorphans = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// was lookahead scan successful?
|
||||||
|
if (lfsr_t_islookahead(t.o.o.flags)
|
||||||
|
&& !lfsr_f_ismutated(t.o.o.flags)) {
|
||||||
|
LFS_ASSERT(!lfsr_f_isdirty(t.o.o.flags));
|
||||||
|
lfs_alloc_markfree(lfs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update flags, clear mutated/dirty
|
||||||
|
flags = t.o.o.flags & ~LFS_F_DIRTY & ~LFS_F_MUTATED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lfsr_t_ismkconsistent(flags)) {
|
||||||
|
LFS_ASSERT(lfsr_grm_count(lfs) == 0);
|
||||||
|
LFS_ASSERT(lfs->hasorphans == false);
|
||||||
|
}
|
||||||
|
if (lfsr_t_islookahead(flags)) {
|
||||||
|
LFS_ASSERT(lfs->lookahead.next == 0);
|
||||||
|
LFS_ASSERT(lfs->lookahead.size > 0);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int lfsr_fs_grow(lfs_t *lfs, lfs_size_t block_count_) {
|
int lfsr_fs_grow(lfs_t *lfs, lfs_size_t block_count_) {
|
||||||
// shrinking the filesystem is not supported
|
// shrinking the filesystem is not supported
|
||||||
@@ -12884,6 +12975,7 @@ int lfsr_traversal_open(lfs_t *lfs, lfsr_traversal_t *t, uint32_t flags) {
|
|||||||
LFS_ASSERT(!lfsr_t_ismtreeonly(flags) || !lfsr_t_isckdata(flags));
|
LFS_ASSERT(!lfsr_t_ismtreeonly(flags) || !lfsr_t_isckdata(flags));
|
||||||
// these flags are internal and shouldn't be provided by the user
|
// these flags are internal and shouldn't be provided by the user
|
||||||
LFS_ASSERT(!lfsr_f_isdirty(flags));
|
LFS_ASSERT(!lfsr_f_isdirty(flags));
|
||||||
|
LFS_ASSERT(!lfsr_f_ismutated(flags));
|
||||||
|
|
||||||
// setup traversal state
|
// setup traversal state
|
||||||
t->o.o.type = LFS_TYPE_TRAVERSAL;
|
t->o.o.type = LFS_TYPE_TRAVERSAL;
|
||||||
|
|||||||
@@ -183,6 +183,19 @@ enum lfs_traversal_flags {
|
|||||||
LFS_F_MUTATED = 0x4000, // Filesystem modified by traversal
|
LFS_F_MUTATED = 0x4000, // Filesystem modified by traversal
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// GC flags
|
||||||
|
enum lfs_gc_flags {
|
||||||
|
LFS_GC_MTREEONLY = 0x0010, // Only traverse the mtree
|
||||||
|
LFS_GC_MKCONSISTENT = 0x0020, // Make the filesystem consistent
|
||||||
|
LFS_GC_LOOKAHEAD = 0x0040, // Populate lookahead buffer
|
||||||
|
LFS_GC_COMPACT = 0x0080, // Compact metadata logs
|
||||||
|
LFS_GC_CKMETA = 0x0100, // Check metadata checksums
|
||||||
|
LFS_GC_CKDATA = 0x0200, // Check metadata + data checksums
|
||||||
|
// TODO
|
||||||
|
// LFS_GC_REPAIRMETA = 0x0400, // Repair metadata blocks
|
||||||
|
// LFS_GC_REPAIRDATA = 0x0800, // Repair metadata + data blocks
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// Configuration provided during initialization of the littlefs
|
// Configuration provided during initialization of the littlefs
|
||||||
struct lfs_config {
|
struct lfs_config {
|
||||||
@@ -1114,6 +1127,20 @@ lfs_ssize_t lfsr_fs_size(lfs_t *lfs);
|
|||||||
int lfsr_fs_mkconsistent(lfs_t *lfs);
|
int lfsr_fs_mkconsistent(lfs_t *lfs);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef LFS_READONLY
|
||||||
|
// Attempt any janitorial work that may be pending.
|
||||||
|
//
|
||||||
|
// The exact janitorial work depends on the provided flags. Note that most
|
||||||
|
// of this work can also be accomplished incrementally via
|
||||||
|
// lfsr_traversal_read.
|
||||||
|
//
|
||||||
|
// Calling this function is not required, but may allow the offloading of
|
||||||
|
// expensive janitorial work to a less time-critical code path.
|
||||||
|
//
|
||||||
|
// Returns a negative error code on failure.
|
||||||
|
int lfsr_fs_gc(lfs_t *lfs, uint32_t flags);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef LFS_READONLY
|
#ifndef LFS_READONLY
|
||||||
// Change the number of blocks used by the filesystem
|
// Change the number of blocks used by the filesystem
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ after = [
|
|||||||
'test_fwrite',
|
'test_fwrite',
|
||||||
'test_forphans',
|
'test_forphans',
|
||||||
'test_traversal',
|
'test_traversal',
|
||||||
|
'test_gc',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+1515
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,7 @@ after = [
|
|||||||
'test_files',
|
'test_files',
|
||||||
'test_forphans',
|
'test_forphans',
|
||||||
'test_traversal',
|
'test_traversal',
|
||||||
|
'test_gc',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user