Added lfsr_file_fetch to deduplicate file struct fetching

This saves most of the cost of adding lfsr_file_resync in the first
place:

                  code          stack
  before resync: 36412           2616
  before fetch:  36748 (+0.9%)   2616 (+0.0%)
  after fetch:   36552 (+0.4%)   2616 (+0.0%)
This commit is contained in:
Christopher Haster
2024-08-20 20:25:51 -05:00
parent ed96e304de
commit 1a4795ec72
+98 -140
View File
@@ -11086,9 +11086,94 @@ static inline lfs_off_t lfsr_file_size_(const lfsr_file_t *file) {
// file operations
// needed in lfsr_file_opencfg
// needed in lfsr_file_fetch
static lfs_ssize_t lfsr_file_read_(lfs_t *lfs, const lfsr_file_t *file,
lfs_off_t pos, uint8_t *buffer, lfs_size_t size);
static int lfsr_file_fetch(lfs_t *lfs, lfsr_file_t *file) {
// default data state
file->o.bshrub_ = LFSR_BSHRUB_BNULL();
// don't bother reading disk if we're an orphan
if (!lfsr_o_isorphan(file->o.o.flags)) {
// lookup the file struct, if there is one
lfsr_tag_t tag;
lfsr_data_t data;
int err = lfsr_mdir_lookupnext(lfs, &file->o.o.mdir, LFSR_TAG_DATA,
&tag, &data);
if (err && err != LFS_ERR_NOENT) {
return err;
}
// note many of these functions leave bshrub undefined if
// there is an error, so we first read into the staging
// bshrub
// may be a sprout (simple inlined data)
if (err != LFS_ERR_NOENT && tag == LFSR_TAG_DATA) {
file->o.bshrub_.u.bsprout = data;
// or a direct block
} else if (err != LFS_ERR_NOENT && tag == LFSR_TAG_BLOCK) {
err = lfsr_data_readbptr(lfs, &data,
&file->o.bshrub_.u.bptr);
if (err) {
return err;
}
// or a bshrub (inlined btree)
} else if (err != LFS_ERR_NOENT && tag == LFSR_TAG_BSHRUB) {
err = lfsr_data_readshrub(lfs, &data, &file->o.o.mdir,
&file->o.bshrub_.u.bshrub);
if (err) {
return err;
}
// or a btree
} else if (err != LFS_ERR_NOENT && tag == LFSR_TAG_BTREE) {
err = lfsr_data_fetchbtree(lfs, &data,
&file->o.bshrub_.u.btree);
if (err) {
return err;
}
}
}
// mark as flushed and synced if we're not an orphan
file->o.o.flags &= ~(
LFS_O_UNFLUSH
| ((!lfsr_o_isorphan(file->o.o.flags))
? LFS_O_UNSYNC
: 0));
// update the bshrub
file->o.bshrub = file->o.bshrub_;
// discard the current buffer
file->buffer.pos = 0;
file->buffer.size = 0;
// if our file is small, try to keep the whole thing in our buffer
//
// if this fails we may end up with corrupt data, but that's ok, we
// just can't end up with corrupt metadata
lfs_size_t size = lfsr_bshrub_size(&file->o.bshrub);
if (size <= lfsr_file_inlinesize(lfs, file)) {
lfs_ssize_t d = lfsr_file_read_(lfs, file,
0, file->buffer.buffer, size);
if (d < 0) {
return d;
}
// small files remain perpetually unflushed
file->o.o.flags |= LFS_O_UNFLUSH;
file->o.bshrub = LFSR_BSHRUB_BNULL();
file->buffer.pos = 0;
file->buffer.size = size;
}
return 0;
}
// needed in lfsr_file_opencfg
static int lfsr_file_ck(lfs_t *lfs, const lfsr_file_t *file,
uint32_t flags);
@@ -11130,7 +11215,9 @@ int lfsr_file_opencfg(lfs_t *lfs, lfsr_file_t *file,
file->cfg = cfg;
file->o.o.flags = lfsr_o_settype(flags, LFS_TYPE_REG)
// mounted with LFS_M_FLUSH/SYNC? implies LFS_O_FLUSH/SYNC
| (lfs->flags & (LFS_M_FLUSH | LFS_M_SYNC));
| (lfs->flags & (LFS_M_FLUSH | LFS_M_SYNC))
// default to unflushed for orphans/truncated files
| LFS_O_UNFLUSH;
// default data state
file->o.bshrub = LFSR_BSHRUB_BNULL();
file->pos = 0;
@@ -11189,8 +11276,8 @@ int lfsr_file_opencfg(lfs_t *lfs, lfsr_file_t *file,
file->o.o.flags |= LFS_O_UNSYNC | LFS_O_ORPHAN;
} else {
// wanted to create a new entry?
if (lfsr_o_isexcl(flags)) {
// oh, we really wanted to create a new entry
return LFS_ERR_EXIST;
}
@@ -11198,52 +11285,6 @@ int lfsr_file_opencfg(lfs_t *lfs, lfsr_file_t *file,
if (tag != LFSR_TAG_REG) {
return LFS_ERR_ISDIR;
}
// if we're truncating don't bother to read any state, we're
// just going to truncate after all
if (!lfsr_o_istrunc(flags)) {
// lookup the file struct, if there is one
lfsr_tag_t tag;
lfsr_data_t data;
err = lfsr_mdir_lookupnext(lfs, &file->o.o.mdir, LFSR_TAG_DATA,
&tag, &data);
if (err && err != LFS_ERR_NOENT) {
return err;
}
// TODO the above clobbers data on failure, which is why we can't
// lookup into the inlined data directly. Should this be avoided?
// Should we at least be consistent in this codebase?
// may be a sprout (simple inlined data)
if (err != LFS_ERR_NOENT && tag == LFSR_TAG_DATA) {
file->o.bshrub.u.bsprout = data;
// or a direct block
} else if (err != LFS_ERR_NOENT && tag == LFSR_TAG_BLOCK) {
err = lfsr_data_readbptr(lfs, &data,
&file->o.bshrub.u.bptr);
if (err) {
return err;
}
// or a bshrub (inlined btree)
} else if (err != LFS_ERR_NOENT && tag == LFSR_TAG_BSHRUB) {
err = lfsr_data_readshrub(lfs, &data, &file->o.o.mdir,
&file->o.bshrub.u.bshrub);
if (err) {
return err;
}
// or a btree
} else if (err != LFS_ERR_NOENT && tag == LFSR_TAG_BTREE) {
err = lfsr_data_fetchbtree(lfs, &data,
&file->o.bshrub.u.btree);
if (err) {
return err;
}
}
}
}
// allocate buffer if necessary
@@ -11258,21 +11299,12 @@ int lfsr_file_opencfg(lfs_t *lfs, lfsr_file_t *file,
file->buffer.pos = 0;
file->buffer.size = 0;
// if our file is small, try to keep the whole thing in our buffer
lfs_size_t size = lfsr_bshrub_size(&file->o.bshrub);
if (size <= lfsr_file_inlinesize(lfs, file)) {
lfs_ssize_t d = lfsr_file_read_(lfs, file,
0, file->buffer.buffer, size);
if (d < 0) {
err = d;
// fetch the file struct if we're not truncating
if (!lfsr_o_istrunc(file->o.o.flags)) {
err = lfsr_file_fetch(lfs, file);
if (err) {
goto failed;
}
// small files remain perpetually unflushed
file->o.o.flags |= LFS_O_UNFLUSH;
file->o.bshrub = LFSR_BSHRUB_BNULL();
file->buffer.pos = 0;
file->buffer.size = size;
}
// check metadata/data for errors?
@@ -12787,84 +12819,10 @@ int lfsr_file_resync(lfs_t *lfs, lfsr_file_t *file) {
// do nothing if already in-sync
if (lfsr_o_isunsync(file->o.o.flags)) {
// default data state
file->o.bshrub_ = LFSR_BSHRUB_BNULL();
// don't bother reading disk if we're an orphan
if (!lfsr_o_isorphan(file->o.o.flags)) {
// lookup the file struct, if there is one
lfsr_tag_t tag;
lfsr_data_t data;
err = lfsr_mdir_lookupnext(lfs, &file->o.o.mdir, LFSR_TAG_DATA,
&tag, &data);
if (err && err != LFS_ERR_NOENT) {
goto failed;
}
// note many of these functions leave bshrub undefined if
// there is an error, so we first read into the staging
// bshrub
// may be a sprout (simple inlined data)
if (err != LFS_ERR_NOENT && tag == LFSR_TAG_DATA) {
file->o.bshrub_.u.bsprout = data;
// or a direct block
} else if (err != LFS_ERR_NOENT && tag == LFSR_TAG_BLOCK) {
err = lfsr_data_readbptr(lfs, &data,
&file->o.bshrub_.u.bptr);
if (err) {
goto failed;
}
// or a bshrub (inlined btree)
} else if (err != LFS_ERR_NOENT && tag == LFSR_TAG_BSHRUB) {
err = lfsr_data_readshrub(lfs, &data, &file->o.o.mdir,
&file->o.bshrub_.u.bshrub);
if (err) {
goto failed;
}
// or a btree
} else if (err != LFS_ERR_NOENT && tag == LFSR_TAG_BTREE) {
err = lfsr_data_fetchbtree(lfs, &data,
&file->o.bshrub_.u.btree);
if (err) {
goto failed;
}
}
}
// mark as flushed and synced if we're not an orphan
file->o.o.flags &= ~(
LFS_O_UNFLUSH
| ((!lfsr_o_isorphan(file->o.o.flags))
? LFS_O_UNSYNC
: 0));
// update the bshrub
file->o.bshrub = file->o.bshrub_;
// discard the current buffer
file->buffer.pos = 0;
file->buffer.size = 0;
// if our file is small, try to keep the whole thing in our buffer
//
// if this fails we may end up with corrupt data, but that's ok, we
// just can't end up with corrupt metadata
lfs_size_t size = lfsr_bshrub_size(&file->o.bshrub);
if (size <= lfsr_file_inlinesize(lfs, file)) {
lfs_ssize_t d = lfsr_file_read_(lfs, file,
0, file->buffer.buffer, size);
if (d < 0) {
err = d;
goto failed;
}
// small files remain perpetually unflushed
file->o.o.flags |= LFS_O_UNFLUSH;
file->o.bshrub = LFSR_BSHRUB_BNULL();
file->buffer.pos = 0;
file->buffer.size = size;
// refetch the file struct from disk
err = lfsr_file_fetch(lfs, file);
if (err) {
goto failed;
}
}