t: Added lookahead to lfsr_traversal_t, adopted in lfs_alloc
This sort of turned into a complete refactor of lfs_alloc in order to
move/reuse the lookahead buffer filling logic into lfsr_fs_traverse.
lfs_alloc now calls lfsr_fs_traverse to fill the lookahead buffer when
no more blocks are available, but also you can too with lfsr_traversal_t
+ LFS_T_LOOKAHEAD.
The one big caveat being if any mutation happens to the filesystem, any
incomplete lookahead needs to be tossed out. To help with this,
lfsr_traversal_read now returns LFS_ERR_BUSY (-16) instead of
LFS_ERR_NOENT (-2) if the filesystem has been modified since the
traversal was opened.
Note that by default lfsr_traversal_t will still try to keep traversing
blocks, but can be told to terminate immediately with LFS_T_EXCL.
Continuing the traversal is probably desired for checking checksums,
debugging, etc, as otherwise you could end up looping over only the
first couple blocks in a write-heavy system, but if you are trying to
populate the lookahead buffer you probably want to just abort and start
over.
I considered adding a flags field to lfs_tinfo for this, but decided
against it since it would be the only place in the current API where we
don't use error codes to convey behavior-changing information. Though
this may be worth reconsidering at some point...
---
In reworking lfs_alloc, a lot of the internal logic was broken up into
specific functions:
- lfs_alloc_ckpoint - checkpoint the allocator
- lfs_alloc_discard - discard any lookahead
- lfs_alloc_shift - discard/shift lookahead if progress can be made
- lfs_alloc_markinuse - mark a block as in-use
- lfs_alloc_markfree - mark any remaining blocks as free
- lfs_alloc_findnext - find the next free block in lookahead
If anything this probably makes lfs_alloc more readable, though the
original motivation was to allow lfsr_traversal_t to only shift/zero the
lookahead buffer if there's a chance we can make progress.
This was based on upstream work by opilat and myself.
Code changes:
code stack
before: 34226 2560
after: 34474 (+0.7%) 2552 (-0.3%)
This commit is contained in:
@@ -2109,7 +2109,7 @@ static int lfsr_bptr_ck(lfs_t *lfs, const lfsr_bptr_t *bptr) {
|
|||||||
|
|
||||||
|
|
||||||
// predeclare block allocator functions
|
// predeclare block allocator functions
|
||||||
static int lfs_alloc(lfs_t *lfs, lfs_block_t *block, bool erase);
|
static lfs_sblock_t lfs_alloc(lfs_t *lfs, bool erase);
|
||||||
static void lfs_alloc_ckpoint(lfs_t *lfs);
|
static void lfs_alloc_ckpoint(lfs_t *lfs);
|
||||||
|
|
||||||
|
|
||||||
@@ -2152,12 +2152,16 @@ static inline int lfsr_rbyd_cmp(
|
|||||||
|
|
||||||
// allocate an rbyd block
|
// allocate an rbyd block
|
||||||
static int lfsr_rbyd_alloc(lfs_t *lfs, lfsr_rbyd_t *rbyd) {
|
static int lfsr_rbyd_alloc(lfs_t *lfs, lfsr_rbyd_t *rbyd) {
|
||||||
*rbyd = (lfsr_rbyd_t){.weight=0, .trunk=0, .eoff=0, .cksum=0};
|
lfs_sblock_t block = lfs_alloc(lfs, true);
|
||||||
int err = lfs_alloc(lfs, &rbyd->blocks[0], true);
|
if (block < 0) {
|
||||||
if (err) {
|
return block;
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rbyd->blocks[0] = block;
|
||||||
|
rbyd->trunk = 0;
|
||||||
|
rbyd->weight = 0;
|
||||||
|
rbyd->eoff = 0;
|
||||||
|
rbyd->cksum = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6016,10 +6020,11 @@ static int lfsr_mdir_alloc__(lfs_t *lfs, lfsr_mdir_t *mdir,
|
|||||||
|
|
||||||
if (all) {
|
if (all) {
|
||||||
// allocate one block without an erase
|
// allocate one block without an erase
|
||||||
int err = lfs_alloc(lfs, &mdir->rbyd.blocks[1], false);
|
lfs_sblock_t block = lfs_alloc(lfs, false);
|
||||||
if (err) {
|
if (block < 0) {
|
||||||
return err;
|
return block;
|
||||||
}
|
}
|
||||||
|
mdir->rbyd.blocks[1] = block;
|
||||||
}
|
}
|
||||||
|
|
||||||
// read the new revision count
|
// read the new revision count
|
||||||
@@ -6039,10 +6044,11 @@ static int lfsr_mdir_alloc__(lfs_t *lfs, lfsr_mdir_t *mdir,
|
|||||||
|
|
||||||
relocate:;
|
relocate:;
|
||||||
// allocate another block with an erase
|
// allocate another block with an erase
|
||||||
err = lfs_alloc(lfs, &mdir->rbyd.blocks[0], true);
|
lfs_sblock_t block = lfs_alloc(lfs, true);
|
||||||
if (err) {
|
if (block < 0) {
|
||||||
return err;
|
return block;
|
||||||
}
|
}
|
||||||
|
mdir->rbyd.blocks[0] = block;
|
||||||
mdir->rbyd.weight = 0;
|
mdir->rbyd.weight = 0;
|
||||||
mdir->rbyd.trunk = 0;
|
mdir->rbyd.trunk = 0;
|
||||||
mdir->rbyd.eoff = 0;
|
mdir->rbyd.eoff = 0;
|
||||||
@@ -7718,18 +7724,15 @@ static int lfsr_mtree_pathlookup(lfs_t *lfs, const lfsr_mtree_t *mtree,
|
|||||||
|
|
||||||
// traversing littlefs is a bit complex, so we use a state machine to keep
|
// traversing littlefs is a bit complex, so we use a state machine to keep
|
||||||
// track of where we are
|
// track of where we are
|
||||||
//
|
|
||||||
// note the lower two bits are reserved so upper layers can iterate over
|
|
||||||
// redund blocks easily
|
|
||||||
enum {
|
enum {
|
||||||
LFSR_TSTATE_MROOTANCHOR = 0 << 2,
|
LFSR_TSTATE_MROOTANCHOR = 0,
|
||||||
LFSR_TSTATE_MROOTCHAIN = 1 << 2,
|
LFSR_TSTATE_MROOTCHAIN = 1,
|
||||||
LFSR_TSTATE_MTREE = 2 << 2,
|
LFSR_TSTATE_MTREE = 2,
|
||||||
LFSR_TSTATE_MDIR = 3 << 2,
|
LFSR_TSTATE_MDIR = 3,
|
||||||
LFSR_TSTATE_MDIRBTREE = 4 << 2,
|
LFSR_TSTATE_MDIRBTREE = 4,
|
||||||
LFSR_TSTATE_OMDIR = 5 << 2,
|
LFSR_TSTATE_OMDIR = 5,
|
||||||
LFSR_TSTATE_OMDIRBTREE = 6 << 2,
|
LFSR_TSTATE_OMDIRBTREE = 6,
|
||||||
LFSR_TSTATE_DONE = 7 << 2,
|
LFSR_TSTATE_DONE = 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define LFSR_MTRAVERSAL(_flags) \
|
#define LFSR_MTRAVERSAL(_flags) \
|
||||||
@@ -7742,10 +7745,29 @@ enum {
|
|||||||
.u.mtortoise.step=0, \
|
.u.mtortoise.step=0, \
|
||||||
.u.mtortoise.power=0})
|
.u.mtortoise.power=0})
|
||||||
|
|
||||||
|
static void lfsr_fs_traverserewind(lfs_t *lfs, lfsr_mtraversal_t *mt) {
|
||||||
|
(void)lfs;
|
||||||
|
mt->o.flags &= ~LFS_F_DIRTY;
|
||||||
|
mt->o.state = LFSR_TSTATE_MROOTANCHOR;
|
||||||
|
mt->o.mdir.mid = -1;
|
||||||
|
mt->u.mtortoise.mptr.blocks[0] = 0;
|
||||||
|
mt->u.mtortoise.mptr.blocks[1] = 0;
|
||||||
|
mt->u.mtortoise.step = 0;
|
||||||
|
mt->u.mtortoise.power = 0;
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool lfsr_t_ismtreeonly(uint32_t flags) {
|
static inline bool lfsr_t_ismtreeonly(uint32_t flags) {
|
||||||
return flags & LFS_T_MTREEONLY;
|
return flags & LFS_T_MTREEONLY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool lfsr_t_isexcl(uint32_t flags) {
|
||||||
|
return flags & LFS_T_EXCL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool lfsr_t_ismkconsistent(uint32_t flags) {
|
||||||
|
return flags & LFS_T_MKCONSISTENT;
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool lfsr_t_islookahead(uint32_t flags) {
|
static inline bool lfsr_t_islookahead(uint32_t flags) {
|
||||||
return flags & LFS_T_LOOKAHEAD;
|
return flags & LFS_T_LOOKAHEAD;
|
||||||
}
|
}
|
||||||
@@ -7762,16 +7784,8 @@ static inline bool lfsr_t_isckdata(uint32_t flags) {
|
|||||||
return flags & LFS_T_CKDATA;
|
return flags & LFS_T_CKDATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool lfsr_t_isdirty(uint32_t flags) {
|
static inline bool lfsr_f_isdirty(uint32_t flags) {
|
||||||
return flags & LFS_T_DIRTY;
|
return flags & LFS_F_DIRTY;
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool lfsr_t_iscorruptmetadata(uint32_t flags) {
|
|
||||||
return flags & LFS_T_CORRUPTMETADATA;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool lfsr_t_iscorruptdata(uint32_t flags) {
|
|
||||||
return flags & LFS_T_CORRUPTDATA;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -7793,12 +7807,12 @@ static int lfsr_bshrub_traverse(lfs_t *lfs, const lfsr_file_t *file,
|
|||||||
static int lfsr_fs_traverse_(lfs_t *lfs, lfsr_mtraversal_t *mt,
|
static int lfsr_fs_traverse_(lfs_t *lfs, lfsr_mtraversal_t *mt,
|
||||||
lfsr_mtinfo_t *mtinfo) {
|
lfsr_mtinfo_t *mtinfo) {
|
||||||
while (true) {
|
while (true) {
|
||||||
switch (mt->o.state >> 2) {
|
switch (mt->o.state) {
|
||||||
// start with the mrootanchor 0x{0,1}
|
// start with the mrootanchor 0x{0,1}
|
||||||
//
|
//
|
||||||
// note we make sure to include all mroots in our mroot chain!
|
// note we make sure to include all mroots in our mroot chain!
|
||||||
//
|
//
|
||||||
case LFSR_TSTATE_MROOTANCHOR >> 2:;
|
case LFSR_TSTATE_MROOTANCHOR:;
|
||||||
// fetch the first mroot 0x{0,1}
|
// fetch the first mroot 0x{0,1}
|
||||||
int err = lfsr_mdir_fetch(lfs, &mt->o.mdir,
|
int err = lfsr_mdir_fetch(lfs, &mt->o.mdir,
|
||||||
-1, &LFSR_MPTR_MROOTANCHOR());
|
-1, &LFSR_MPTR_MROOTANCHOR());
|
||||||
@@ -7814,7 +7828,7 @@ static int lfsr_fs_traverse_(lfs_t *lfs, lfsr_mtraversal_t *mt,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// traverse the mroot chain, checking for mroot/mtree/mdir
|
// traverse the mroot chain, checking for mroot/mtree/mdir
|
||||||
case LFSR_TSTATE_MROOTCHAIN >> 2:;
|
case LFSR_TSTATE_MROOTCHAIN:;
|
||||||
// lookup mroot, if we find one this just an mroot chain link
|
// lookup mroot, if we find one this just an mroot chain link
|
||||||
lfsr_tag_t tag;
|
lfsr_tag_t tag;
|
||||||
lfsr_data_t data;
|
lfsr_data_t data;
|
||||||
@@ -7915,7 +7929,7 @@ static int lfsr_fs_traverse_(lfs_t *lfs, lfsr_mtraversal_t *mt,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// traverse the mtree, including both inner btree nodes and mdirs
|
// traverse the mtree, including both inner btree nodes and mdirs
|
||||||
case LFSR_TSTATE_MTREE >> 2:;
|
case LFSR_TSTATE_MTREE:;
|
||||||
// no mtree? transition to traversing any opened mdirs
|
// no mtree? transition to traversing any opened mdirs
|
||||||
if (lfsr_mtree_ismptr(&lfs->mtree)) {
|
if (lfsr_mtree_ismptr(&lfs->mtree)) {
|
||||||
mt->u.o = lfs->opened;
|
mt->u.o = lfs->opened;
|
||||||
@@ -7981,7 +7995,7 @@ static int lfsr_fs_traverse_(lfs_t *lfs, lfsr_mtraversal_t *mt,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// scan for blocks/btrees in the current mdir
|
// scan for blocks/btrees in the current mdir
|
||||||
case LFSR_TSTATE_MDIR >> 2:;
|
case LFSR_TSTATE_MDIR:;
|
||||||
// not traversing all blocks? have we exceeded our mdir's weight?
|
// not traversing all blocks? have we exceeded our mdir's weight?
|
||||||
// return to mtree traversal
|
// return to mtree traversal
|
||||||
if (lfsr_t_ismtreeonly(mt->o.flags)
|
if (lfsr_t_ismtreeonly(mt->o.flags)
|
||||||
@@ -8033,7 +8047,7 @@ static int lfsr_fs_traverse_(lfs_t *lfs, lfsr_mtraversal_t *mt,
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// scan for blocks/btrees in our opened file list
|
// scan for blocks/btrees in our opened file list
|
||||||
case LFSR_TSTATE_OMDIR >> 2:;
|
case LFSR_TSTATE_OMDIR:;
|
||||||
// not traversing all blocks? reached end of opened file list?
|
// not traversing all blocks? reached end of opened file list?
|
||||||
if (lfsr_t_ismtreeonly(mt->o.flags) || !mt->u.o) {
|
if (lfsr_t_ismtreeonly(mt->o.flags) || !mt->u.o) {
|
||||||
mt->o.state = LFSR_TSTATE_DONE;
|
mt->o.state = LFSR_TSTATE_DONE;
|
||||||
@@ -8056,21 +8070,19 @@ static int lfsr_fs_traverse_(lfs_t *lfs, lfsr_mtraversal_t *mt,
|
|||||||
|
|
||||||
// traverse any file btrees, including both inner btree nodes and
|
// traverse any file btrees, including both inner btree nodes and
|
||||||
// block pointers
|
// block pointers
|
||||||
case LFSR_TSTATE_MDIRBTREE >> 2:;
|
case LFSR_TSTATE_MDIRBTREE:;
|
||||||
case LFSR_TSTATE_OMDIRBTREE >> 2:;
|
case LFSR_TSTATE_OMDIRBTREE:;
|
||||||
// traverse through our file
|
// traverse through our file
|
||||||
err = lfsr_bshrub_traverse(lfs, (const lfsr_file_t*)mt, &mt->bt,
|
err = lfsr_bshrub_traverse(lfs, (const lfsr_file_t*)mt, &mt->bt,
|
||||||
&btinfo);
|
&btinfo);
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err == LFS_ERR_NOENT) {
|
if (err == LFS_ERR_NOENT) {
|
||||||
// end of btree? go to next file
|
// end of btree? go to next file
|
||||||
if ((mt->o.state >> 2)
|
if (mt->o.state == LFSR_TSTATE_MDIRBTREE) {
|
||||||
== (LFSR_TSTATE_MDIRBTREE >> 2)) {
|
|
||||||
mt->o.mdir.mid += 1;
|
mt->o.mdir.mid += 1;
|
||||||
mt->o.state = LFSR_TSTATE_MDIR;
|
mt->o.state = LFSR_TSTATE_MDIR;
|
||||||
continue;
|
continue;
|
||||||
} else if ((mt->o.state >> 2)
|
} else if (mt->o.state == LFSR_TSTATE_OMDIRBTREE) {
|
||||||
== (LFSR_TSTATE_OMDIRBTREE >> 2)) {
|
|
||||||
mt->u.o = mt->u.o->next;
|
mt->u.o = mt->u.o->next;
|
||||||
mt->o.state = LFSR_TSTATE_OMDIR;
|
mt->o.state = LFSR_TSTATE_OMDIR;
|
||||||
continue;
|
continue;
|
||||||
@@ -8101,7 +8113,7 @@ static int lfsr_fs_traverse_(lfs_t *lfs, lfsr_mtraversal_t *mt,
|
|||||||
LFS_UNREACHABLE();
|
LFS_UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
case LFSR_TSTATE_DONE >> 2:;
|
case LFSR_TSTATE_DONE:;
|
||||||
return LFS_ERR_NOENT;
|
return LFS_ERR_NOENT;
|
||||||
|
|
||||||
default:;
|
default:;
|
||||||
@@ -8110,7 +8122,11 @@ static int lfsr_fs_traverse_(lfs_t *lfs, lfsr_mtraversal_t *mt,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// high-level traversal, handle extra features here
|
// needed in lfsr_fs_traverse
|
||||||
|
static void lfs_alloc_markinuse(lfs_t *lfs, lfs_block_t block);
|
||||||
|
|
||||||
|
// high-level immutable traversal, handle extra features here,
|
||||||
|
// but no mutation!
|
||||||
static int lfsr_fs_traverse(lfs_t *lfs, lfsr_mtraversal_t *mt,
|
static int lfsr_fs_traverse(lfs_t *lfs, lfsr_mtraversal_t *mt,
|
||||||
lfsr_mtinfo_t *mtinfo) {
|
lfsr_mtinfo_t *mtinfo) {
|
||||||
int err = lfsr_fs_traverse_(lfs, mt, mtinfo);
|
int err = lfsr_fs_traverse_(lfs, mt, mtinfo);
|
||||||
@@ -8139,9 +8155,34 @@ static int lfsr_fs_traverse(lfs_t *lfs, lfsr_mtraversal_t *mt,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// track in-use blocks
|
||||||
|
if (lfsr_t_islookahead(mt->o.flags)) {
|
||||||
|
if (mtinfo->tag == LFSR_TAG_MDIR) {
|
||||||
|
lfs_alloc_markinuse(lfs, mtinfo->u.mdir.rbyd.blocks[0]);
|
||||||
|
lfs_alloc_markinuse(lfs, mtinfo->u.mdir.rbyd.blocks[1]);
|
||||||
|
|
||||||
|
} else if (mtinfo->tag == LFSR_TAG_BRANCH) {
|
||||||
|
lfs_alloc_markinuse(lfs, mtinfo->u.rbyd.blocks[0]);
|
||||||
|
|
||||||
|
} else if (mtinfo->tag == LFSR_TAG_BLOCK) {
|
||||||
|
lfs_alloc_markinuse(lfs, mtinfo->u.bptr.data.u.disk.block);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
LFS_UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// high-level mutating traversal, handle extra features that require
|
||||||
|
// mutation here, upper layers should call lfs_alloc_ckpoint as needed
|
||||||
|
static int lfsr_fs_traversemut(lfs_t *lfs, lfsr_mtraversal_t *mt,
|
||||||
|
lfsr_mtinfo_t *mtinfo) {
|
||||||
|
// TODO
|
||||||
|
return lfsr_fs_traverse(lfs, mt, mtinfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Superblock things ///
|
/// Superblock things ///
|
||||||
|
|
||||||
@@ -8898,81 +8939,128 @@ int lfsr_format(lfs_t *lfs, const struct lfs_config *cfg) {
|
|||||||
|
|
||||||
/// Block allocator ///
|
/// Block allocator ///
|
||||||
|
|
||||||
// Allocations should call this when all allocated blocks are committed to
|
// checkpoint the allocator
|
||||||
// the filesystem, either in the mtree or in tracked mdirs. After a
|
//
|
||||||
// checkpoint, the block allocator may realloc any untracked blocks.
|
// operations that need to alloc should call this to indicate all in-use
|
||||||
|
// blocks are either committed into the filesystem or tracked by an opened
|
||||||
|
// mdir
|
||||||
static void lfs_alloc_ckpoint(lfs_t *lfs) {
|
static void lfs_alloc_ckpoint(lfs_t *lfs) {
|
||||||
lfs->lookahead.ckpoint = lfs->block_count;
|
lfs->lookahead.ckpoint = lfs->block_count;
|
||||||
|
|
||||||
|
// mark all opened traversals as dirty
|
||||||
|
for (lfsr_omdir_t *o = lfs->opened; o; o = o->next) {
|
||||||
|
if (o->type == LFS_TYPE_TRAVERSAL) {
|
||||||
|
o->flags |= LFS_F_DIRTY;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Discard lookahead state, this is necessary if block_count changes
|
// discard any lookahead state, this is necessary if block_count changes
|
||||||
static void lfs_alloc_discard(lfs_t *lfs) {
|
static void lfs_alloc_discard(lfs_t *lfs) {
|
||||||
|
// go ahead shift to next available block, we probably want the next
|
||||||
|
// scan to start here
|
||||||
lfs->lookahead.start = (lfs->lookahead.start + lfs->lookahead.next)
|
lfs->lookahead.start = (lfs->lookahead.start + lfs->lookahead.next)
|
||||||
% lfs->block_count;
|
% lfs->block_count;
|
||||||
lfs->lookahead.next = 0;
|
lfs->lookahead.next = 0;
|
||||||
lfs->lookahead.size = 0;
|
lfs->lookahead.size = 0;
|
||||||
lfs->lookahead.ckpoint = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void lfs_alloc_setinuse(lfs_t *lfs, lfs_block_t block) {
|
// shift the lookahead buffer to try to allocate more blocks, may do nothing
|
||||||
|
static void lfs_alloc_shift(lfs_t *lfs) {
|
||||||
|
// do nothing if shifting would make no progress
|
||||||
|
if (lfs->lookahead.next > 0) {
|
||||||
|
// discard already shifts to the next block
|
||||||
|
lfs_alloc_discard(lfs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// zero lookahead buffer
|
||||||
|
if (lfs->lookahead.size == 0) {
|
||||||
|
lfs_memset(lfs->lookahead.buffer, 0, lfs->cfg->lookahead_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't update size until a successful lookahead scan
|
||||||
|
}
|
||||||
|
|
||||||
|
// mark a block as in-use
|
||||||
|
static void lfs_alloc_markinuse(lfs_t *lfs, lfs_block_t block) {
|
||||||
// translate to lookahead-relative
|
// translate to lookahead-relative
|
||||||
lfs_block_t rel = (block + lfs->block_count - lfs->lookahead.start)
|
lfs_block_t mark = (block + lfs->block_count - lfs->lookahead.start)
|
||||||
% lfs->block_count;
|
% lfs->block_count;
|
||||||
if (rel < lfs->lookahead.size) {
|
if (mark < 8*lfs->cfg->lookahead_size) {
|
||||||
// mark as in-use
|
// mark as in-use
|
||||||
lfs->lookahead.buffer[rel / 8] |= 1 << (rel % 8);
|
lfs->lookahead.buffer[mark / 8] |= 1 << (mark % 8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int lfs_alloc(lfs_t *lfs, lfs_block_t *block, bool erase) {
|
// needed in lfs_alloc_markfree
|
||||||
|
static lfs_sblock_t lfs_alloc_findnext(lfs_t *lfs);
|
||||||
|
|
||||||
|
// mark any not-in-use blocks as free
|
||||||
|
static void lfs_alloc_markfree(lfs_t *lfs) {
|
||||||
|
// make lookahead buffer usable
|
||||||
|
lfs->lookahead.size = lfs_min(
|
||||||
|
8*lfs->cfg->lookahead_size,
|
||||||
|
lfs->lookahead.ckpoint);
|
||||||
|
|
||||||
|
// eagerly find the next free block so shift can make progress
|
||||||
|
lfs_alloc_findnext(lfs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// find next free block in lookahead buffer, if there is one
|
||||||
|
static lfs_sblock_t lfs_alloc_findnext(lfs_t *lfs) {
|
||||||
|
while (lfs->lookahead.next < lfs->lookahead.size) {
|
||||||
|
if (!(lfs->lookahead.buffer[lfs->lookahead.next / 8]
|
||||||
|
& (1 << (lfs->lookahead.next % 8)))) {
|
||||||
|
// found a free block
|
||||||
|
return (lfs->lookahead.start + lfs->lookahead.next)
|
||||||
|
% lfs->block_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
lfs->lookahead.next += 1;
|
||||||
|
lfs->lookahead.ckpoint -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return LFS_ERR_NOSPC;
|
||||||
|
}
|
||||||
|
|
||||||
|
static lfs_sblock_t lfs_alloc(lfs_t *lfs, bool erase) {
|
||||||
while (true) {
|
while (true) {
|
||||||
// scan our lookahead buffer for free blocks
|
// scan our lookahead buffer for free blocks
|
||||||
while (lfs->lookahead.next < lfs->lookahead.size) {
|
lfs_sblock_t block = lfs_alloc_findnext(lfs);
|
||||||
if (!(lfs->lookahead.buffer[lfs->lookahead.next / 8]
|
if (block < 0 && block != LFS_ERR_NOSPC) {
|
||||||
& (1 << (lfs->lookahead.next % 8)))) {
|
return block;
|
||||||
// found a free block
|
}
|
||||||
*block = (lfs->lookahead.start + lfs->lookahead.next)
|
|
||||||
% lfs->block_count;
|
|
||||||
|
|
||||||
// we should never alloc blocks {0,1}
|
if (block != LFS_ERR_NOSPC) {
|
||||||
LFS_ASSERT(*block != 0 && *block != 1);
|
// we should never alloc blocks {0,1}
|
||||||
|
LFS_ASSERT(block != 0 && block != 1);
|
||||||
|
|
||||||
// erase requested?
|
// erase requested?
|
||||||
if (erase) {
|
if (erase) {
|
||||||
int err = lfsr_bd_erase(lfs, *block);
|
int err = lfsr_bd_erase(lfs, block);
|
||||||
if (err) {
|
if (err) {
|
||||||
// bad erase? try another block
|
// bad erase? try another block
|
||||||
if (err == LFS_ERR_CORRUPT) {
|
if (err == LFS_ERR_CORRUPT) {
|
||||||
goto next;
|
lfs->lookahead.next += 1;
|
||||||
}
|
lfs->lookahead.ckpoint -= 1;
|
||||||
return err;
|
continue;
|
||||||
}
|
}
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eagerly find next free block to maximize how many blocks
|
|
||||||
// lfs_alloc_ckpoint makes available for scanning
|
|
||||||
while (true) {
|
|
||||||
lfs->lookahead.next += 1;
|
|
||||||
lfs->lookahead.ckpoint -= 1;
|
|
||||||
|
|
||||||
if (lfs->lookahead.next >= lfs->lookahead.size
|
|
||||||
|| !(lfs->lookahead.buffer[lfs->lookahead.next / 8]
|
|
||||||
& (1 << (lfs->lookahead.next % 8)))) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
next:;
|
// eagerly find the next free block to maximize how many blocks
|
||||||
|
// lfs_alloc_ckpoint makes available for scanning
|
||||||
lfs->lookahead.next += 1;
|
lfs->lookahead.next += 1;
|
||||||
lfs->lookahead.ckpoint -= 1;
|
lfs->lookahead.ckpoint -= 1;
|
||||||
|
lfs_alloc_findnext(lfs);
|
||||||
|
|
||||||
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
// In order to keep our block allocator from spinning forever when our
|
// In order to keep our block allocator from spinning forever when our
|
||||||
// filesystem is full, we mark points where there are no in-flight
|
// filesystem is full, we mark points where there are no in-flight
|
||||||
// allocations with a checkpoint before starting a set of allocaitons.
|
// allocations with a checkpoint before starting a set of allocations.
|
||||||
//
|
//
|
||||||
// If we've looked at all blocks since the last checkpoint, we report
|
// If we've looked at all blocks since the last checkpoint, we report
|
||||||
// the filesystem as out of storage.
|
// the filesystem as out of storage.
|
||||||
@@ -8984,48 +9072,27 @@ static int lfs_alloc(lfs_t *lfs, lfs_block_t *block, bool erase) {
|
|||||||
return LFS_ERR_NOSPC;
|
return LFS_ERR_NOSPC;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No blocks in our lookahead buffer, we need to scan the filesystem for
|
// no blocks in our lookahead buffer, we need to scan the filesystem
|
||||||
// unused blocks in the next lookahead window.
|
// for unused blocks in the next lookahead window
|
||||||
//
|
lfs_alloc_shift(lfs);
|
||||||
// note we limit the lookahead window to at most the amount of blocks
|
|
||||||
// checkpointed, this prevents the above math from underflowing
|
|
||||||
//
|
|
||||||
lfs->lookahead.start = (lfs->lookahead.start + lfs->lookahead.size)
|
|
||||||
% lfs->block_count;
|
|
||||||
lfs->lookahead.next = 0;
|
|
||||||
lfs->lookahead.size = lfs_min(
|
|
||||||
8*lfs->cfg->lookahead_size,
|
|
||||||
lfs->lookahead.ckpoint);
|
|
||||||
lfs_memset(lfs->lookahead.buffer, 0, lfs->cfg->lookahead_size);
|
|
||||||
|
|
||||||
// traverse the filesystem, building up knowledge of what blocks are
|
// traverse the filesystem, building up knowledge of what blocks are
|
||||||
// in use in our lookahead window
|
// in use in our lookahead window
|
||||||
lfsr_mtraversal_t mt = LFSR_MTRAVERSAL(0);
|
lfsr_mtraversal_t mt = LFSR_MTRAVERSAL(LFS_T_LOOKAHEAD);
|
||||||
while (true) {
|
while (true) {
|
||||||
lfsr_mtinfo_t mtinfo;
|
lfsr_mtinfo_t mtinfo;
|
||||||
int err = lfsr_fs_traverse(lfs, &mt, &mtinfo);
|
int err = lfsr_fs_traverse(lfs, &mt, &mtinfo);
|
||||||
if (err) {
|
if (err) {
|
||||||
|
LFS_ASSERT(err != LFS_ERR_BUSY);
|
||||||
if (err == LFS_ERR_NOENT) {
|
if (err == LFS_ERR_NOENT) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
// mark any blocks we see at in-use, including any btree/mdir blocks
|
|
||||||
if (mtinfo.tag == LFSR_TAG_MDIR) {
|
|
||||||
lfs_alloc_setinuse(lfs, mtinfo.u.mdir.rbyd.blocks[1]);
|
|
||||||
lfs_alloc_setinuse(lfs, mtinfo.u.mdir.rbyd.blocks[0]);
|
|
||||||
|
|
||||||
} else if (mtinfo.tag == LFSR_TAG_BRANCH) {
|
|
||||||
lfs_alloc_setinuse(lfs, mtinfo.u.rbyd.blocks[0]);
|
|
||||||
|
|
||||||
} else if (mtinfo.tag == LFSR_TAG_BLOCK) {
|
|
||||||
lfs_alloc_setinuse(lfs, mtinfo.u.bptr.data.u.disk.block);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
LFS_UNREACHABLE();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mark anything not seen as free
|
||||||
|
lfs_alloc_markfree(lfs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9270,20 +9337,21 @@ failed:;
|
|||||||
|
|
||||||
/// High-level filesystem traversal ///
|
/// High-level filesystem traversal ///
|
||||||
|
|
||||||
int lfsr_traversal_open(lfs_t *lfs, lfsr_traversal_t *traversal,
|
// needed in lfsr_traversal_open
|
||||||
uint32_t flags) {
|
static int lfsr_traversal_rewind_(lfs_t *lfs, lfsr_traversal_t *t);
|
||||||
|
|
||||||
|
int lfsr_traversal_open(lfs_t *lfs, lfsr_traversal_t *t, uint32_t flags) {
|
||||||
// already open?
|
// already open?
|
||||||
LFS_ASSERT(!lfsr_opened_isopen(lfs, &traversal->mt.o));
|
LFS_ASSERT(!lfsr_opened_isopen(lfs, &t->mt.o));
|
||||||
// some flags don't make sense when only traversing the mtree
|
// 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_islookahead(flags));
|
||||||
LFS_ASSERT(!lfsr_t_ismtreeonly(flags) || !lfsr_t_isckdata(flags));
|
LFS_ASSERT(!lfsr_t_ismtreeonly(flags) || !lfsr_t_isckdata(flags));
|
||||||
// these flags are returned by tinfo, not provided here
|
// these flags are internal and shouldn't be provided by the user
|
||||||
LFS_ASSERT(!lfsr_t_isdirty(flags));
|
LFS_ASSERT(!lfsr_f_isdirty(flags));
|
||||||
LFS_ASSERT(!lfsr_t_iscorruptmetadata(flags));
|
|
||||||
LFS_ASSERT(!lfsr_t_iscorruptdata(flags));
|
|
||||||
|
|
||||||
// some flags mutate the filesystem
|
// some flags mutate the filesystem
|
||||||
if (lfsr_t_iscompact(flags)) {
|
if (lfsr_t_ismkconsistent(flags)
|
||||||
|
|| lfsr_t_iscompact(flags)) {
|
||||||
// prepare our filesystem for writing
|
// prepare our filesystem for writing
|
||||||
int err = lfsr_fs_mkconsistent(lfs);
|
int err = lfsr_fs_mkconsistent(lfs);
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -9292,90 +9360,115 @@ int lfsr_traversal_open(lfs_t *lfs, lfsr_traversal_t *traversal,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// setup traversal state
|
// setup traversal state
|
||||||
traversal->mt = LFSR_MTRAVERSAL(flags);
|
t->mt.o.type = LFS_TYPE_TRAVERSAL;
|
||||||
traversal->count = 0;
|
t->mt.o.flags = flags;
|
||||||
|
|
||||||
|
// let rewind initialize/reset things
|
||||||
|
int err = lfsr_traversal_rewind_(lfs, t);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
// add to tracked mdirs
|
// add to tracked mdirs
|
||||||
lfsr_opened_add(lfs, &traversal->mt.o);
|
lfsr_opened_add(lfs, &t->mt.o);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lfsr_traversal_close(lfs_t *lfs, lfsr_traversal_t *traversal) {
|
int lfsr_traversal_close(lfs_t *lfs, lfsr_traversal_t *t) {
|
||||||
LFS_ASSERT(lfsr_opened_isopen(lfs, &traversal->mt.o));
|
LFS_ASSERT(lfsr_opened_isopen(lfs, &t->mt.o));
|
||||||
|
|
||||||
// remove from tracked mdirs
|
// remove from tracked mdirs
|
||||||
lfsr_opened_remove(lfs, &traversal->mt.o);
|
lfsr_opened_remove(lfs, &t->mt.o);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lfsr_traversal_read(lfs_t *lfs, lfsr_traversal_t *traversal,
|
int lfsr_traversal_read(lfs_t *lfs, lfsr_traversal_t *t,
|
||||||
struct lfs_tinfo *tinfo) {
|
struct lfs_tinfo *tinfo) {
|
||||||
LFS_ASSERT(lfsr_opened_isopen(lfs, &traversal->mt.o));
|
LFS_ASSERT(lfsr_opened_isopen(lfs, &t->mt.o));
|
||||||
|
|
||||||
|
// traversal dirty and excl? terminate early
|
||||||
|
if (lfsr_t_isexcl(t->mt.o.flags)
|
||||||
|
&& lfsr_f_isdirty(t->mt.o.flags)) {
|
||||||
|
return LFS_ERR_BUSY;
|
||||||
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
// some redund blocks left over?
|
// some redund blocks left over?
|
||||||
if (traversal->count > 0) {
|
if (t->blocks[0] != -1) {
|
||||||
// write our traversal info
|
// write our traversal info
|
||||||
tinfo->flags = traversal->mt.o.flags
|
tinfo->btype = t->btype;
|
||||||
& LFS_T_DIRTY
|
tinfo->block = t->blocks[0];
|
||||||
& LFS_T_CORRUPTMETADATA
|
|
||||||
& LFS_T_CORRUPTDATA;
|
|
||||||
tinfo->btype = traversal->btype;
|
|
||||||
tinfo->block = traversal->blocks[0];
|
|
||||||
|
|
||||||
traversal->blocks[0] = traversal->blocks[1];
|
t->blocks[0] = t->blocks[1];
|
||||||
traversal->count -= 1;
|
t->blocks[1] = -1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// find next block
|
// find next block
|
||||||
lfsr_mtinfo_t mtinfo;
|
lfsr_mtinfo_t mtinfo;
|
||||||
int err = lfsr_fs_traverse(lfs, &traversal->mt, &mtinfo);
|
int err = lfsr_fs_traversemut(lfs, &t->mt, &mtinfo);
|
||||||
if (err) {
|
if (err) {
|
||||||
|
// end of traversal?
|
||||||
|
if (err == LFS_ERR_NOENT) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
// figure out type/blocks
|
// figure out type/blocks
|
||||||
if (mtinfo.tag == LFSR_TAG_MDIR) {
|
if (mtinfo.tag == LFSR_TAG_MDIR) {
|
||||||
traversal->btype = LFS_BTYPE_MDIR;
|
t->btype = LFS_BTYPE_MDIR;
|
||||||
traversal->blocks[0] = mtinfo.u.mdir.rbyd.blocks[0];
|
t->blocks[0] = mtinfo.u.mdir.rbyd.blocks[0];
|
||||||
traversal->blocks[1] = mtinfo.u.mdir.rbyd.blocks[1];
|
t->blocks[1] = mtinfo.u.mdir.rbyd.blocks[1];
|
||||||
traversal->count = 2;
|
|
||||||
|
|
||||||
} else if (mtinfo.tag == LFSR_TAG_BRANCH) {
|
} else if (mtinfo.tag == LFSR_TAG_BRANCH) {
|
||||||
traversal->btype = LFS_BTYPE_BTREE;
|
t->btype = LFS_BTYPE_BTREE;
|
||||||
traversal->blocks[0] = mtinfo.u.rbyd.blocks[0];
|
t->blocks[0] = mtinfo.u.rbyd.blocks[0];
|
||||||
traversal->count = 1;
|
t->blocks[1] = -1;
|
||||||
|
|
||||||
} else if (mtinfo.tag == LFSR_TAG_BLOCK) {
|
} else if (mtinfo.tag == LFSR_TAG_BLOCK) {
|
||||||
traversal->btype = LFS_BTYPE_DATA;
|
t->btype = LFS_BTYPE_DATA;
|
||||||
traversal->blocks[0] = mtinfo.u.bptr.data.u.disk.block;
|
t->blocks[0] = mtinfo.u.bptr.data.u.disk.block;
|
||||||
traversal->count = 1;
|
t->blocks[1] = -1;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
LFS_UNREACHABLE();
|
LFS_UNREACHABLE();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
done:;
|
||||||
|
// was a lookahead scan successful?
|
||||||
|
if (lfsr_t_islookahead(t->mt.o.flags)
|
||||||
|
&& !lfsr_f_isdirty(t->mt.o.flags)) {
|
||||||
|
lfs_alloc_markfree(lfs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// return BUSY if we're dirty, NOENT if we're clean
|
||||||
|
return (lfsr_f_isdirty(t->mt.o.flags))
|
||||||
|
? LFS_ERR_BUSY
|
||||||
|
: LFS_ERR_NOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lfsr_traversal_rewind(lfs_t *lfs, lfsr_traversal_t *traversal) {
|
static int lfsr_traversal_rewind_(lfs_t *lfs, lfsr_traversal_t *t) {
|
||||||
LFS_ASSERT(lfsr_opened_isopen(lfs, &traversal->mt.o));
|
|
||||||
|
|
||||||
// reset traversal state
|
// reset traversal state
|
||||||
traversal->mt.o.flags &= ~LFS_T_DIRTY
|
lfsr_fs_traverserewind(lfs, &t->mt);
|
||||||
& ~LFS_T_CORRUPTMETADATA
|
t->blocks[0] = -1;
|
||||||
& ~LFS_T_CORRUPTDATA;
|
t->blocks[1] = -1;
|
||||||
traversal->mt.o.state = LFSR_TSTATE_MROOTANCHOR;
|
|
||||||
traversal->mt.o.mdir.mid = -1;
|
// shift the lookahead buffer if requested
|
||||||
traversal->mt.u.mtortoise.mptr.blocks[0] = 0;
|
if (lfsr_t_islookahead(t->mt.o.flags)) {
|
||||||
traversal->mt.u.mtortoise.mptr.blocks[1] = 0;
|
lfs_alloc_shift(lfs);
|
||||||
traversal->mt.u.mtortoise.step = 0;
|
}
|
||||||
traversal->mt.u.mtortoise.power = 0;
|
|
||||||
traversal->count = 0;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int lfsr_traversal_rewind(lfs_t *lfs, lfsr_traversal_t *t) {
|
||||||
|
LFS_ASSERT(lfsr_opened_isopen(lfs, &t->mt.o));
|
||||||
|
|
||||||
|
return lfsr_traversal_rewind_(lfs, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -11400,12 +11493,12 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
|
|||||||
//
|
//
|
||||||
// note if we relocate, we rewrite the entire block from block_start
|
// note if we relocate, we rewrite the entire block from block_start
|
||||||
// using what we can find in our tree
|
// using what we can find in our tree
|
||||||
int err = lfs_alloc(lfs, &bptr.data.u.disk.block, true);
|
lfs_sblock_t block = lfs_alloc(lfs, true);
|
||||||
if (err) {
|
if (block < 0) {
|
||||||
return err;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
bptr.data = LFSR_DATA_DISK(bptr.data.u.disk.block, 0, 0);
|
bptr.data = LFSR_DATA_DISK(block, 0, 0);
|
||||||
bptr.cksize = 0;
|
bptr.cksize = 0;
|
||||||
bptr.cksum = 0;
|
bptr.cksum = 0;
|
||||||
|
|
||||||
@@ -11435,7 +11528,7 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
|
|||||||
lfs_ssize_t d_ = lfs_min(
|
lfs_ssize_t d_ = lfs_min(
|
||||||
d,
|
d,
|
||||||
size - (pos_ - pos));
|
size - (pos_ - pos));
|
||||||
err = lfsr_bd_prog(lfs, bptr.data.u.disk.block,
|
int err = lfsr_bd_prog(lfs, bptr.data.u.disk.block,
|
||||||
bptr.cksize,
|
bptr.cksize,
|
||||||
&buffer[pos_ - pos], d_,
|
&buffer[pos_ - pos], d_,
|
||||||
&bptr.cksum, true);
|
&bptr.cksum, true);
|
||||||
@@ -11463,7 +11556,7 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
|
|||||||
lfsr_tag_t tag_;
|
lfsr_tag_t tag_;
|
||||||
lfsr_bid_t weight_;
|
lfsr_bid_t weight_;
|
||||||
lfsr_bptr_t bptr_;
|
lfsr_bptr_t bptr_;
|
||||||
err = lfsr_bshrub_lookupnext(lfs, file, pos_,
|
int err = lfsr_bshrub_lookupnext(lfs, file, pos_,
|
||||||
&bid_, &tag_, &weight_, &bptr_);
|
&bid_, &tag_, &weight_, &bptr_);
|
||||||
if (err) {
|
if (err) {
|
||||||
LFS_ASSERT(err != LFS_ERR_NOENT);
|
LFS_ASSERT(err != LFS_ERR_NOENT);
|
||||||
@@ -11518,7 +11611,7 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// found a hole? fill with zeros
|
// found a hole? fill with zeros
|
||||||
err = lfsr_bd_set(lfs, bptr.data.u.disk.block, bptr.cksize,
|
int err = lfsr_bd_set(lfs, bptr.data.u.disk.block, bptr.cksize,
|
||||||
0, d,
|
0, d,
|
||||||
&bptr.cksum, true);
|
&bptr.cksum, true);
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -11543,7 +11636,7 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
|
|||||||
bptr.cksize -= d;
|
bptr.cksize -= d;
|
||||||
|
|
||||||
// finalize our write
|
// finalize our write
|
||||||
err = lfsr_bd_flush(lfs, &bptr.cksum, true);
|
int err = lfsr_bd_flush(lfs, &bptr.cksum, true);
|
||||||
if (err) {
|
if (err) {
|
||||||
// bad prog? try another block
|
// bad prog? try another block
|
||||||
if (err == LFS_ERR_CORRUPT) {
|
if (err == LFS_ERR_CORRUPT) {
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ typedef uint32_t lfs_off_t;
|
|||||||
typedef int32_t lfs_soff_t;
|
typedef int32_t lfs_soff_t;
|
||||||
|
|
||||||
typedef uint32_t lfs_block_t;
|
typedef uint32_t lfs_block_t;
|
||||||
|
typedef int32_t lfs_sblock_t;
|
||||||
|
|
||||||
typedef uint32_t lfsr_rid_t;
|
typedef uint32_t lfsr_rid_t;
|
||||||
typedef int32_t lfsr_srid_t;
|
typedef int32_t lfsr_srid_t;
|
||||||
@@ -97,6 +98,7 @@ enum lfs_error {
|
|||||||
LFS_ERR_UNKNOWN = -1, // Unknown error
|
LFS_ERR_UNKNOWN = -1, // Unknown error
|
||||||
LFS_ERR_INVAL = -22, // Invalid parameter
|
LFS_ERR_INVAL = -22, // Invalid parameter
|
||||||
LFS_ERR_NOTSUP = -95, // Operation not supported
|
LFS_ERR_NOTSUP = -95, // Operation not supported
|
||||||
|
LFS_ERR_BUSY = -16, // Device or resource busy
|
||||||
LFS_ERR_IO = -5, // Error during device operation
|
LFS_ERR_IO = -5, // Error during device operation
|
||||||
LFS_ERR_CORRUPT = -84, // Corrupted
|
LFS_ERR_CORRUPT = -84, // Corrupted
|
||||||
LFS_ERR_NOENT = -2, // No directory entry
|
LFS_ERR_NOENT = -2, // No directory entry
|
||||||
@@ -166,20 +168,19 @@ enum lfs_btype {
|
|||||||
// Traversal flags
|
// Traversal flags
|
||||||
enum lfs_traversal_flags {
|
enum lfs_traversal_flags {
|
||||||
// traversal open flags
|
// traversal open flags
|
||||||
LFS_T_MTREEONLY = 0x0010, // Only traverse the mtree
|
LFS_T_MTREEONLY = 0x0008, // Only traverse the mtree
|
||||||
LFS_T_LOOKAHEAD = 0x0020, // Populate lookahead buffer
|
LFS_T_EXCL = 0x0010, // Terminate if filesystem modified
|
||||||
LFS_T_COMPACT = 0x0040, // Compact metadata logs
|
LFS_T_MKCONSISTENT = 0x0020, // Make the filesystem consistent
|
||||||
LFS_T_CKMETADATA = 0x0080, // Check metadata checksums
|
LFS_T_LOOKAHEAD = 0x0040, // Populate lookahead buffer
|
||||||
LFS_T_CKDATA = 0x0100, // Check data checksums
|
LFS_T_COMPACT = 0x0080, // Compact metadata logs
|
||||||
|
LFS_T_CKMETADATA = 0x0100, // Check metadata checksums
|
||||||
|
LFS_T_CKDATA = 0x0200, // Check data checksums
|
||||||
// TODO
|
// TODO
|
||||||
// LFS_T_REPAIRMETADATA = 0x0100, // Repair metadata blocks
|
// LFS_T_REPAIRMETADATA = 0x0400, // Repair metadata blocks
|
||||||
// LFS_T_REPAIRDATA = 0x0200, // Repair data blocks
|
// LFS_T_REPAIRDATA = 0x0800, // Repair data blocks
|
||||||
|
|
||||||
// flags set in tinfo by lfsr_traversal_read
|
// internally used flags
|
||||||
LFS_T_DIRTY = 0x0001, // Filesystem mutated
|
LFS_F_DIRTY = 0x1000, // Filesystem has been modified
|
||||||
// TODO should we have CORRUPTMETADATA vs just error?
|
|
||||||
LFS_T_CORRUPTMETADATA = 0x0002, // Found corrupted metadata
|
|
||||||
LFS_T_CORRUPTDATA = 0x0004, // Found corrupted data
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -372,9 +373,6 @@ struct lfs_fsinfo {
|
|||||||
|
|
||||||
// Traversal info structure
|
// Traversal info structure
|
||||||
struct lfs_tinfo {
|
struct lfs_tinfo {
|
||||||
// Traversal flags
|
|
||||||
uint8_t flags;
|
|
||||||
|
|
||||||
// Type of the block
|
// Type of the block
|
||||||
uint8_t btype;
|
uint8_t btype;
|
||||||
|
|
||||||
@@ -632,8 +630,7 @@ typedef struct lfsr_traversal {
|
|||||||
// lfsr_mtraversal_t contains most of what we need
|
// lfsr_mtraversal_t contains most of what we need
|
||||||
lfsr_mtraversal_t mt;
|
lfsr_mtraversal_t mt;
|
||||||
uint8_t btype;
|
uint8_t btype;
|
||||||
uint8_t count;
|
lfs_sblock_t blocks[2];
|
||||||
lfs_block_t blocks[2];
|
|
||||||
} lfsr_traversal_t;
|
} lfsr_traversal_t;
|
||||||
|
|
||||||
//typedef struct lfs_superblock {
|
//typedef struct lfs_superblock {
|
||||||
@@ -1061,8 +1058,8 @@ int lfsr_traversal_close(lfs_t *lfs, lfsr_traversal_t *traversal);
|
|||||||
//
|
//
|
||||||
// Fills out the tinfo structure.
|
// Fills out the tinfo structure.
|
||||||
//
|
//
|
||||||
// Returns 0 on success, LFS_ERR_NOENT at the end of traversal, or a
|
// Returns 0 on success, LFS_ERR_NOENT at the end of traversal, LFS_ERR_BUSY
|
||||||
// negative error code on failure.
|
// if filesystem has been modified, or a negative error code on failure.
|
||||||
int lfsr_traversal_read(lfs_t *lfs, lfsr_traversal_t *traversal,
|
int lfsr_traversal_read(lfs_t *lfs, lfsr_traversal_t *traversal,
|
||||||
struct lfs_tinfo *tinfo);
|
struct lfs_tinfo *tinfo);
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ ERRS = [
|
|||||||
('UNKNOWN', -1, "Unknown error" ),
|
('UNKNOWN', -1, "Unknown error" ),
|
||||||
('INVAL', -22, "Invalid parameter" ),
|
('INVAL', -22, "Invalid parameter" ),
|
||||||
('NOTSUP', -95, "Operation not supported" ),
|
('NOTSUP', -95, "Operation not supported" ),
|
||||||
|
('BUSY', -16, "Device or resource busy" ),
|
||||||
('IO', -5, "Error during device operation" ),
|
('IO', -5, "Error during device operation" ),
|
||||||
('CORRUPT', -84, "Corrupted" ),
|
('CORRUPT', -84, "Corrupted" ),
|
||||||
('NOENT', -2, "No directory entry" ),
|
('NOENT', -2, "No directory entry" ),
|
||||||
|
|||||||
+9
-12
@@ -27,11 +27,10 @@ code = '''
|
|||||||
lfs_alloc_ckpoint(&lfs);
|
lfs_alloc_ckpoint(&lfs);
|
||||||
lfs_size_t alloced = 0;
|
lfs_size_t alloced = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
lfs_block_t block;
|
lfs_sblock_t block = lfs_alloc(&lfs, ERASE);
|
||||||
int err = lfs_alloc(&lfs, &block, ERASE);
|
assert(block >= 0 || block == LFS_ERR_NOSPC);
|
||||||
assert(!err || err == LFS_ERR_NOSPC);
|
|
||||||
|
|
||||||
if (err == LFS_ERR_NOSPC) {
|
if (block == LFS_ERR_NOSPC) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
alloced += 1;
|
alloced += 1;
|
||||||
@@ -61,11 +60,10 @@ code = '''
|
|||||||
lfs_alloc_ckpoint(&lfs);
|
lfs_alloc_ckpoint(&lfs);
|
||||||
lfs_size_t alloced = 0;
|
lfs_size_t alloced = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
lfs_block_t block;
|
lfs_sblock_t block = lfs_alloc(&lfs, ERASE);
|
||||||
int err = lfs_alloc(&lfs, &block, ERASE);
|
assert(block >= 0 || block == LFS_ERR_NOSPC);
|
||||||
assert(!err || err == LFS_ERR_NOSPC);
|
|
||||||
|
|
||||||
if (err == LFS_ERR_NOSPC) {
|
if (block == LFS_ERR_NOSPC) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
alloced += 1;
|
alloced += 1;
|
||||||
@@ -83,11 +81,10 @@ code = '''
|
|||||||
lfs_alloc_ckpoint(&lfs);
|
lfs_alloc_ckpoint(&lfs);
|
||||||
alloced = 0;
|
alloced = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
lfs_block_t block;
|
lfs_sblock_t block = lfs_alloc(&lfs, ERASE);
|
||||||
int err = lfs_alloc(&lfs, &block, ERASE);
|
assert(block >= 0 || block == LFS_ERR_NOSPC);
|
||||||
assert(!err || err == LFS_ERR_NOSPC);
|
|
||||||
|
|
||||||
if (err == LFS_ERR_NOSPC) {
|
if (block == LFS_ERR_NOSPC) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
alloced += 1;
|
alloced += 1;
|
||||||
|
|||||||
+2101
-45
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user