Skip to content

Commit

Permalink
Added ability to revert to inline file in lfs_file_truncate
Browse files Browse the repository at this point in the history
Before, once converted to a CTZ skip-list, a file would remain a CTZ
skip-list even if truncated back to a size that could be inlined.

This was just a shortcut in implementation. And since the fix for boundary
truncates needed special handling for size==0, it made sense to extend
this special condition to allow reverting to inline files.

---

The only case I can think of, where reverting to an inline file would be
detrimental, is if it's a readonly file that you would otherwise not need
to pay the metadata overhead for. But as a tradeoff, inlining the file
would free up the block it was on, so it's unclear if this really is
a net loss.

If the truncate is followed by a write, reverting to an inline file will
always be beneficial. We assume writes will change the data, so in the
non-inlined case there's no way to avoid copying the underlying block.
Even if we assume padding issues are solved.
  • Loading branch information
geky committed Apr 17, 2023
1 parent 6dc18c3 commit e57402c
Showing 1 changed file with 47 additions and 18 deletions.
65 changes: 47 additions & 18 deletions lfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -3526,26 +3526,55 @@ static int lfs_file_rawtruncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
lfs_off_t pos = file->pos;
lfs_off_t oldsize = lfs_file_rawsize(lfs, file);
if (size < oldsize) {
// need to flush since directly changing metadata
int err = lfs_file_flush(lfs, file);
if (err) {
return err;
}
// revert to inline file?
if (size <= lfs_min(0x3fe, lfs_min(
lfs->cfg->cache_size,
(lfs->cfg->metadata_max ?
lfs->cfg->metadata_max : lfs->cfg->block_size) / 8))) {
// flush+seek to head
lfs_soff_t res = lfs_file_rawseek(lfs, file, 0, LFS_SEEK_SET);
if (res < 0) {
return (int)res;
}

// lookup new head in ctz skip list
err = lfs_ctz_find(lfs, NULL, &file->cache,
file->ctz.head, file->ctz.size,
size-lfs_min(1, size), &file->block, &(lfs_off_t){0});
if (err) {
return err;
}
// read our data into rcache temporarily
lfs_cache_drop(lfs, &lfs->rcache);
res = lfs_file_flushedread(lfs, file,
lfs->rcache.buffer, size);
if (res < 0) {
return (int)res;
}

// need to set pos/block/off consistently so seeking back to
// the old position does not get confused
file->pos = size;
file->ctz.head = file->block;
file->ctz.size = size;
file->flags |= LFS_F_DIRTY | LFS_F_READING;
file->ctz.head = LFS_BLOCK_INLINE;
file->ctz.size = size;
file->flags |= LFS_F_DIRTY | LFS_F_READING | LFS_F_INLINE;
file->cache.block = file->ctz.head;
file->cache.off = 0;
file->cache.size = lfs->cfg->cache_size;
memcpy(file->cache.buffer, lfs->rcache.buffer, size);

} else {
// need to flush since directly changing metadata
int err = lfs_file_flush(lfs, file);
if (err) {
return err;
}

// lookup new head in ctz skip list
err = lfs_ctz_find(lfs, NULL, &file->cache,
file->ctz.head, file->ctz.size,
size-1, &file->block, &(lfs_off_t){0});
if (err) {
return err;
}

// need to set pos/block/off consistently so seeking back to
// the old position does not get confused
file->pos = size;
file->ctz.head = file->block;
file->ctz.size = size;
file->flags |= LFS_F_DIRTY | LFS_F_READING;
}
} else if (size > oldsize) {
// flush+seek if not already at end
lfs_soff_t res = lfs_file_rawseek(lfs, file, 0, LFS_SEEK_END);
Expand Down

0 comments on commit e57402c

Please sign in to comment.