Reworked lfsr_fs_gc to be incremental
Thinking about use case a bit, most lfsr_fs_gc will be to perform
background work, and can benefit from being incremental.
We already support incremental gc and all the mess associated with
traversal invalidation via the traversal API, so we might as well expose
this through lfsr_fs_gc.
The main downside is that we need to store an lfsr_traversal_t object
somewhere, which is not exactly a cheap struct. I was originally
considering limiting incremental gc to the traversal API for this
reason, but I think the value add of an incremental lfsr_fs_gc is too
compelling... Though we really should add a compile-time option
(LFS_NO_GC? LFS_NO_INCRGC?) to allow users to opt-out of this RAM cost
if they're never going to call this function.
Oh, and lfs_t also becomes self-referential, which might become a
problem for higher-level language users...
---
The incremental behavior of lfsr_fs_gc can be controlled by the new
gc_steps config option. This allows more than one step to be performed
at a time, which may allow for more progress when intermixed with
write-heavy filesystem operations. Setting gc_steps=-1 performs a full
traversal every call, which guarantees always making some amount of
progress.
This adds a bit of code, since we now need to check for/resume existing
traversals. But the real cost is the added RAM to lfs_t, which is
unfortunately wasted if you never call lfsr_fs_gc:
code stack lfs_t
before: 35708 2672 164
after: 35756 (+0.1%) 2672 (+0.0%) 296 (+80.5%)
This commit is contained in:
@@ -281,6 +281,17 @@ struct lfs_config {
|
||||
// can track 8 blocks.
|
||||
lfs_size_t lookahead_size;
|
||||
|
||||
// How many gc steps to perform on each lfsr_fs_gc call.
|
||||
//
|
||||
// Each gc step progresses janitorial work by ~1 block (this is equivalent
|
||||
// to lfsr_traversal_read). More steps per call may make more progress if
|
||||
// interleaving with other work.
|
||||
//
|
||||
// 0 defaults to 1 step, and -1 will perform a full traversal every call,
|
||||
// though multiple traversals may still be needed to complete all
|
||||
// janitorial work.
|
||||
int32_t gc_steps;
|
||||
|
||||
// Threshold for metadata compaction during gc in bytes. Metadata logs
|
||||
// that exceed this threshold will be compacted during gc operations.
|
||||
// Defaults to ~88% block_size when zero, though this default may change
|
||||
@@ -741,6 +752,9 @@ typedef struct lfs {
|
||||
lfsr_grm_t grm;
|
||||
uint8_t grm_p[LFSR_GRM_DSIZE];
|
||||
uint8_t grm_d[LFSR_GRM_DSIZE];
|
||||
|
||||
// TODO allow compile time opt-out to reclaim RAM
|
||||
lfsr_traversal_t gc;
|
||||
} lfs_t;
|
||||
|
||||
|
||||
@@ -1130,9 +1144,8 @@ int lfsr_fs_mkconsistent(lfs_t *lfs);
|
||||
#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.
|
||||
// The exact janitorial work depends on the provided flags. Note multiple
|
||||
// calls may be required to complete all janitorial work.
|
||||
//
|
||||
// Calling this function is not required, but may allow the offloading of
|
||||
// expensive janitorial work to a less time-critical code path.
|
||||
|
||||
Reference in New Issue
Block a user