Reworked lfs3_file_truncate/fruncate to simplify crystallize
Now that we don't need to worry about losing data due to ungrafted
state, we can decide whether or not to discard leaves after
truncate/fruncate.
This simplifies lfs3_file_truncate/fruncate (and makes them much more
readable as a plus), but also lets us simplify lfs3_file_crystallize
since we no longer need to worry about implicit flushes.
lfs3_file_crystallize's call sites:
- lfs3_file_flush_ - We've already committed to flushing, so
opportunistically clearing LFS3_o_UNFLUSH has no effect.
lfs3_file_flush_'s logic should already take advantage of possible
flushes anyways.
- lfs3_file_flush - We only call lfs3_file_crystallize _after_
lfs3_file_flush_, so this has no effect.
This saves a bit more code and stack:
code stack ctx
before: 37588 2472 656
after: 37468 (-0.3%) 2464 (-0.3%) 656 (+0.0%)
This commit is contained in:
@@ -11969,9 +11969,6 @@ static int lfs3_file_lookupnext(lfs3_t *lfs3, const lfs3_file_t *file,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// needed in lfs3_file_readnext
|
|
||||||
static int lfs3_file_crystallize(lfs3_t *lfs3, lfs3_file_t *file);
|
|
||||||
|
|
||||||
#ifndef LFS3_KVONLY
|
#ifndef LFS3_KVONLY
|
||||||
static lfs3_ssize_t lfs3_file_readnext(lfs3_t *lfs3, lfs3_file_t *file,
|
static lfs3_ssize_t lfs3_file_readnext(lfs3_t *lfs3, lfs3_file_t *file,
|
||||||
lfs3_off_t pos, uint8_t *buffer, lfs3_size_t size) {
|
lfs3_off_t pos, uint8_t *buffer, lfs3_size_t size) {
|
||||||
@@ -12530,8 +12527,11 @@ static int lfs3_file_crystallize__(lfs3_t *lfs3, lfs3_file_t *file,
|
|||||||
goto relocate;
|
goto relocate;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we're resuming crystallization, block pointer shouldn't be
|
// only blocks can be uncrystallized
|
||||||
// truncated or anything
|
LFS3_ASSERT(lfs3_bptr_isbptr(&file->leaf.bptr));
|
||||||
|
LFS3_ASSERT(lfs3_bptr_iserased(&file->leaf.bptr));
|
||||||
|
|
||||||
|
// uncrystallized blocks shouldn't be truncated or anything
|
||||||
LFS3_ASSERT(file->leaf.pos - lfs3_bptr_off(&file->leaf.bptr)
|
LFS3_ASSERT(file->leaf.pos - lfs3_bptr_off(&file->leaf.bptr)
|
||||||
== block_pos);
|
== block_pos);
|
||||||
LFS3_ASSERT(lfs3_bptr_off(&file->leaf.bptr)
|
LFS3_ASSERT(lfs3_bptr_off(&file->leaf.bptr)
|
||||||
@@ -12792,41 +12792,26 @@ failed:;
|
|||||||
|
|
||||||
#if !defined(LFS3_RDONLY) && !defined(LFS3_KVONLY) && !defined(LFS3_2BONLY)
|
#if !defined(LFS3_RDONLY) && !defined(LFS3_KVONLY) && !defined(LFS3_2BONLY)
|
||||||
static int lfs3_file_crystallize(lfs3_t *lfs3, lfs3_file_t *file) {
|
static int lfs3_file_crystallize(lfs3_t *lfs3, lfs3_file_t *file) {
|
||||||
// TODO do we care about this possibility?
|
// do nothing if our file is already crystallized
|
||||||
// is it possible for this to flush the cache?
|
if (!lfs3_o_isuncryst(file->b.o.flags)) {
|
||||||
bool flushing = (
|
return 0;
|
||||||
file->cache.pos
|
}
|
||||||
>= file->leaf.pos + lfs3_bptr_size(&file->leaf.bptr));
|
|
||||||
|
|
||||||
|
// uncrystallized files must be unsynced
|
||||||
|
LFS3_ASSERT(lfs3_o_isunsync(file->b.o.flags));
|
||||||
|
|
||||||
|
// checkpoint the allocator
|
||||||
|
lfs3_alloc_ckpoint(lfs3);
|
||||||
// finish crystallizing
|
// finish crystallizing
|
||||||
if (lfs3_o_isuncryst(file->b.o.flags)) {
|
int err = lfs3_file_crystallize_(lfs3, file,
|
||||||
// uncrystallized files must be unsynced
|
file->leaf.pos - lfs3_bptr_off(&file->leaf.bptr), -1, -1,
|
||||||
LFS3_ASSERT(lfs3_o_isunsync(file->b.o.flags));
|
0, NULL, 0);
|
||||||
// only blocks can be uncrystallized
|
if (err) {
|
||||||
LFS3_ASSERT(lfs3_bptr_isbptr(&file->leaf.bptr));
|
return err;
|
||||||
LFS3_ASSERT(lfs3_bptr_iserased(&file->leaf.bptr));
|
|
||||||
|
|
||||||
// checkpoint the allocator
|
|
||||||
lfs3_alloc_ckpoint(lfs3);
|
|
||||||
// finish crystallizing
|
|
||||||
int err = lfs3_file_crystallize_(lfs3, file,
|
|
||||||
file->leaf.pos - lfs3_bptr_off(&file->leaf.bptr), -1, -1,
|
|
||||||
file->cache.pos, file->cache.buffer, file->cache.size);
|
|
||||||
if (err) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we should have crystallized
|
|
||||||
LFS3_ASSERT(!lfs3_o_isuncryst(file->b.o.flags));
|
|
||||||
}
|
|
||||||
|
|
||||||
// eagerly mark as flushed if this included all of our cache
|
|
||||||
if (flushing
|
|
||||||
&& file->leaf.pos + lfs3_bptr_size(&file->leaf.bptr)
|
|
||||||
>= file->cache.pos + file->cache.size) {
|
|
||||||
file->b.o.flags &= ~LFS3_o_UNFLUSH;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we should have crystallized
|
||||||
|
LFS3_ASSERT(!lfs3_o_isuncryst(file->b.o.flags));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -13974,27 +13959,6 @@ int lfs3_file_truncate(lfs3_t *lfs3, lfs3_file_t *file, lfs3_off_t size_) {
|
|||||||
// mark as unsynced in case we fail
|
// mark as unsynced in case we fail
|
||||||
file->b.o.flags |= LFS3_o_UNSYNC;
|
file->b.o.flags |= LFS3_o_UNSYNC;
|
||||||
|
|
||||||
// if our leaf is a fragment or will be fragmented, we need
|
|
||||||
// to go ahead and crystallize + discard it, otherwise we risk
|
|
||||||
// out-of-date fragments as btree commits move things around
|
|
||||||
//
|
|
||||||
// note this is mostly to match the behavior of fruncate, where we
|
|
||||||
// _really_ don't want to discard erased-state
|
|
||||||
if (!lfs3_bptr_isbptr(&file->leaf.bptr)
|
|
||||||
|| size_ - lfs3_min(file->leaf.pos, size_)
|
|
||||||
< lfs3_min(
|
|
||||||
lfs3->cfg->fragment_thresh,
|
|
||||||
lfs3->cfg->crystal_thresh)) {
|
|
||||||
#ifndef LFS3_2BONLY
|
|
||||||
err = lfs3_file_crystallize(lfs3, file);
|
|
||||||
if (err) {
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
lfs3_file_discardleaf(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkpoint the allocator
|
// checkpoint the allocator
|
||||||
lfs3_alloc_ckpoint(lfs3);
|
lfs3_alloc_ckpoint(lfs3);
|
||||||
// truncate our btree
|
// truncate our btree
|
||||||
@@ -14007,12 +13971,9 @@ int lfs3_file_truncate(lfs3_t *lfs3, lfs3_file_t *file, lfs3_off_t size_) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// truncate our leaf
|
// truncate our leaf
|
||||||
#ifndef LFS3_2BONLY
|
//
|
||||||
if (size_ < file->leaf.pos + lfs3_bptr_size(&file->leaf.bptr)) {
|
// note we don't unconditionally discard to match fruncate, where we
|
||||||
lfs3_bptr_claim(&file->leaf.bptr);
|
// _really_ don't want to discard erased-state
|
||||||
file->b.o.flags &= ~LFS3_o_UNCRYST;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
file->leaf.bptr.data = LFS3_DATA_TRUNCATE(
|
file->leaf.bptr.data = LFS3_DATA_TRUNCATE(
|
||||||
file->leaf.bptr.data,
|
file->leaf.bptr.data,
|
||||||
size_ - lfs3_min(file->leaf.pos, size_));
|
size_ - lfs3_min(file->leaf.pos, size_));
|
||||||
@@ -14020,6 +13981,25 @@ int lfs3_file_truncate(lfs3_t *lfs3, lfs3_file_t *file, lfs3_off_t size_) {
|
|||||||
file->leaf.weight,
|
file->leaf.weight,
|
||||||
size_ - lfs3_min(file->leaf.pos, size_));
|
size_ - lfs3_min(file->leaf.pos, size_));
|
||||||
file->leaf.pos = lfs3_min(file->leaf.pos, size_);
|
file->leaf.pos = lfs3_min(file->leaf.pos, size_);
|
||||||
|
#ifndef LFS3_2BONLY
|
||||||
|
// mark as crystallized if this truncates our erased-state
|
||||||
|
if (lfs3_bptr_off(&file->leaf.bptr)
|
||||||
|
+ lfs3_bptr_size(&file->leaf.bptr)
|
||||||
|
< lfs3_bptr_cksize(&file->leaf.bptr)) {
|
||||||
|
lfs3_bptr_claim(&file->leaf.bptr);
|
||||||
|
file->b.o.flags &= ~LFS3_o_UNCRYST;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// discard if our leaf is a fragment, is fragmented, or is completed
|
||||||
|
// truncated, we can't rely on any in-bshrub/btree state
|
||||||
|
if (!lfs3_bptr_isbptr(&file->leaf.bptr)
|
||||||
|
|| lfs3_bptr_size(&file->leaf.bptr)
|
||||||
|
< lfs3_min(
|
||||||
|
lfs3->cfg->fragment_thresh,
|
||||||
|
lfs3->cfg->crystal_thresh)
|
||||||
|
|| lfs3_bptr_size(&file->leaf.bptr) == 0) {
|
||||||
|
lfs3_file_discardleaf(file);
|
||||||
|
}
|
||||||
|
|
||||||
// truncate our cache
|
// truncate our cache
|
||||||
file->cache.size = lfs3_min(
|
file->cache.size = lfs3_min(
|
||||||
@@ -14060,32 +14040,6 @@ int lfs3_file_fruncate(lfs3_t *lfs3, lfs3_file_t *file, lfs3_off_t size_) {
|
|||||||
// mark as unsynced in case we fail
|
// mark as unsynced in case we fail
|
||||||
file->b.o.flags |= LFS3_o_UNSYNC;
|
file->b.o.flags |= LFS3_o_UNSYNC;
|
||||||
|
|
||||||
// if our leaf is a fragment or will be fragmented, we need
|
|
||||||
// to go ahead and crystallize + discard it, otherwise we risk
|
|
||||||
// out-of-date fragments as btree commits move things around
|
|
||||||
//
|
|
||||||
// note that fruncate is commonly used when logging, where we
|
|
||||||
// _really_ don't want to discard erased-state, otherwise we'd just
|
|
||||||
// discard this unconditionally
|
|
||||||
if (!lfs3_bptr_isbptr(&file->leaf.bptr)
|
|
||||||
|| lfs3_bptr_size(&file->leaf.bptr) - lfs3_min(
|
|
||||||
lfs3_smax(
|
|
||||||
size - size_ - file->leaf.pos,
|
|
||||||
0),
|
|
||||||
lfs3_bptr_size(&file->leaf.bptr))
|
|
||||||
< lfs3_min(
|
|
||||||
lfs3->cfg->fragment_thresh,
|
|
||||||
lfs3->cfg->crystal_thresh)) {
|
|
||||||
#ifndef LFS3_2BONLY
|
|
||||||
err = lfs3_file_crystallize(lfs3, file);
|
|
||||||
if (err) {
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
lfs3_file_discardleaf(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkpoint the allocator
|
// checkpoint the allocator
|
||||||
lfs3_alloc_ckpoint(lfs3);
|
lfs3_alloc_ckpoint(lfs3);
|
||||||
// fruncate our btree
|
// fruncate our btree
|
||||||
@@ -14098,14 +14052,10 @@ int lfs3_file_fruncate(lfs3_t *lfs3, lfs3_file_t *file, lfs3_off_t size_) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// fruncate our leaf
|
// fruncate our leaf
|
||||||
#ifndef LFS3_2BONLY
|
//
|
||||||
if ((lfs3_soff_t)(size - size_)
|
// note we _really_ don't want to discard erased-state if possible,
|
||||||
> (lfs3_soff_t)(file->leaf.pos
|
// as fruncate is intended for logging operations, otherwise we'd
|
||||||
+ lfs3_bptr_size(&file->leaf.bptr))) {
|
// just unconditionally discard the leaf and avoid this hassle
|
||||||
lfs3_bptr_claim(&file->leaf.bptr);
|
|
||||||
file->b.o.flags &= ~LFS3_o_UNCRYST;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
file->leaf.bptr.data = LFS3_DATA_FRUNCATE(
|
file->leaf.bptr.data = LFS3_DATA_FRUNCATE(
|
||||||
file->leaf.bptr.data,
|
file->leaf.bptr.data,
|
||||||
lfs3_bptr_size(&file->leaf.bptr) - lfs3_min(
|
lfs3_bptr_size(&file->leaf.bptr) - lfs3_min(
|
||||||
@@ -14121,6 +14071,16 @@ int lfs3_file_fruncate(lfs3_t *lfs3, lfs3_file_t *file, lfs3_off_t size_) {
|
|||||||
file->leaf.pos -= lfs3_smin(
|
file->leaf.pos -= lfs3_smin(
|
||||||
size - size_,
|
size - size_,
|
||||||
file->leaf.pos);
|
file->leaf.pos);
|
||||||
|
// discard if our leaf is a fragment, is fragmented, or is completed
|
||||||
|
// truncated, we can't rely on any in-bshrub/btree state
|
||||||
|
if (!lfs3_bptr_isbptr(&file->leaf.bptr)
|
||||||
|
|| lfs3_bptr_size(&file->leaf.bptr)
|
||||||
|
< lfs3_min(
|
||||||
|
lfs3->cfg->fragment_thresh,
|
||||||
|
lfs3->cfg->crystal_thresh)
|
||||||
|
|| lfs3_bptr_size(&file->leaf.bptr) == 0) {
|
||||||
|
lfs3_file_discardleaf(file);
|
||||||
|
}
|
||||||
|
|
||||||
// fruncate our cache
|
// fruncate our cache
|
||||||
lfs3_memmove(file->cache.buffer,
|
lfs3_memmove(file->cache.buffer,
|
||||||
|
|||||||
Reference in New Issue
Block a user