Limited file->leaf to reads + erased-state caching

This reverts most of the lazy-grafting/crystallization logic, but keeps
the general crystallization algorithm rewrite and file->leaf for caching
read operations and erased-state.

Unfortunately lazy-grafting/crystallization is both a code and stack
heavy feature for a relatively specific write pattern. It doesn't even
help if we're forced to write fragments due to prog alignment.

Dropping lazy-grafting/crystallization trades off linear write/rewrite
performance for code and stack savings:

                           code          stack          ctx
  before:                 37084           2304          636
  after:                  36428 (-1.8%)   2248 (-2.4%)  636 (+0.0%)

But with file->leaf we still keep the improvements to linear read
performance!

Compared to pre-file->leaf:

                           code          stack          ctx
  before file->leaf:      36016           2296          636
  after lazy file->leaf:  37084 (+3.0%)   2304 (+0.3%)  636 (+0.0%)
  after eager file->leaf: 36428 (+1.1%)   2248 (-2.1%)  636 (+0.0%)

I'm still on the fence about this, but lazy-grafting/crystallization is
just a lot of code... And the first 6 letters of littlefs don't spell
"speedy" last time I checked...

At the very least we can always add lazy-grafting/crystallization as an
opt-in write strategy later.
This commit is contained in:
Christopher Haster
2025-05-22 18:11:04 -05:00
parent 9c3a866508
commit 22c43124de
3 changed files with 60 additions and 233 deletions
+60 -229
View File
@@ -7003,14 +7003,6 @@ static inline bool lfsr_o_isbshrub(uint32_t flags) {
|| lfsr_o_type(flags) == LFS_type_TRAVERSAL; || lfsr_o_type(flags) == LFS_type_TRAVERSAL;
} }
static inline bool lfsr_o_isuncryst(uint32_t flags) {
return flags & LFS_o_UNCRYST;
}
static inline bool lfsr_o_isungraft(uint32_t flags) {
return flags & LFS_o_UNGRAFT;
}
static inline bool lfsr_o_isunflush(uint32_t flags) { static inline bool lfsr_o_isunflush(uint32_t flags) {
return flags & LFS_o_UNFLUSH; return flags & LFS_o_UNFLUSH;
} }
@@ -9719,21 +9711,11 @@ static int lfsr_mtree_traverse_(lfs_t *lfs, lfsr_traversal_t *t,
continue; continue;
} }
// transition to traversing the file // start traversing the file
const lfsr_file_t *file = (const lfsr_file_t*)t->ot; const lfsr_file_t *file = (const lfsr_file_t*)t->ot;
t->b.shrub = file->b.shrub; t->b.shrub = file->b.shrub;
lfsr_btraversal_init(&t->u.bt); lfsr_btraversal_init(&t->u.bt);
lfsr_t_settstate(&t->b.o.flags, LFSR_TSTATE_OBTREE); lfsr_t_settstate(&t->b.o.flags, LFSR_TSTATE_OBTREE);
// wait, do we have an ungrafted leaf?
if (lfsr_o_isungraft(file->b.o.flags)) {
if (tag_) {
*tag_ = LFSR_TAG_BLOCK;
}
*bptr = file->leaf.bptr;
return 0;
}
continue; continue;
// traverse any bshrubs/btrees we see, this includes the mtree // traverse any bshrubs/btrees we see, this includes the mtree
@@ -11255,13 +11237,11 @@ int lfsr_removeattr(lfs_t *lfs, const char *path, uint8_t type) {
// file helpers // file helpers
static inline void lfsr_file_discardcache(lfsr_file_t *file) { static inline void lfsr_file_discardcache(lfsr_file_t *file) {
file->b.o.flags &= ~LFS_o_UNFLUSH;
file->cache.pos = 0; file->cache.pos = 0;
file->cache.size = 0; file->cache.size = 0;
} }
static inline void lfsr_file_discardleaf(lfsr_file_t *file) { static inline void lfsr_file_discardleaf(lfsr_file_t *file) {
file->b.o.flags &= ~LFS_o_UNCRYST & ~LFS_o_UNGRAFT;
file->leaf.pos = 0; file->leaf.pos = 0;
file->leaf.weight = 0; file->leaf.weight = 0;
lfsr_bptr_discard(&file->leaf.bptr); lfsr_bptr_discard(&file->leaf.bptr);
@@ -11278,16 +11258,10 @@ static inline lfs_size_t lfsr_file_cachesize(lfs_t *lfs,
: lfs->cfg->file_cache_size; : lfs->cfg->file_cache_size;
} }
static inline lfs_off_t lfsr_file_weight(const lfsr_file_t *file) {
return lfs_max(
file->leaf.pos + file->leaf.weight,
file->b.shrub.weight);
}
static inline lfs_off_t lfsr_file_size_(const lfsr_file_t *file) { static inline lfs_off_t lfsr_file_size_(const lfsr_file_t *file) {
return lfs_max( return lfs_max(
file->cache.pos + file->cache.size, file->cache.pos + file->cache.size,
lfsr_file_weight(file)); file->b.shrub.weight);
} }
@@ -11301,6 +11275,8 @@ static int lfsr_file_fetch(lfs_t *lfs, lfsr_file_t *file, bool trunc) {
lfsr_file_discardcache(file); lfsr_file_discardcache(file);
// discard the current leaf // discard the current leaf
lfsr_file_discardleaf(file); lfsr_file_discardleaf(file);
// mark as flushed
file->b.o.flags &= ~LFS_o_UNFLUSH;
// don't bother reading disk if we're not created or truncating // don't bother reading disk if we're not created or truncating
if (!lfsr_o_isuncreat(file->b.o.flags) && !trunc) { if (!lfsr_o_isuncreat(file->b.o.flags) && !trunc) {
@@ -11671,57 +11647,9 @@ static int lfsr_file_lookup(lfs_t *lfs, const lfsr_file_t *file,
return 0; return 0;
} }
// in between bshrub/btree and ungrafted leaf? pretend there's a
// hole here
if (bid >= file->b.shrub.weight && bid < file->leaf.pos) {
if (bid_) {
*bid_ = file->leaf.pos-1;
}
if (weight_) {
*weight_ = file->leaf.pos - file->b.shrub.weight;
}
lfsr_bptr_discard(bptr_);
return 0;
}
// lookup on disk // lookup on disk
lfsr_bid_t bid__; return lfsr_file_lookup_(lfs, file, bid,
lfsr_bid_t weight__; bid_, weight_, bptr_);
int err = lfsr_file_lookup_(lfs, file, bid,
&bid__, &weight__, bptr_);
if (err) {
return err;
}
// hits our leaf? our leaf takes priority
//
// slice left leaf?
if (bid > file->leaf.pos + file->leaf.weight
&& bid__-(weight__-1) < file->leaf.pos + file->leaf.weight) {
lfs_soff_t d = (file->leaf.pos + file->leaf.weight)
- (bid__-(weight__-1));
weight__ -= d;
bptr_->data = LFSR_DATA_SLICE(bptr_->data,
lfs_min(d, lfsr_bptr_size(bptr_)),
-1);
// slice right leaf?
} else if (bid < file->leaf.pos
&& bid__+1 > file->leaf.pos) {
lfs_soff_t d = bid__+1 - file->leaf.pos;
bid__ -= d;
weight__ -= d;
bptr_->data = LFSR_DATA_SLICE(bptr_->data,
-1,
lfsr_bptr_size(bptr_) - lfs_min(d, lfsr_bptr_size(bptr_)));
}
if (bid_) {
*bid_ = bid__;
}
if (weight_) {
*weight_ = weight__;
}
return 0;
} }
static lfs_ssize_t lfsr_file_read_(lfs_t *lfs, lfsr_file_t *file, static lfs_ssize_t lfsr_file_read_(lfs_t *lfs, lfsr_file_t *file,
@@ -11729,15 +11657,6 @@ static lfs_ssize_t lfsr_file_read_(lfs_t *lfs, lfsr_file_t *file,
// need to fetch a new leaf? // need to fetch a new leaf?
if (!(pos >= file->leaf.pos if (!(pos >= file->leaf.pos
&& pos < file->leaf.pos + file->leaf.weight)) { && pos < file->leaf.pos + file->leaf.weight)) {
// leaf in use? we need to flush it
if (lfsr_o_isungraft(file->b.o.flags)
|| lfsr_o_isuncryst(file->b.o.flags)) {
int err = lfsr_file_flush(lfs, file);
if (err) {
return err;
}
}
// fetch a leaf // fetch a leaf
lfsr_bid_t bid; lfsr_bid_t bid;
lfsr_bid_t weight; lfsr_bid_t weight;
@@ -11838,7 +11757,7 @@ lfs_ssize_t lfsr_file_read(lfs_t *lfs, lfsr_file_t *file,
} }
// any data in our btree? // any data in our btree?
if (pos_ < lfsr_file_weight(file)) { if (pos_ < file->b.shrub.weight) {
// bypass cache? // bypass cache?
if ((lfs_size_t)d >= lfsr_file_cachesize(lfs, file)) { if ((lfs_size_t)d >= lfsr_file_cachesize(lfs, file)) {
lfs_ssize_t d_ = lfsr_file_read_(lfs, file, lfs_ssize_t d_ = lfsr_file_read_(lfs, file,
@@ -12170,19 +12089,18 @@ static int lfsr_file_graft(lfs_t *lfs, lfsr_file_t *file,
return 0; return 0;
} }
// this LFS_NOINLINE is to force lfsr_file_crystallize_ off the stack
// hot-path
LFS_NOINLINE
static int lfsr_file_crystallize_(lfs_t *lfs, lfsr_file_t *file, static int lfsr_file_crystallize_(lfs_t *lfs, lfsr_file_t *file,
lfs_off_t block_pos, lfs_soff_t crystal_size, lfs_off_t block_pos, lfs_soff_t crystal_size,
lfs_off_t pos, const uint8_t *buffer, lfs_size_t size) { lfs_off_t pos, const uint8_t *buffer, lfs_size_t size) {
// align to prog_size, limit to block_size and theoretical file size // limit to block_size and theoretical file size
lfs_off_t crystal_limit = lfs_min( lfs_off_t crystal_limit = lfs_min(
block_pos + lfs_min( block_pos + lfs->cfg->block_size,
lfs_aligndown(
(lfs_off_t)crystal_size,
lfs->cfg->prog_size),
lfs->cfg->block_size),
lfs_max( lfs_max(
pos + size, pos + size,
lfsr_file_weight(file))); file->b.shrub.weight));
// do we need to allocate a new block? // do we need to allocate a new block?
if (!lfsr_bptr_isbptr(&file->leaf.bptr) if (!lfsr_bptr_isbptr(&file->leaf.bptr)
@@ -12254,7 +12172,7 @@ static int lfsr_file_crystallize_(lfs_t *lfs, lfsr_file_t *file,
} }
// any data on disk? // any data on disk?
if (pos_ < lfsr_file_weight(file)) { if (pos_ < file->b.shrub.weight) {
lfsr_bid_t bid__; lfsr_bid_t bid__;
lfsr_bid_t weight__; lfsr_bid_t weight__;
lfsr_bptr_t bptr__; lfsr_bptr_t bptr__;
@@ -12374,9 +12292,6 @@ static int lfsr_file_crystallize_(lfs_t *lfs, lfsr_file_t *file,
// mark as erased // mark as erased
(pos_ - block_pos) | LFSR_BPTR_ISERASED, (pos_ - block_pos) | LFSR_BPTR_ISERASED,
cksum_); cksum_);
// mark as uncrystallized and ungrafted
file->b.o.flags |= LFS_o_UNCRYST | LFS_o_UNGRAFT;
return 0; return 0;
relocate:; relocate:;
@@ -12397,42 +12312,23 @@ static int lfsr_file_crystallize_(lfs_t *lfs, lfsr_file_t *file,
} }
} }
static int lfsr_file_crystallize(lfs_t *lfs, lfsr_file_t *file) { static int lfsr_file_crystallize(lfs_t *lfs, lfsr_file_t *file,
// finish crystallizing lfs_off_t block_pos, lfs_soff_t crystal_size,
if (lfsr_o_isuncryst(file->b.o.flags)) { lfs_off_t pos, const uint8_t *buffer, lfs_size_t size) {
// uncrystallized files must be unsynced // this is split into two functions to minimize stack usage
LFS_ASSERT(lfsr_o_isunsync(file->b.o.flags));
// only blocks can be uncrystallized
LFS_ASSERT(lfsr_bptr_isbptr(&file->leaf.bptr));
LFS_ASSERT(lfsr_bptr_iserased(&file->leaf.bptr));
// finish crystallizing the block // crystallize
int err = lfsr_file_crystallize_(lfs, file, int err = lfsr_file_crystallize_(lfs, file,
file->leaf.pos - lfsr_bptr_off(&file->leaf.bptr), block_pos, crystal_size,
-1, pos, buffer, size);
file->cache.pos, file->cache.buffer, file->cache.size); if (err) {
if (err) { return err;
return err;
}
// mark as crystallized
file->b.o.flags &= ~LFS_o_UNCRYST;
} }
// and graft into tree // and graft it into our tree
if (lfsr_o_isungraft(file->b.o.flags)) { return lfsr_file_graft(lfs, file,
int err = lfsr_file_graft(lfs, file, file->leaf.pos, file->leaf.weight, 0,
file->leaf.pos, file->leaf.weight, 0, &file->leaf.bptr.data, -1);
&file->leaf.bptr.data, -1);
if (err) {
return err;
}
// mark as grafted
file->b.o.flags &= ~LFS_o_UNGRAFT;
}
return 0;
} }
static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file, static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
@@ -12464,7 +12360,7 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
&& pos - block_end < lfs->cfg->crystal_thresh && pos - block_end < lfs->cfg->crystal_thresh
// need to bail if we can't meet prog alignment // need to bail if we can't meet prog alignment
&& (pos + size) - block_end >= lfs->cfg->prog_size) { && (pos + size) - block_end >= lfs->cfg->prog_size) {
int err = lfsr_file_crystallize_(lfs, file, int err = lfsr_file_crystallize(lfs, file,
file->leaf.pos - lfsr_bptr_off(&file->leaf.bptr), file->leaf.pos - lfsr_bptr_off(&file->leaf.bptr),
(pos + size) (pos + size)
- (file->leaf.pos - lfsr_bptr_off(&file->leaf.bptr)), - (file->leaf.pos - lfsr_bptr_off(&file->leaf.bptr)),
@@ -12508,7 +12404,7 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
0); 0);
if (crystal_end - crystal_start < lfs->cfg->crystal_thresh if (crystal_end - crystal_start < lfs->cfg->crystal_thresh
&& crystal_start > 0 && crystal_start > 0
&& poke < lfsr_file_weight(file) && poke < file->b.shrub.weight
// don't bother looking up left after the first block // don't bother looking up left after the first block
&& !aligned) { && !aligned) {
lfsr_bid_t bid; lfsr_bid_t bid;
@@ -12541,9 +12437,9 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
// find right crystal neighbor // find right crystal neighbor
poke = lfs_min( poke = lfs_min(
crystal_start + (lfs->cfg->crystal_thresh-1), crystal_start + (lfs->cfg->crystal_thresh-1),
lfsr_file_weight(file)-1); file->b.shrub.weight-1);
if (crystal_end - crystal_start < lfs->cfg->crystal_thresh if (crystal_end - crystal_start < lfs->cfg->crystal_thresh
&& crystal_end < lfsr_file_weight(file)) { && crystal_end < file->b.shrub.weight) {
lfsr_bid_t bid; lfsr_bid_t bid;
lfsr_bid_t weight; lfsr_bid_t weight;
lfsr_bptr_t bptr; lfsr_bptr_t bptr;
@@ -12583,18 +12479,11 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
// exceeded crystallization threshold? we need to allocate a // exceeded crystallization threshold? we need to allocate a
// new block // new block
// if we're mid-crystallization, finish crystallizing the block
// and graft it into our bshrub/btree
int err = lfsr_file_crystallize(lfs, file);
if (err) {
return err;
}
// mark as unerased so lfsr_file_crystallize doesn't try to // mark as unerased so lfsr_file_crystallize doesn't try to
// resume crystallizing this block // resume crystallizing this block
lfsr_bptr_claim(&file->leaf.bptr); lfsr_bptr_claim(&file->leaf.bptr);
// before we can crystallize we need to figure out the best // before we can crystallize, we need to figure out the best
// block alignment, we use the entry immediately to the left of // block alignment, we use the entry immediately to the left of
// our crystal for this // our crystal for this
if (crystal_start > 0 if (crystal_start > 0
@@ -12631,8 +12520,8 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
// start crystallizing! // start crystallizing!
// //
// lfsr_file_crystallize_ handles block allocation/relocation // lfsr_file_crystallize handles block allocation/relocation
err = lfsr_file_crystallize_(lfs, file, int err = lfsr_file_crystallize(lfs, file,
crystal_start, crystal_end - crystal_start, crystal_start, crystal_end - crystal_start,
pos, buffer, size); pos, buffer, size);
if (err) { if (err) {
@@ -12654,24 +12543,6 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
fragment:; fragment:;
// iteratively write fragments (inlined leaves) // iteratively write fragments (inlined leaves)
while (size > 0) { while (size > 0) {
// before we write fragments, we need to make sure our crystal
// is grafted into the tree
//
// but note we're still tracking its erased state for future
// writes!
if (lfsr_o_isungraft(file->b.o.flags)) {
// graft our crystal
int err = lfsr_file_graft(lfs, file,
file->leaf.pos, file->leaf.weight, 0,
&file->leaf.bptr.data, -1);
if (err) {
return err;
}
// mark as grafted
file->b.o.flags &= ~LFS_o_UNGRAFT;
}
// do we need to discard our leaf? we need to discard fragments // do we need to discard our leaf? we need to discard fragments
// in case the underlying rbyd compacts, and we need to discard // in case the underlying rbyd compacts, and we need to discard
// overwritten blocks // overwritten blocks
@@ -12680,8 +12551,8 @@ fragment:;
// single graft may be split up into multiple commits // single graft may be split up into multiple commits
// //
// unfortunately we don't know where our fragment will end up // unfortunately we don't know where our fragment will end up
// until after the commit, so we can't track it in our leaf // until after the commit, so we can't track written fragments
// quite yet // in our leaf easily
if (!lfsr_bptr_isbptr(&file->leaf.bptr) if (!lfsr_bptr_isbptr(&file->leaf.bptr)
|| (pos < file->leaf.pos + lfsr_bptr_size(&file->leaf.bptr) || (pos < file->leaf.pos + lfsr_bptr_size(&file->leaf.bptr)
&& pos + size > file->leaf.pos)) { && pos + size > file->leaf.pos)) {
@@ -12701,7 +12572,7 @@ fragment:;
// is already full // is already full
if (fragment_end - fragment_start < lfs->cfg->fragment_size if (fragment_end - fragment_start < lfs->cfg->fragment_size
&& fragment_start > 0 && fragment_start > 0
&& fragment_start <= lfsr_file_weight(file) && fragment_start <= file->b.shrub.weight
// don't bother to lookup left after first fragment // don't bother to lookup left after first fragment
&& !aligned) { && !aligned) {
lfsr_bid_t bid; lfsr_bid_t bid;
@@ -12750,7 +12621,7 @@ fragment:;
// //
// note this may the same as our left sibling // note this may the same as our left sibling
if (fragment_end - fragment_start < lfs->cfg->fragment_size if (fragment_end - fragment_start < lfs->cfg->fragment_size
&& fragment_end < lfsr_file_weight(file)) { && fragment_end < file->b.shrub.weight) {
lfsr_bid_t bid; lfsr_bid_t bid;
lfsr_bid_t weight; lfsr_bid_t weight;
lfsr_bptr_t bptr; lfsr_bptr_t bptr;
@@ -12971,11 +12842,8 @@ int lfsr_file_flush(lfs_t *lfs, lfsr_file_t *file) {
// can't write to readonly files // can't write to readonly files
LFS_ASSERT(!lfsr_o_isrdonly(file->b.o.flags)); LFS_ASSERT(!lfsr_o_isrdonly(file->b.o.flags));
// do nothing if our file is already flushed, crystallized, // do nothing if our file is already flushed
// and grafted if (!lfsr_o_isunflush(file->b.o.flags)) {
if (!lfsr_o_isunflush(file->b.o.flags)
&& !lfsr_o_isuncryst(file->b.o.flags)
&& !lfsr_o_isungraft(file->b.o.flags)) {
return 0; return 0;
} }
// unflushed files must be unsynced // unflushed files must be unsynced
@@ -12988,23 +12856,14 @@ int lfsr_file_flush(lfs_t *lfs, lfsr_file_t *file) {
int err; int err;
// flush our cache // flush our cache
if (lfsr_o_isunflush(file->b.o.flags)) { err = lfsr_file_flush_(lfs, file,
err = lfsr_file_flush_(lfs, file, file->cache.pos, file->cache.buffer, file->cache.size);
file->cache.pos, file->cache.buffer, file->cache.size);
if (err) {
goto failed;
}
// mark as flushed
file->b.o.flags &= ~LFS_o_UNFLUSH;
}
// and crystallize/graft our leaf
err = lfsr_file_crystallize(lfs, file);
if (err) { if (err) {
goto failed; goto failed;
} }
// mark as flushed
file->b.o.flags &= ~LFS_o_UNFLUSH;
return 0; return 0;
failed:; failed:;
@@ -13184,15 +13043,10 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) {
// //
// though don't flush quite yet if our file is small and can be // though don't flush quite yet if our file is small and can be
// combined with sync in a single commit // combined with sync in a single commit
if (file->cache.size == lfsr_file_size_(file) if (!(file->cache.size == lfsr_file_size_(file)
&& file->cache.size <= lfs->cfg->inline_size && file->cache.size <= lfs->cfg->inline_size
&& file->cache.size <= lfs->cfg->fragment_size && file->cache.size <= lfs->cfg->fragment_size
&& file->cache.size < lfs->cfg->crystal_thresh) { && file->cache.size < lfs->cfg->crystal_thresh)) {
if (lfsr_o_isungraft(file->b.o.flags)) {
file->b.o.flags |= LFS_o_UNFLUSH;
}
lfsr_file_discardleaf(file);
} else {
err = lfsr_file_flush(lfs, file); err = lfsr_file_flush(lfs, file);
if (err) { if (err) {
goto failed; goto failed;
@@ -13226,10 +13080,7 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) {
// update synced files // update synced files
} else { } else {
// update flags // update flags
file_->b.o.flags &= ~LFS_o_UNSYNC file_->b.o.flags &= ~LFS_o_UNSYNC & ~LFS_o_UNFLUSH;
& ~LFS_o_UNFLUSH
& ~LFS_o_UNCRYST
& ~LFS_o_UNGRAFT;
// update shrubs // update shrubs
file_->b.shrub = file->b.shrub; file_->b.shrub = file->b.shrub;
// update leaves // update leaves
@@ -13287,8 +13138,6 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) {
// mark as synced // mark as synced
file->b.o.flags &= ~LFS_o_UNSYNC file->b.o.flags &= ~LFS_o_UNSYNC
& ~LFS_o_UNFLUSH & ~LFS_o_UNFLUSH
& ~LFS_o_UNCRYST
& ~LFS_o_UNGRAFT
& ~LFS_o_UNCREAT & ~LFS_o_UNCREAT
& ~LFS_O_DESYNC; & ~LFS_O_DESYNC;
return 0; return 0;
@@ -13412,21 +13261,18 @@ int lfsr_file_truncate(lfs_t *lfs, lfsr_file_t *file, lfs_off_t size_) {
// mark as unsynced in case we fail // mark as unsynced in case we fail
file->b.o.flags |= LFS_o_UNSYNC; file->b.o.flags |= LFS_o_UNSYNC;
// if our leaf is a fragment or will be fragmented, we need // if our leaf is a fragment or will be fragmented, we need to go
// to go ahead and graft and discard it // ahead and discard it, otherwise we risk out-of-date fragments as
// btree commits move things around
// //
// otherwise we risk out-of-date fragments as btree commits move // note that fruncate is commonly used when logging, where we
// things around // _really_ don't want to discard erased-state, otherwise we'd just
// discard this unconditionally
if (!lfsr_bptr_isbptr(&file->leaf.bptr) if (!lfsr_bptr_isbptr(&file->leaf.bptr)
|| size_ - lfs_min(file->leaf.pos, size_) || size_ - lfs_min(file->leaf.pos, size_)
< lfs_min( < lfs_min(
lfs->cfg->fragment_thresh, lfs->cfg->fragment_thresh,
lfs->cfg->crystal_thresh)) { lfs->cfg->crystal_thresh)) {
err = lfsr_file_crystallize(lfs, file);
if (err) {
goto failed;
}
lfsr_file_discardleaf(file); lfsr_file_discardleaf(file);
} }
@@ -13442,7 +13288,6 @@ int lfsr_file_truncate(lfs_t *lfs, lfsr_file_t *file, lfs_off_t size_) {
// truncate our leaf // truncate our leaf
if (size_ < file->leaf.pos + lfsr_bptr_size(&file->leaf.bptr)) { if (size_ < file->leaf.pos + lfsr_bptr_size(&file->leaf.bptr)) {
lfsr_bptr_claim(&file->leaf.bptr); lfsr_bptr_claim(&file->leaf.bptr);
file->b.o.flags &= ~LFS_o_UNCRYST;
} }
file->leaf.bptr.data = LFSR_DATA_TRUNCATE( file->leaf.bptr.data = LFSR_DATA_TRUNCATE(
file->leaf.bptr.data, file->leaf.bptr.data,
@@ -13491,11 +13336,13 @@ int lfsr_file_fruncate(lfs_t *lfs, lfsr_file_t *file, lfs_off_t size_) {
// mark as unsynced in case we fail // mark as unsynced in case we fail
file->b.o.flags |= LFS_o_UNSYNC; file->b.o.flags |= LFS_o_UNSYNC;
// if our leaf is a fragment or will be fragmented, we need // if our leaf is a fragment or will be fragmented, we need to go
// to go ahead and graft and discard it // ahead and discard it, otherwise we risk out-of-date fragments as
// btree commits move things around
// //
// otherwise we risk out-of-date fragments as btree commits move // note that fruncate is commonly used when logging, where we
// things around // _really_ don't want to discard erased-state, otherwise we'd just
// discard this unconditionally
if (!lfsr_bptr_isbptr(&file->leaf.bptr) if (!lfsr_bptr_isbptr(&file->leaf.bptr)
|| lfsr_bptr_size(&file->leaf.bptr) - lfs_min( || lfsr_bptr_size(&file->leaf.bptr) - lfs_min(
lfs_smax( lfs_smax(
@@ -13505,11 +13352,6 @@ int lfsr_file_fruncate(lfs_t *lfs, lfsr_file_t *file, lfs_off_t size_) {
< lfs_min( < lfs_min(
lfs->cfg->fragment_thresh, lfs->cfg->fragment_thresh,
lfs->cfg->crystal_thresh)) { lfs->cfg->crystal_thresh)) {
err = lfsr_file_crystallize(lfs, file);
if (err) {
goto failed;
}
lfsr_file_discardleaf(file); lfsr_file_discardleaf(file);
} }
@@ -13527,7 +13369,6 @@ int lfsr_file_fruncate(lfs_t *lfs, lfsr_file_t *file, lfs_off_t size_) {
> (lfs_soff_t)(file->leaf.pos > (lfs_soff_t)(file->leaf.pos
+ lfsr_bptr_size(&file->leaf.bptr))) { + lfsr_bptr_size(&file->leaf.bptr))) {
lfsr_bptr_claim(&file->leaf.bptr); lfsr_bptr_claim(&file->leaf.bptr);
file->b.o.flags &= ~LFS_o_UNCRYST;
} }
file->leaf.bptr.data = LFSR_DATA_FRUNCATE( file->leaf.bptr.data = LFSR_DATA_FRUNCATE(
file->leaf.bptr.data, file->leaf.bptr.data,
@@ -13619,16 +13460,6 @@ static int lfsr_file_traverse(lfs_t *lfs, const lfsr_file_t *file,
static int lfsr_file_ck(lfs_t *lfs, const lfsr_file_t *file, static int lfsr_file_ck(lfs_t *lfs, const lfsr_file_t *file,
uint32_t flags) { uint32_t flags) {
// validate ungrafted data block?
if (lfsr_t_isckdata(flags)
&& lfsr_o_isungraft(file->b.o.flags)) {
LFS_ASSERT(lfsr_bptr_isbptr(&file->leaf.bptr));
int err = lfsr_bptr_ck(lfs, &file->leaf.bptr);
if (err) {
return err;
}
}
// traverse the file's bshrub/btree // traverse the file's bshrub/btree
lfsr_btraversal_t bt; lfsr_btraversal_t bt;
lfsr_btraversal_init(&bt); lfsr_btraversal_init(&bt);
-2
View File
@@ -144,8 +144,6 @@ enum lfs_type {
// internally used flags, don't use these // internally used flags, don't use these
#define LFS_o_TYPE 0xf0000000 // The file's type #define LFS_o_TYPE 0xf0000000 // The file's type
#define LFS_o_UNCRYST 0x00400000 // File's leaf not fully crystallized
#define LFS_o_UNGRAFT 0x00800000 // File's leaf does not match bshrub/btree
#define LFS_o_UNFLUSH 0x01000000 // File's data does not match disk #define LFS_o_UNFLUSH 0x01000000 // File's data does not match disk
#define LFS_o_UNSYNC 0x02000000 // File's metadata does not match disk #define LFS_o_UNSYNC 0x02000000 // File's metadata does not match disk
#define LFS_o_UNCREAT 0x04000000 // File does not exist yet #define LFS_o_UNCREAT 0x04000000 // File does not exist yet
-2
View File
@@ -44,8 +44,6 @@ FLAGS = [
('^', 'ORPHAN', 0x50000000, "Type = orphan" ), ('^', 'ORPHAN', 0x50000000, "Type = orphan" ),
('^', 'TRAVERSAL', 0x60000000, "Type = traversal" ), ('^', 'TRAVERSAL', 0x60000000, "Type = traversal" ),
('^', 'UNKNOWN', 0x70000000, "Type = unknown" ), ('^', 'UNKNOWN', 0x70000000, "Type = unknown" ),
('o', 'UNCRYST', 0x00400000, "File's leaf not fully crystallized" ),
('o', 'UNGRAFT', 0x00800000, "File's leaf does not match bshrub/btree" ),
('o', 'UNFLUSH', 0x01000000, "File's data does not match disk" ), ('o', 'UNFLUSH', 0x01000000, "File's data does not match disk" ),
('o', 'UNSYNC', 0x02000000, "File's metadata does not match disk" ), ('o', 'UNSYNC', 0x02000000, "File's metadata does not match disk" ),
('o', 'UNCREAT', 0x04000000, "File does not exist yet" ), ('o', 'UNCREAT', 0x04000000, "File does not exist yet" ),