Made significant progress around inlined-file state during mdir commits

The main improvement is moving the special inlined-file compaction logic
up into lfsr_mdir_compact__. We only need this logic for files stored in
mdirs, and thanks to its recursive nature, we weren't getting any
benefit from handling this at a lower level anyways.

This is a nice logical restructuring that probably saves a bit of code
cost in the end.

Another significant improvement is moving the staging copy of the
inlined tree's state up into the file struct itself. This solves the
problem of needed N copies of temporary inlined state when you have N
open files.

It also provides a central place to stage changes when compacting
inlined trees, which happens across several different places in the mdir
commit logic. Though some may see this as more a hack than a feature.

Also note-worthy, but minor: these changes required an additional
opened-mdir linked-list to know when the mdir is a file and may contain
an inlined tree.
This commit is contained in:
Christopher Haster
2023-09-20 23:31:56 -05:00
parent 541fb07da4
commit 9f0160556f
4 changed files with 366 additions and 202 deletions
+278 -140
View File
@@ -724,10 +724,6 @@ static inline bool lfsr_tag_istrunk(lfsr_tag_t tag) {
return (tag & 0x6000) != 0x2000;
}
static inline uint8_t lfsr_tag_filetype(lfsr_tag_t tag) {
return tag - LFSR_TAG_REG;
}
static inline bool lfsr_tag_isinternal(lfsr_tag_t tag) {
return tag & LFSR_TAG_INTERNAL;
}
@@ -3017,11 +3013,6 @@ static int lfsr_rbyd_appendcompactattr(lfs_t *lfs, lfsr_rbyd_t *rbyd,
return 0;
}
// lfsr_rbyd_appendcompactrbyd actually calls lfsr_rbyd_compact if it
// encounters an inlined tree
static int lfsr_rbyd_compact(lfs_t *lfs, lfsr_rbyd_t *rbyd,
bool deferred, lfs_off_t off);
static int lfsr_rbyd_appendcompactrbyd(lfs_t *lfs, lfsr_rbyd_t *rbyd_,
lfsr_srid_t start_rid, lfsr_srid_t end_rid,
const lfsr_rbyd_t *rbyd) {
@@ -3047,69 +3038,11 @@ static int lfsr_rbyd_appendcompactrbyd(lfs_t *lfs, lfsr_rbyd_t *rbyd_,
break;
}
// found an inlined tree? we need to compact the tree as well to bring
// it along with us
if (tag == LFSR_TAG_TRUNK) {
lfsr_rbyd_t inlined_rbyd;
err = lfsr_data_readtrunk(lfs, &data, &inlined_rbyd);
if (err) {
return err;
}
inlined_rbyd.block = rbyd->block;
// keep track of the start of our new tree
lfs_size_t off = rbyd_->eoff;
// what a great use case for recursion! too bad we can't use
// recursion here
lfsr_srid_t rid_ = -1;
lfsr_tag_t tag_ = 0;
while (true) {
lfsr_rid_t weight_;
lfsr_data_t data_;
err = lfsr_rbyd_lookupnext(lfs, &inlined_rbyd,
rid_, tag_+1,
&rid_, &tag_, &weight_, &data_);
if (err) {
if (err == LFS_ERR_NOENT) {
break;
}
return err;
}
// write the tag_
err = lfsr_rbyd_appendcompactattr(lfs, rbyd_,
tag_, weight_, data_);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
}
// compact our inlined tree
err = lfsr_rbyd_compact(lfs, rbyd_, true, off);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
// write the new inlined tree tag
uint8_t trunk_buf[LFSR_TRUNK_DSIZE];
err = lfsr_rbyd_appendcompactattr(lfs, rbyd_,
tag, weight, lfsr_data_fromtrunk(lfs,
rbyd_, trunk_buf));
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
} else {
// write the tag
err = lfsr_rbyd_appendcompactattr(lfs, rbyd_, tag, weight, data);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
// write the tag
err = lfsr_rbyd_appendcompactattr(lfs, rbyd_, tag, weight, data);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
}
@@ -3120,8 +3053,9 @@ static int lfsr_rbyd_compact(lfs_t *lfs, lfsr_rbyd_t *rbyd,
bool deferred, lfs_size_t off) {
// must fetch before mutating!
LFS_ASSERT(lfsr_rbyd_isfetched(rbyd));
// offset must be after the revision count
LFS_ASSERT(off >= sizeof(uint32_t));
// TODO still need this?
// ignore empty rbyds, we can't really compact these, so leave it up to
// upper layers to deal with this
if (rbyd->eoff <= sizeof(uint32_t)) {
@@ -4719,13 +4653,15 @@ static int lfsr_data_readmblocks(lfs_t *lfs, lfsr_data_t *data,
// track opened mdirs that may need to by updated
static void lfsr_mdir_addopened(lfs_t *lfs, int type,
lfsr_openedmdir_t *opened) {
opened->next = lfs->opened[type];
lfs->opened[type] = opened;
opened->next = lfs->opened[type-LFS_TYPE_REG];
lfs->opened[type-LFS_TYPE_REG] = opened;
}
static void lfsr_mdir_removeopened(lfs_t *lfs, int type,
lfsr_openedmdir_t *opened) {
for (lfsr_openedmdir_t **p = &lfs->opened[type]; *p; p = &(*p)->next) {
for (lfsr_openedmdir_t **p = &lfs->opened[type-LFS_TYPE_REG];
*p;
p = &(*p)->next) {
if (*p == opened) {
*p = (*p)->next;
break;
@@ -4735,7 +4671,9 @@ static void lfsr_mdir_removeopened(lfs_t *lfs, int type,
static bool lfsr_mdir_isopened(lfs_t *lfs, int type,
const lfsr_openedmdir_t *opened) {
for (lfsr_openedmdir_t *p = lfs->opened[type]; p; p = p->next) {
for (lfsr_openedmdir_t *p = lfs->opened[type-LFS_TYPE_REG];
p;
p = p->next) {
if (p == opened) {
return true;
}
@@ -5148,13 +5086,14 @@ static int lfsr_mdir_commit__(lfs_t *lfs, lfsr_mdir_t *mdir,
} else if (attrs[i].tag == LFSR_TAG_DEFER) {
const lfsr_defer_t *defer
= (const lfsr_defer_t*)attrs[i].data.u.b.buffer;
// swap out our trunk/weight temporarily, note we're operating
// on a copy so if this fails not _too_ many things will get
// messed up
//
// it is important that these rbyds share eoff/cksum/etc
mdir_.u.m.weight = defer->rbyd->weight;
mdir_.u.m.trunk = defer->rbyd->trunk;
mdir_.u.m.weight = defer->rbyd->weight;
// append any deferred attributes
int err = lfsr_rbyd_appendattrs(lfs, &mdir_.u.r.rbyd, -1, -1,
@@ -5163,10 +5102,11 @@ static int lfsr_mdir_commit__(lfs_t *lfs, lfsr_mdir_t *mdir,
return err;
}
defer->rbyd->weight = mdir_.u.m.weight;
// revert to mdir trunk/weight
defer->rbyd->trunk = mdir_.u.m.trunk;
mdir_.u.m.weight = mdir->u.m.weight;
defer->rbyd->weight = mdir_.u.m.weight;
mdir_.u.m.trunk = mdir->u.m.trunk;
mdir_.u.m.weight = mdir->u.m.weight;
// write out normal tags normally
} else {
@@ -5228,18 +5168,207 @@ static int lfsr_mdir_commit__(lfs_t *lfs, lfsr_mdir_t *mdir,
return 0;
}
// needed by lfsr_mdir_compact__
static bool lfsr_file_isinlineddata(const lfsr_file_t *file);
static bool lfsr_file_isinlinedtree(const lfsr_file_t *file);
static int lfsr_mdir_compact__(lfs_t *lfs, lfsr_mdir_t *mdir_,
lfsr_srid_t start_rid, lfsr_srid_t end_rid,
const lfsr_mdir_t *mdir) {
int err = lfsr_rbyd_appendcompactrbyd(lfs, &mdir_->u.r.rbyd,
start_rid, end_rid, &mdir->u.r.rbyd);
// this is basically the same as lfsr_rbyd_appendcompactrbyd +
// lfsr_rbyd_compact, but with special handling for inlined trees.
//
// it's really tempting to deduplicate this via recursion! but we can't
// do that here
// copy over tags in the rbyd in order
lfsr_srid_t rid = start_rid;
lfsr_tag_t tag = 0;
while (true) {
lfsr_rid_t weight;
lfsr_data_t data;
int err = lfsr_rbyd_lookupnext(lfs, &mdir->u.r.rbyd,
rid, tag+1,
&rid, &tag, &weight, &data);
if (err) {
if (err == LFS_ERR_NOENT) {
break;
}
return err;
}
// end of range? note the use of rid+1 and unsigned comparison here to
// treat end_rid=-1 as "unbounded" in such a way that rid=-1 is still
// included
if ((lfs_size_t)(rid + 1) > (lfs_size_t)end_rid) {
break;
}
// found an inlined data? we can just copy this like normal but
// we need to update any opened inlined files
if (tag == LFSR_TAG_INLINED) {
// write the tag
err = lfsr_rbyd_appendcompactattr(lfs, &mdir_->u.r.rbyd,
tag, weight, data);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
// stage any opened deferred trees with their new location so
// we can update these later if our commit is a success
for (lfsr_openedmdir_t *opened = lfs->opened[
LFS_TYPE_REG-LFS_TYPE_REG];
opened;
opened = opened->next) {
lfsr_file_t *file = (lfsr_file_t*)opened;
if (lfsr_file_isinlineddata(file)
&& file->inlined.u.data.u.d.block == data.u.d.block
&& file->inlined.u.data.u.d.off == data.u.d.off) {
// this is a bit tricky see we don't know the tag size,
// but we have enough enough info
file->inlined_.u.data = LFSR_DATA_DISK(
mdir_->u.r.rbyd.block,
mdir_->u.r.rbyd.eoff
- lfsr_data_size(&file->inlined.u.data),
lfsr_data_size(&file->inlined.u.data));
}
}
// found an inlined tree? we need to compact the tree as well to bring
// it along with us
} else if (tag == LFSR_TAG_TRUNK) {
lfsr_rbyd_t inlined_rbyd;
err = lfsr_data_readtrunk(lfs, &data, &inlined_rbyd);
if (err) {
return err;
}
inlined_rbyd.block = mdir->u.r.rbyd.block;
// keep track of the start of our new tree
lfs_size_t off = mdir_->u.r.rbyd.eoff;
// compact our inlined tree
err = lfsr_rbyd_appendcompactrbyd(lfs, &mdir_->u.r.rbyd, -1, -1,
&inlined_rbyd);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
err = lfsr_rbyd_compact(lfs, &mdir_->u.r.rbyd, true, off);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
// write the new inlined tree tag
uint8_t trunk_buf[LFSR_TRUNK_DSIZE];
err = lfsr_rbyd_appendcompactattr(lfs, &mdir_->u.r.rbyd,
tag, weight, lfsr_data_fromtrunk(lfs,
&mdir_->u.r.rbyd, trunk_buf));
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
// stage any opened deferred trees with their new location so
// we can update these later if our commit is a success
for (lfsr_openedmdir_t *opened = lfs->opened[
LFS_TYPE_REG-LFS_TYPE_REG];
opened;
opened = opened->next) {
lfsr_file_t *file = (lfsr_file_t*)opened;
if (lfsr_file_isinlinedtree(file)
&& file->inlined.u.rbyd.block == mdir->u.r.rbyd.block
&& file->inlined.u.rbyd.trunk == inlined_rbyd.trunk) {
file->inlined_.u.rbyd.block = mdir_->u.r.rbyd.block;
file->inlined_.u.rbyd.trunk = mdir_->u.r.rbyd.trunk;
file->inlined_.u.rbyd.weight = mdir_->u.r.rbyd.weight;
file->inlined_.u.deferred.overhead
= file->inlined.u.deferred.overhead;
}
}
} else {
// write the tag
err = lfsr_rbyd_appendcompactattr(lfs, &mdir_->u.r.rbyd,
tag, weight, data);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
}
}
int err = lfsr_rbyd_compact(lfs, &mdir_->u.r.rbyd, false, sizeof(uint32_t));
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
err = lfsr_rbyd_compact(lfs, &mdir_->u.r.rbyd, false, sizeof(uint32_t));
if (err) {
return err;
// we're not quite done! we also need to bring over any opened,
// unsynced files
//
// TODO note for this to fully work we need to mark opened readonly
// files as unsynced if their entry is updated
for (lfsr_openedmdir_t *opened = lfs->opened[
LFS_TYPE_REG-LFS_TYPE_REG];
opened;
opened = opened->next) {
lfsr_file_t *file = (lfsr_file_t*)opened;
// inlined data?
if (lfsr_file_isinlineddata(file)
&& (file->flags & LFS_F_UNSYNCED)
&& file->inlined.u.data.u.d.block == mdir->u.r.rbyd.block) {
// write the data as a deferred tag
err = lfsr_rbyd_appendcompactattr(lfs, &mdir_->u.r.rbyd,
LFSR_TAG_DEFERRED(INLINED), 0, file->inlined.u.data);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
// this is a bit tricky see we don't know the tag size,
// but we have enough enough info
file->inlined_.u.data = LFSR_DATA_DISK(
mdir_->u.r.rbyd.block,
mdir_->u.r.rbyd.eoff
- lfsr_data_size(&file->inlined.u.data),
lfsr_data_size(&file->inlined.u.data));
// inlined tree?
} else if (lfsr_file_isinlinedtree(file)
&& (file->flags & LFS_F_UNSYNCED)
&& file->inlined.u.rbyd.block == mdir->u.r.rbyd.block) {
// save our current off/trunk/weight
lfs_size_t off = mdir_->u.r.rbyd.eoff;
lfs_size_t trunk = mdir_->u.r.rbyd.trunk;
lfsr_srid_t weight = mdir_->u.r.rbyd.weight;
// compact our inlined tree
err = lfsr_rbyd_appendcompactrbyd(lfs, &mdir_->u.r.rbyd, -1, -1,
&file->inlined.u.rbyd);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
err = lfsr_rbyd_compact(lfs, &mdir_->u.r.rbyd, true, off);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
// stage our new trunk and revert to mdir trunk/weight
file->inlined_.u.rbyd.block = mdir_->u.r.rbyd.block;
file->inlined_.u.rbyd.trunk = mdir_->u.r.rbyd.trunk;
file->inlined_.u.rbyd.weight = mdir_->u.r.rbyd.weight;
file->inlined_.u.deferred.overhead
= file->inlined.u.deferred.overhead;
mdir_->u.r.rbyd.trunk = trunk;
mdir_->u.r.rbyd.weight = weight;
}
}
return 0;
@@ -5347,8 +5476,8 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
if (mid == -1 || lfsr_mtree_isinlined(lfs)) {
lfsr_mdir_unerase(&lfs->mroot);
}
for (int type = 0; type < 2; type++) {
for (lfsr_openedmdir_t *opened = lfs->opened[type];
for (int type = LFS_TYPE_REG; type < LFS_TYPE_REG+3; type++) {
for (lfsr_openedmdir_t *opened = lfs->opened[type-LFS_TYPE_REG];
opened;
opened = opened->next) {
if ((opened->mdir.mid & lfsr_midbmask(lfs))
@@ -5754,8 +5883,8 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
// gstate must have been committed by a lower-level function at this point
LFS_ASSERT(lfsr_grm_iszero(lfs->dgrm));
// update our gstate
for (lfs_size_t i = 0; i < attr_count; i++) {
// update gstate
if (attrs[i].tag == LFSR_TAG_GRM) {
lfs->grm = *(lfsr_grm_t*)attrs[i].data.u.b.buffer;
@@ -5765,8 +5894,8 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
}
// update any opened mdirs
for (int type = 0; type < 2; type++) {
for (lfsr_openedmdir_t *opened = lfs->opened[type];
for (int type = LFS_TYPE_REG; type < LFS_TYPE_REG+3; type++) {
for (lfsr_openedmdir_t *opened = lfs->opened[type-LFS_TYPE_REG];
opened;
opened = opened->next) {
// avoid double updating current mdir, and avoid updating
@@ -5826,7 +5955,8 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
- lfsr_mtree_weight(lfs);
}
// some extra work is needed for opened dirs
// update any changes to directory bookmarks/positions, this
// gets a bit tricky
if (type == LFS_TYPE_DIR) {
lfsr_dir_t *dir = (lfsr_dir_t*)opened;
for (lfs_size_t i = 0; i < attr_count; i++) {
@@ -5867,6 +5997,20 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
- lfsr_mtree_weight(lfs);
}
}
// if we compacted, update any staged changes to inlined data/trees
if (type == LFS_TYPE_REG) {
lfsr_file_t *file = (lfsr_file_t*)opened;
if (mdir_.u.m.blocks[0] != mdir->u.m.blocks[0]
&& ((lfsr_file_isinlineddata(file)
&& file->inlined.u.data.u.d.block
== mdir->u.m.blocks[0])
|| (lfsr_file_isinlinedtree(file)
&& file->inlined.u.rbyd.block
== mdir->u.m.blocks[0]))) {
file->inlined = file->inlined_;
}
}
next:;
}
}
@@ -7218,7 +7362,7 @@ int lfsr_mkdir(lfs_t *lfs, const char *path) {
// the bookmark first risks inserting the bookmark before the metadata
// entry, which breaks things.
//
lfsr_mdir_addopened(lfs, LFS_TYPE_REG, &bookmark);
lfsr_mdir_addopened(lfs, LFS_TYPE_INTERNAL, &bookmark);
// commit our new directory into our parent, creating a grm to self-remove
// in case of powerloss
@@ -7230,7 +7374,7 @@ int lfsr_mkdir(lfs_t *lfs, const char *path) {
goto failed_with_bookmark;
}
lfsr_mdir_removeopened(lfs, LFS_TYPE_REG, &bookmark);
lfsr_mdir_removeopened(lfs, LFS_TYPE_INTERNAL, &bookmark);
// commit our bookmark and zero the grm, the bookmark tag is an empty
// entry that marks our did as allocated
@@ -7494,7 +7638,7 @@ static int lfsr_mdir_stat(lfs_t *lfs, lfsr_mdir_t *mdir, lfsr_mid_t mid,
}
// get file type from the tag
info->type = lfsr_tag_filetype(tag);
info->type = lfsr_tag_subtype(tag);
// get file name from the name entry
LFS_ASSERT(lfsr_data_size(&data) <= LFS_NAME_MAX);
@@ -7724,7 +7868,8 @@ int lfsr_dir_rewind(lfs_t *lfs, lfsr_dir_t *dir) {
#define LFSR_FILE_INLINEDDATA 0x80000000
static bool lfsr_file_isinlineddata(const lfsr_file_t *file) {
return file->inlined.u.weight & LFSR_FILE_INLINEDDATA;
// this checks that both the inlineddata bit and non-zero
return (lfs_size_t)file->inlined.u.weight > (LFSR_FILE_INLINEDDATA | 0);
}
static bool lfsr_file_isinlinedtree(const lfsr_file_t *file) {
@@ -7759,9 +7904,9 @@ int lfsr_file_opencfg(lfs_t *lfs, lfsr_file_t *file,
file->cfg = cfg;
file->pos = 0;
// default inlined state
file->inlined.u.d.block = 0;
file->inlined.u.d.off = 0;
file->inlined.u.d.size = LFSR_FILE_INLINEDDATA | 0;
file->inlined.u.data.u.d.block = 0;
file->inlined.u.data.u.d.off = 0;
file->inlined.u.data.u.d.size = LFSR_FILE_INLINEDDATA | 0;
// lookup our parent
lfsr_tag_t tag;
@@ -7840,10 +7985,12 @@ int lfsr_file_opencfg(lfs_t *lfs, lfsr_file_t *file,
if (err != LFS_ERR_NOENT) {
LFS_ASSERT(lfsr_file_inlinedsize(file) == 0);
err = lfsr_data_readtrunk(lfs, &data,
(lfsr_rbyd_t*)&file->inlined);
&file->inlined.u.rbyd);
if (err) {
return err;
}
file->inlined.u.rbyd.block = file->m.mdir.u.m.blocks[0];
}
}
}
@@ -7941,16 +8088,11 @@ int lfsr_file_read_(lfs_t *lfs, const lfsr_file_t *file,
// is the data in an inlined tree?
if (lfsr_file_isinlinedtree(file)
&& pos < lfsr_file_inlinedsize(file)) {
lfsr_rbyd_t rbyd;
rbyd.block = file->m.mdir.u.m.blocks[0];
rbyd.trunk = file->inlined.u.t.trunk;
rbyd.weight = file->inlined.u.t.weight;
lfsr_srid_t rid;
lfsr_tag_t tag;
lfsr_rid_t weight;
lfsr_data_t data;
int err = lfsr_rbyd_lookupnext(lfs, &rbyd, pos, 0,
int err = lfsr_rbyd_lookupnext(lfs, &file->inlined.u.rbyd, pos, 0,
&rid, &tag, &weight, &data);
if (err && err != LFS_ERR_NOENT) {
return err;
@@ -8003,7 +8145,7 @@ lfs_ssize_t lfsr_file_read(lfs_t *lfs, lfsr_file_t *file,
static int lfsr_file_flushbuffer(lfs_t *lfs, lfsr_file_t *file) {
while (file->buffer_size > 0) {
// do we need an inlined tree?
if (lfsr_file_isinlineddata(file)) {
if (!lfsr_file_isinlinedtree(file)) {
// TODO rm? we haven't updated file->size yet!
// // we shouldn't reach this point if we still fit entirely
// // in a simple inlined file
@@ -8013,30 +8155,33 @@ static int lfsr_file_flushbuffer(lfs_t *lfs, lfsr_file_t *file) {
// do we carve out any data from the inlined data?
lfsr_data_t left_data = LFSR_DATA_DISK(
file->inlined.u.d.block,
file->inlined.u.d.off,
file->inlined.u.data.u.d.block,
file->inlined.u.data.u.d.off,
lfs_min32(
file->buffer_pos,
lfsr_file_inlinedsize(file)));
lfs_off_t right_pos = file->buffer_pos + file->buffer_size;
lfsr_data_t right_data = LFSR_DATA_DISK(
file->inlined.u.d.block,
file->inlined.u.d.off + right_pos,
file->inlined.u.data.u.d.block,
file->inlined.u.data.u.d.off + right_pos,
lfsr_file_inlinedsize(file) - lfs_min32(
right_pos,
lfsr_file_inlinedsize(file)));
// create a zero weight trunk
file->inlined.u.t.trunk = 0;
file->inlined.u.t.weight = 0;
//
// make sure to use our staging rbyd so we catch in-flight updates
// caused by mdir compactions
file->inlined_.u.rbyd.block = file->m.mdir.u.m.blocks[0];
file->inlined_.u.rbyd.trunk = 0;
file->inlined_.u.rbyd.weight = 0;
// TODO
file->inlined.u.t.overhead = 0;
file->inlined_.u.deferred.overhead = 0;
int err = lfsr_mdir_commit(lfs, &file->m.mdir, LFSR_ATTRS(
LFSR_ATTR_(file->m.mdir.mid, DEFER, 0, DEFER(
// TODO bit of a hack...
(lfsr_rbyd_t*)&file->inlined,
&file->inlined_.u.rbyd,
// note we always need a zero entry
(file->buffer_pos > 0
? LFSR_ATTR(0,
@@ -8058,6 +8203,7 @@ static int lfsr_file_flushbuffer(lfs_t *lfs, lfsr_file_t *file) {
return err;
}
file->inlined = file->inlined_;
file->buffer_size = 0;
continue;
}
@@ -8069,19 +8215,12 @@ static int lfsr_file_flushbuffer(lfs_t *lfs, lfsr_file_t *file) {
// this is a complex question since we may be carving out leaves of the
// inlined tree, we need to find these leaves to know for sure
//
// TODO can we somehow fit an rbyd struct in the file_t so we don't
// need this copy?
lfsr_rbyd_t rbyd;
rbyd.block = file->m.mdir.u.m.blocks[0];
rbyd.trunk = file->inlined.u.t.trunk;
rbyd.weight = file->inlined.u.t.weight;
lfsr_srid_t left_rid;
lfsr_tag_t left_tag;
lfsr_rid_t left_weight;
lfsr_data_t left_data;
int err = lfsr_rbyd_lookupnext(lfs, &rbyd,
lfs_min32(file->buffer_pos, rbyd.weight-1), 0,
int err = lfsr_rbyd_lookupnext(lfs, &file->inlined.u.rbyd,
lfs_min32(file->buffer_pos, file->inlined.u.rbyd.weight-1), 0,
&left_rid, &left_tag, &left_weight, &left_data);
if (err && err != LFS_ERR_NOENT) {
return err;
@@ -8108,7 +8247,7 @@ static int lfsr_file_flushbuffer(lfs_t *lfs, lfsr_file_t *file) {
lfsr_tag_t right_tag;
lfsr_rid_t right_weight;
lfsr_data_t right_data;
err = lfsr_rbyd_lookupnext(lfs, &rbyd,
err = lfsr_rbyd_lookupnext(lfs, &file->inlined.u.rbyd,
file->buffer_pos + file->buffer_size-1, 0,
&right_rid, &right_tag, &right_weight, &right_data);
if (err && err != LFS_ERR_NOENT) {
@@ -8136,19 +8275,22 @@ static int lfsr_file_flushbuffer(lfs_t *lfs, lfsr_file_t *file) {
}
// write out our buffer and any carved data
//
// make sure to use our staging rbyd so we catch in-flight updates
// caused by mdir compactions
file->inlined_ = file->inlined;
err = lfsr_mdir_commit(lfs, &file->m.mdir, LFSR_ATTRS(
LFSR_ATTR_(file->m.mdir.mid, DEFER, 0, DEFER(
// TODO bit of a hack...
(lfsr_rbyd_t*)&file->inlined,
&file->inlined_.u.rbyd,
// first remove anything in the way
LFSR_ATTR(
lfs_min32(
right_pos + lfsr_data_size(&right_data),
rbyd.weight)-1,
file->inlined.u.rbyd.weight)-1,
RM,
lfs_min32(
right_pos + lfsr_data_size(&right_data),
rbyd.weight) - left_pos,
file->inlined.u.rbyd.weight) - left_pos,
NULL),
// TODO this should handling holes somehow
(lfsr_data_size(&left_data) > 0
@@ -8171,6 +8313,7 @@ static int lfsr_file_flushbuffer(lfs_t *lfs, lfsr_file_t *file) {
return err;
}
file->inlined = file->inlined_;
file->buffer_size = 0;
continue;
@@ -8258,12 +8401,7 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) {
return 0;
}
// write out any data we need to
int err = lfsr_file_flush(lfs, file);
if (err) {
goto failed;
}
int err;
if (file->flags & LFS_F_UNSYNCED) {
// TODO what if buffer_size > inlined_size?
// TODO should we also update file to be unbuffered after syncing
@@ -8288,7 +8426,7 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) {
}
// but clear buffer after syncing simple inlined files, otherwise
// we risk O(n^2) behavior
// we risk runaway O(n^2) behavior
file->buffer_size = 0;
// we need to look up the inlined data again...
@@ -8315,8 +8453,7 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) {
uint8_t trunk_buf[LFSR_TRUNK_DSIZE];
err = lfsr_mdir_commit(lfs, &file->m.mdir, LFSR_ATTRS(
LFSR_ATTR(file->m.mdir.mid, WIDE(TRUNK), 0, FROMTRUNK(
// TODO too much of a hack?
lfs, (const lfsr_rbyd_t*)&file->inlined, trunk_buf))));
lfs, &file->inlined.u.rbyd, trunk_buf))));
if (err) {
goto failed;
}
@@ -11831,8 +11968,9 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
lfs->mleaf_bits = lfs_nlog2(lfs->cfg->block_size/16);
// zero linked-lists of opened mdirs
lfs->opened[LFS_TYPE_REG] = NULL;
lfs->opened[LFS_TYPE_DIR] = NULL;
lfs->opened[LFS_TYPE_REG - LFS_TYPE_REG] = NULL;
lfs->opened[LFS_TYPE_DIR - LFS_TYPE_REG] = NULL;
lfs->opened[LFS_TYPE_INTERNAL - LFS_TYPE_REG] = NULL;
// zero gstate
memset(lfs->ggrm, 0, LFSR_GRM_DSIZE);
+28 -22
View File
@@ -106,8 +106,11 @@ enum lfs_error {
// File types
enum lfs_type {
// file types
LFS_TYPE_REG = 0,
LFS_TYPE_DIR = 1,
LFS_TYPE_REG = 2,
LFS_TYPE_DIR = 3,
// used internally, don't use this
LFS_TYPE_INTERNAL = 4,
// // internally used types
// LFS_TYPE_SPLICE = 0x400,
@@ -152,6 +155,7 @@ enum lfs_open_flags {
#endif
// internally used flags
// TODO do we need both unflushed and unsynced?
LFS_F_UNFLUSHED = 0x010000, // File's has data that needs to be written
LFS_F_UNSYNCED = 0x020000, // File's metadata does not match storage
LFS_F_ERRORED = 0x040000, // An error occurred during write
@@ -490,6 +494,22 @@ typedef struct lfs_file {
const struct lfs_file_config *cfg;
} lfs_file_t;
typedef struct lfsr_inlined {
union {
// note sign bit indicates if data is a single inlined data, or an
// inlined tree, this works because inlined data is always on disk,
// so data.size always has sign=1
lfs_soff_t weight;
lfsr_data_t data;
lfsr_rbyd_t rbyd;
struct {
lfs_soff_t weight;
lfs_size_t trunk;
lfs_size_t overhead;
} deferred;
} u;
} lfsr_inlined_t;
typedef struct lfsr_file {
lfsr_openedmdir_t m;
uint32_t flags;
@@ -500,25 +520,11 @@ typedef struct lfsr_file {
uint8_t *buffer;
lfs_size_t buffer_size;
struct {
union {
// note sign bit indicates if data is a single inlined data, or an
// inlined tree, this works because inlined data is always on disk,
// so data.size always has sign=1
lfs_soff_t weight;
lfsr_data_t data;
struct {
lfs_ssize_t size;
lfs_size_t off;
lfs_block_t block;
} d;
struct {
lfs_soff_t weight;
lfs_size_t trunk;
lfs_size_t overhead;
} t;
} u;
} inlined;
// we need copies of inlined references in case of mdir compaction, this
// exists here instead of on the stack becuase we don't know how many
// inlined files may be opened
lfsr_inlined_t inlined;
lfsr_inlined_t inlined_;
const struct lfs_file_config *cfg;
} lfsr_file_t;
@@ -578,7 +584,7 @@ typedef struct lfs {
// linked-lists of opened mdirs, we keep a separate linked-list
// for each type since these need to be handled a bit differently
lfsr_openedmdir_t *opened[2];
lfsr_openedmdir_t *opened[3];
#ifdef LFS_MIGRATE
struct lfs1 *lfs1;
+20
View File
@@ -874,6 +874,7 @@ def frepr(mdir, rid, tag):
did, _ = fromleb128(data)
did = '0x%x' % did
return 'bookmark %s' % did
elif tag == TAG_DIR:
# read the did
did = '?'
@@ -882,6 +883,25 @@ def frepr(mdir, rid, tag):
did, _ = fromleb128(data)
did = '0x%x' % did
return 'dir %s' % did
elif tag == TAG_REG:
size = 0
structs = []
# inlined?
done, rid_, tag_, w_, j, d, data, _ = mdir.lookup(rid, TAG_INLINED)
if not done and rid_ == rid and tag_ == TAG_INLINED:
size = max(size, len(data))
structs.append('inlined 0x%x.%x %d' % (mdir.block, j+d, len(data)))
# inlined tree?
done, rid_, tag_, w_, j, d, data, _ = mdir.lookup(rid, TAG_TRUNK)
if not done and rid_ == rid and tag_ == TAG_TRUNK:
d = 0
trunk, d_ = fromleb128(data[d:]); d += d_
weight, d_ = fromleb128(data[d:]); d += d_
size = max(size, weight)
structs.append('trunk 0x%x.%x' % (mdir.block, trunk))
return 'reg %s' % ', '.join(it.chain(['%d' % size], structs))
else:
return 'type 0x%02x' % (tag & 0xff)
+40 -40
View File
@@ -2707,8 +2707,8 @@ code = '''
.mdir={.mid=0, .u.m=lfs.mroot.u.m}};
lfsr_openedmdir_t right_neighbor = {
.mdir={.mid=1, .u.m=lfs.mroot.u.m}};
lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &left_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &right_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor);
// insert a new entry, this should update our neighbors
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS(
@@ -2730,8 +2730,8 @@ code = '''
assert(memcmp(&right_neighbor.mdir.u.m, &lfs.mroot.u.m,
sizeof(lfs.mroot.u.m)) == 0);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &left_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &right_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor);
lfsr_unmount(&lfs) => 0;
'''
@@ -2757,8 +2757,8 @@ code = '''
.mdir={.mid=0, .u.m=lfs.mroot.u.m}};
lfsr_openedmdir_t right_neighbor = {
.mdir={.mid=1, .u.m=lfs.mroot.u.m}};
lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &left_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &right_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor);
// try removing our left entry
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS(
@@ -2773,8 +2773,8 @@ code = '''
assert(memcmp(&right_neighbor.mdir.u.m, &lfs.mroot.u.m,
sizeof(lfs.mroot.u.m)) == 0);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &left_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &right_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor);
lfsr_unmount(&lfs) => 0;
'''
@@ -2800,8 +2800,8 @@ code = '''
.mdir={.mid=0, .u.m=lfs.mroot.u.m}};
lfsr_openedmdir_t right_neighbor = {
.mdir={.mid=1, .u.m=lfs.mroot.u.m}};
lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &left_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &right_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor);
// try removing our left entry
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS(
@@ -2816,8 +2816,8 @@ code = '''
sizeof(lfs.mroot.u.m)) == 0);
assert(right_neighbor.mdir.mid == -1);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &left_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &right_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor);
lfsr_unmount(&lfs) => 0;
'''
@@ -2845,8 +2845,8 @@ code = '''
.mdir={.mid=0, .u.m=lfs.mroot.u.m}};
lfsr_openedmdir_t right_neighbor = {
.mdir={.mid=1, .u.m=lfs.mroot.u.m}};
lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &left_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &right_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor);
// prepare mroot with a large attr so the next entry can not fit
uint8_t buffer[SIZE];
@@ -2895,8 +2895,8 @@ code = '''
assert(memcmp(&right_neighbor.mdir.u.m, &msibling.u.m,
sizeof(msibling.u.m)) == 0);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &left_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &right_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor);
lfsr_unmount(&lfs) => 0;
'''
@@ -2924,8 +2924,8 @@ code = '''
.mdir={.mid=0, .u.m=lfs.mroot.u.m}};
lfsr_openedmdir_t right_neighbor = {
.mdir={.mid=1, .u.m=lfs.mroot.u.m}};
lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &left_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &right_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor);
// create 2 large entries that needs to be uninlined and split
uint8_t buffer[SIZE];
@@ -2969,8 +2969,8 @@ code = '''
assert(memcmp(&right_neighbor.mdir.u.m, &msibling.u.m,
sizeof(msibling.u.m)) == 0);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &left_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &right_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor);
lfsr_unmount(&lfs) => 0;
'''
@@ -3025,8 +3025,8 @@ code = '''
.mdir={.mid=mdir.mid+0, .u.m=mdir.u.m}};
lfsr_openedmdir_t right_neighbor = {
.mdir={.mid=mdir.mid+2, .u.m=mdir.u.m}};
lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &left_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &right_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor);
// now add another large entry to the mdir, forcing a split
memset(buffer, 'e', SIZE);
@@ -3070,8 +3070,8 @@ code = '''
assert(memcmp(&right_neighbor.mdir.u.m, &msibling.u.m,
sizeof(msibling.u.m)) == 0);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &left_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &right_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor);
lfsr_unmount(&lfs) => 0;
'''
@@ -3101,8 +3101,8 @@ code = '''
.mdir={.mid=0, .u.m=lfs.mroot.u.m}};
lfsr_openedmdir_t right_neighbor = {
.mdir={.mid=1, .u.m=lfs.mroot.u.m}};
lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &left_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &right_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor);
// prepare mroot with an attr
uint8_t buffer[SIZE];
@@ -3136,8 +3136,8 @@ code = '''
assert(memcmp(&right_neighbor.mdir.u.m, &lfs.mroot.u.m,
sizeof(lfs.mroot.u.m)) == 0);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &left_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &right_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor);
lfsr_unmount(&lfs) => 0;
'''
@@ -3194,8 +3194,8 @@ code = '''
.mdir={.mid=mdir.mid+0, .u.m=mdir.u.m}};
lfsr_openedmdir_t right_neighbor = {
.mdir={.mid=mdir.mid+2, .u.m=mdir.u.m}};
lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &left_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &right_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor);
// force mdir to compact twice, this should relocate
lfsr_mdir_t old_mdir = mdir;
@@ -3229,8 +3229,8 @@ code = '''
assert(right_neighbor.mdir.mid == 0*lfsr_mleafweight(&lfs)+2);
assert(memcmp(&right_neighbor.mdir.u.m, &mdir.u.m, sizeof(mdir.u.m)) == 0);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &left_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &right_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor);
lfsr_unmount(&lfs) => 0;
'''
@@ -3296,8 +3296,8 @@ code = '''
&right_neighbor.mdir) => 0;
assert(right_neighbor.mdir.u.m.weight == 1);
lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &left_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &right_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor);
// cause middle mdir to split
lfsr_mtree_lookup(&lfs, 1*lfsr_mleafweight(&lfs)+1, &mdir) => 0;
@@ -3322,8 +3322,8 @@ code = '''
lfsr_mtree_lookup(&lfs, 3*lfsr_mleafweight(&lfs)+0, &mdir) => 0;
assert(memcmp(&right_neighbor.mdir.u.m, &mdir.u.m, sizeof(mdir.u.m)) == 0);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &left_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &right_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor);
lfsr_unmount(&lfs) => 0;
'''
@@ -3389,8 +3389,8 @@ code = '''
&right_neighbor.mdir) => 0;
assert(right_neighbor.mdir.u.m.weight == 1);
lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &left_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &right_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor);
lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor);
// cause middle mdir to drop
lfsr_mtree_lookup(&lfs, 1*lfsr_mleafweight(&lfs)+0, &mdir) => 0;
@@ -3410,8 +3410,8 @@ code = '''
lfsr_mtree_lookup(&lfs, 1*lfsr_mleafweight(&lfs)+0, &mdir) => 0;
assert(memcmp(&right_neighbor.mdir.u.m, &mdir.u.m, sizeof(mdir.u.m)) == 0);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &left_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &right_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor);
lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor);
lfsr_unmount(&lfs) => 0;
'''