Limited block merging to only merge when fits

This is similar to fragment coalescing, except we may need to merge
multiple fragments/blocks, so we check for merges in a loop (need to
determine if this is the best strategy).

This prevents runaway block allocations when writing to a file
backwards. I'm not really sure why you would write to a file backwards,
but this also has an impact on random writes.
This commit is contained in:
Christopher Haster
2023-11-12 13:17:51 -06:00
parent b0e1d49efe
commit f8e0ff0234
+49 -35
View File
@@ -9523,7 +9523,8 @@ static int lfsr_file_flushbuffer(lfs_t *lfs, lfsr_file_t *file) {
// can we coalesce?
if (pos+lfsr_data_size(&data)
< rid_-(weight_-1) + lfsr_data_size(&data_)
&& lfsr_data_size(&data) + lfsr_data_size(&data_)
&& lfsr_data_size(&data)
+ lfsr_data_size(&data_)
- (pos+lfsr_data_size(&data) - (rid_-(weight_-1)))
<= lfs->cfg->fragment_size) {
datas[data_count++] = lfsr_data_fruncate(data_,
@@ -9974,20 +9975,15 @@ static int lfsr_file_flushshrub(lfs_t *lfs, lfsr_file_t *file) {
// before we can compact we need to figure out the best block
// alignment, we use the entry immediately to the left of our
// crystal for this
lfs_off_t block_off;
// some corner cases where we align arbitrarily
if (crystal_start == 0 || lfsr_tree_size(&file->tree) == 0) {
block_off = crystal_start;
// find left block neighbor
} else {
lfs_off_t block_start = crystal_start;
if (block_start > 0 && lfsr_tree_size(&file->tree) > 0) {
lfsr_bid_t bid_;
lfsr_tag_t tag_;
lfsr_bid_t weight_;
lfsr_data_t data_;
int err = lfsr_tree_lookupnext(lfs, &file->tree,
lfs_min32(
crystal_start-1,
block_start-1,
lfsr_tree_size(&file->tree)-1),
&bid_, &tag_, &weight_, &data_);
if (err) {
@@ -9998,23 +9994,51 @@ static int lfsr_file_flushshrub(lfs_t *lfs, lfsr_file_t *file) {
|| tag_ == LFSR_TAG_BLOCK);
// is our left neighbor in the same block?
if (crystal_start - (bid_-(weight_-1)) < lfs->cfg->block_size
if (block_start - (bid_-(weight_-1)) < lfs->cfg->block_size
&& lfsr_data_size(&data_) > 0) {
block_off = bid_-(weight_-1);
block_start = bid_-(weight_-1);
// no? is our left neighbor at least our left block neighbor?
// align to block alignment
} else if (crystal_start - (bid_-(weight_-1))
} else if (block_start - (bid_-(weight_-1))
< 2*lfs->cfg->block_size
&& lfsr_data_size(&data_) > 0) {
block_off = bid_-(weight_-1) + lfs->cfg->block_size;
// no!? file is sparse, align arbitrarily
} else {
block_off = crystal_start;
block_start = bid_-(weight_-1) + lfs->cfg->block_size;
}
}
// if we have space in our block, lookup right block neighbors
// to see if we can merge
lfs_off_t block_end = lfs_min32(
crystal_end,
block_start + lfs->cfg->block_size);
while (block_end - block_start < lfs->cfg->block_size
&& block_end < lfsr_tree_size(&file->tree)) {
lfsr_bid_t bid_;
lfsr_tag_t tag_;
lfsr_bid_t weight_;
lfsr_data_t data_;
int err = lfsr_tree_lookupnext(lfs, &file->tree,
block_end,
&bid_, &tag_, &weight_, &data_);
if (err) {
LFS_ASSERT(err != LFS_ERR_NOENT);
return err;
}
LFS_ASSERT(tag_ == LFSR_TAG_DATA
|| tag_ == LFSR_TAG_BLOCK);
// can we merge?
if (bid_-(weight_-1)+lfsr_data_size(&data_) <= block_end
|| bid_-(weight_-1)+lfsr_data_size(&data_)
- block_start
> lfs->cfg->block_size) {
break;
}
block_end = bid_-(weight_-1)+lfsr_data_size(&data_);
}
// allocate a new block
lfs_block_t block;
int err = lfs_alloc(lfs, &block);
@@ -10029,11 +10053,10 @@ static int lfsr_file_flushshrub(lfs_t *lfs, lfsr_file_t *file) {
}
// copy any data underneath our block into our block
lfs_off_t pos_ = block_off;
while (pos_ < block_off + lfs->cfg->block_size) {
lfs_off_t pos_ = block_start;
while (pos_ < block_end) {
lfsr_data_t data;
err = lfsr_file_readnext(lfs, file, pos_,
block_off + lfs->cfg->block_size - pos_,
err = lfsr_file_readnext(lfs, file, pos_, block_end - pos_,
&data);
if (err) {
// end of file?
@@ -10044,16 +10067,8 @@ static int lfsr_file_flushshrub(lfs_t *lfs, lfsr_file_t *file) {
}
LFS_ASSERT(lfsr_data_size(&data) > 0);
// found a hole that goes all the way to the end? terminate
// early
if (lfsr_data_ishole(&data)
&& pos_ + lfsr_data_size(&data)
>= block_off + lfs->cfg->block_size) {
break;
}
// prog data/hole
err = lfsr_bd_progdata(lfs, block, pos_ - block_off,
err = lfsr_bd_progdata(lfs, block, pos_ - block_start,
data,
NULL);
if (err) {
@@ -10062,7 +10077,6 @@ static int lfsr_file_flushshrub(lfs_t *lfs, lfsr_file_t *file) {
pos_ += lfsr_data_size(&data);
}
lfs_off_t block_size = pos_ - block_off;
// TODO validate?
// finalize our write
@@ -10075,13 +10089,13 @@ static int lfsr_file_flushshrub(lfs_t *lfs, lfsr_file_t *file) {
lfsr_bptr_t bptr = {
.block = block,
.off = 0,
.size = block_size,
.size = block_end - block_start,
};
// and write it into our tree
uint8_t bptr_buf[LFSR_BPTR_DSIZE];
err = lfsr_tree_carve(lfs, &file->tree,
block_off, block_size, 0,
block_start, block_end - block_start, 0,
LFSR_TAG_BLOCK, lfsr_data_frombptr(&bptr, bptr_buf));
if (err) {
return err;
@@ -10089,7 +10103,7 @@ static int lfsr_file_flushshrub(lfs_t *lfs, lfsr_file_t *file) {
// note due to block alignment we may not actually make progress
// here until a second pass
pos = lfs_max32(pos, block_off + block_size);
pos = lfs_max32(pos, block_end);
// fits in crystallization threshold? just append a fragment
} else {
@@ -10168,7 +10182,7 @@ static int lfsr_file_flushshrub(lfs_t *lfs, lfsr_file_t *file) {
if (pos+lfsr_data_size(&data)
< bid_-(weight_-1) + lfsr_data_size(&data_)
&& lfsr_data_size(&data)
+ lfsr_data_size(&data_)
+ lfsr_data_size(&data_)
- (pos+lfsr_data_size(&data)
- (bid_-(weight_-1)))
<= lfs->cfg->fragment_size) {