Cleaned up reworked commit logic, deduplicated rbyd/attr things again

This attempts to clean up and deduplicate rbyd operations where
possible, without losing the cleaner logic introduced by the commit
rework.

Some tradeoffs were made:

- In btree merges, we append the split name after the compaction.

  This means the split name doesn't get compacted when we merge, but
  avoids making the merge compactions special cases.

- We never clean up vestigial names.

  This one bothers me, since it means we can end up with names that
  never get cleaned up. But then again, that's already true of any names
  that get pushed up in the btree inner nodes that aren't the leading
  btree entry.

  By never cleaning these up, all rbyd compactions in the system behave
  the same.

- We don't push gstate into the mroot during relocations.

  This would be a nice-to-have, but would require lfsr_mdir_commit__ to
  know if we are relocating or extending. And mdirs need to reserve space
  for gstate anyways, so it's not the end of the world to leave a bit
  of extra gstate around.

Also some attr-list operations are not deduplicated due to how special
they are:

- The writing of attrs in lfsr_mdir_commit__, this is where we adjust
  mids->rids and handle special internal attr.

  This is a pain, since we end up duplicating the attr-list range
  operations, but on the plus side keeps the special mdir attrs out of
  the rbyd layers, and saves a bit of RAM from the hot-path.

- The copying of config attrs during mroot extensions.

  This one is just tricky because we want to keep the config attrs, but
  not the gstate attrs or any custom attributes. An explicit compaction
  of only the subrange of config attrs gets the job done.

These changes get our code/RAM costs pretty much back where they
started:

                        code          stack
  before mdir rework:  20826           1744
  after mdir rework:   21434 (+2.8%)   1768 (+1.4%)
  after mdir cleanup:  20850 (+0.1%)   1736 (-0.5%)

It's interesting to note the slight tradeoff of code/RAM here (though
this is very close to the compiler noise floor) comes from the moving of
special mdir attr logic up into lfsr_mdir_commit__.

I wasn't expecting this, but it makes sense since this moves the special
attr handling out of the hot-path going through the mtree commit.
This commit is contained in:
Christopher Haster
2023-09-08 13:53:42 -05:00
parent fc937a7060
commit 17400c5c34
+245 -429
View File
@@ -2856,41 +2856,32 @@ failed:;
return err;
}
// append and consume any pending gstate
static int lfsr_rbyd_appendgdelta(lfs_t *lfs, lfsr_rbyd_t *rbyd) {
// need GRM delta?
if (!lfsr_grm_iszero(lfs->dgrm)) {
// calculate our delta
uint8_t grm_buf[LFSR_GRM_DSIZE];
memset(grm_buf, 0, LFSR_GRM_DSIZE);
lfsr_data_t data;
int err = lfsr_rbyd_lookup(lfs, rbyd, -1, LFSR_TAG_GRM, NULL, &data);
if (err && err != LFS_ERR_NOENT) {
return err;
}
if (err != LFS_ERR_NOENT) {
lfs_ssize_t grm_dsize = lfsr_data_read(lfs, &data,
grm_buf, LFSR_GRM_DSIZE);
if (grm_dsize < 0) {
return grm_dsize;
static int lfsr_rbyd_appendattrs(lfs_t *lfs, lfsr_rbyd_t *rbyd,
lfs_ssize_t start_rid, lfs_ssize_t end_rid,
const lfsr_attr_t *attrs, lfs_size_t attr_count) {
// append each tag to the tree
for (lfs_size_t i = 0; i < attr_count; i++) {
// don't write tags outside of the requested range
if (attrs[i].rid >= start_rid
// 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
&& (lfs_size_t)(attrs[i].rid + 1) <= (lfs_size_t)end_rid) {
int err = lfsr_rbyd_appendattr(lfs, rbyd,
attrs[i].rid - lfs_smax32(start_rid, 0),
attrs[i].tag, attrs[i].delta, attrs[i].data);
if (err) {
return err;
}
}
err = lfsr_grm_xor(lfs, grm_buf, LFSR_DATA(
&lfs->dgrm, LFSR_GRM_DSIZE));
if (err) {
return err;
// we need to make sure we keep start_rid/end_rid updated with
// weight changes
if (attrs[i].rid < start_rid) {
start_rid += attrs[i].delta;
}
// append to our rbyd, note this replaces the original delta
lfs_size_t size = lfsr_grm_size(grm_buf);
err = lfsr_rbyd_appendattr(lfs, rbyd, -1,
// opportunistically remove this tag if delta is all zero
(size == 0 ? LFSR_TAG_RM(GRM) : LFSR_TAG_GRM), 0,
LFSR_DATA(grm_buf, size));
if (err) {
return err;
if (attrs[i].rid < end_rid) {
end_rid += attrs[i].delta;
}
}
@@ -2972,6 +2963,40 @@ failed:;
return err;
}
static int lfsr_rbyd_appendcompactrbyd(lfs_t *lfs, lfsr_rbyd_t *rbyd_,
lfs_ssize_t start_rid, lfs_ssize_t end_rid,
const lfsr_rbyd_t *rbyd) {
// copy over tags in the rbyd in order
lfs_ssize_t rid = start_rid;
lfsr_tag_t tag = 0;
while (true) {
lfs_size_t weight;
lfsr_data_t data;
int err = lfsr_rbyd_lookupnext(lfs, rbyd,
rid, lfsr_tag_next(tag),
&rid, &tag, &weight, &data);
if (err && err != LFS_ERR_NOENT) {
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 (err == LFS_ERR_NOENT
|| (lfs_size_t)(rid + 1) > (lfs_size_t)end_rid) {
break;
}
// write the tag
err = lfsr_rbyd_appendcompactattr(lfs, rbyd_, tag, weight, data);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
}
return 0;
}
static int lfsr_rbyd_compact(lfs_t *lfs, lfsr_rbyd_t *rbyd) {
// must fetch before mutating!
LFS_ASSERT(lfsr_rbyd_isfetched(rbyd));
@@ -3076,6 +3101,47 @@ failed:;
return err;
}
// append and consume any pending gstate
static int lfsr_rbyd_appendgdelta(lfs_t *lfs, lfsr_rbyd_t *rbyd) {
// need GRM delta?
if (!lfsr_grm_iszero(lfs->dgrm)) {
// calculate our delta
uint8_t grm_buf[LFSR_GRM_DSIZE];
memset(grm_buf, 0, LFSR_GRM_DSIZE);
lfsr_data_t data;
int err = lfsr_rbyd_lookup(lfs, rbyd, -1, LFSR_TAG_GRM, NULL, &data);
if (err && err != LFS_ERR_NOENT) {
return err;
}
if (err != LFS_ERR_NOENT) {
lfs_ssize_t grm_dsize = lfsr_data_read(lfs, &data,
grm_buf, LFSR_GRM_DSIZE);
if (grm_dsize < 0) {
return grm_dsize;
}
}
err = lfsr_grm_xor(lfs, grm_buf, LFSR_DATA(
&lfs->dgrm, LFSR_GRM_DSIZE));
if (err) {
return err;
}
// append to our rbyd, note this replaces the original delta
lfs_size_t size = lfsr_grm_size(grm_buf);
err = lfsr_rbyd_appendattr(lfs, rbyd, -1,
// opportunistically remove this tag if delta is all zero
(size == 0 ? LFSR_TAG_RM(GRM) : LFSR_TAG_GRM), 0,
LFSR_DATA(grm_buf, size));
if (err) {
return err;
}
}
return 0;
}
// the following are mostly btree helpers, but since they operate on rbyds,
// exist in the rbyd namespace
@@ -3144,10 +3210,8 @@ static lfs_ssize_t lfsr_rbyd_estimate(lfs_t *lfs, const lfsr_rbyd_t *rbyd,
// note that we don't include -1 tags yet, -1 tags are always cleaned up
// during a split so they shouldn't affect the split_rid
//
lfs_ssize_t lower_rid = (start_rid < 0 ? 0 : start_rid);
lfs_ssize_t upper_rid = (end_rid < 0
? (lfs_ssize_t)rbyd->weight-1
: end_rid-1);
lfs_ssize_t lower_rid = lfs_smax32(start_rid, 0);
lfs_ssize_t upper_rid = lfs_min32(rbyd->weight, end_rid)-1;
lfs_size_t lower_dsize = 0;
lfs_size_t upper_dsize = 0;
@@ -3188,40 +3252,6 @@ static lfs_ssize_t lfsr_rbyd_estimate(lfs_t *lfs, const lfsr_rbyd_t *rbyd,
return dsize + lower_dsize + upper_dsize;
}
// TODO
//// determine if there are fewer than "cutoff" unique ids in the rbyd,
//// this is used to determine if the underlying rbyd is degenerate and can
//// be reverted to an inlined btree
////
//// note cutoff is expected to be quite small, <= 2, so we should make sure
//// to exit our traverse early
//static int lfsr_rbyd_isdegenerate(lfs_t *lfs, const lfsr_rbyd_t *rbyd,
// lfs_ssize_t cutoff) {
// // cutoff=-1 => no cutoff
// if (cutoff < 0) {
// return false;
// }
//
// // count ids until we exceed our cutoff
// lfs_ssize_t rid = -1;
// lfs_size_t count = 0;
// while (true) {
// int err = lfsr_rbyd_lookupnext(lfs, rbyd, rid+1, 0,
// &rid, NULL, NULL, NULL);
// if (err && err != LFS_ERR_NOENT) {
// return err;
// }
// if (err == LFS_ERR_NOENT) {
// return true;
// }
//
// count += 1;
// if (count > (lfs_size_t)cutoff) {
// return false;
// }
// }
//}
// some low-level name things
//
@@ -3688,12 +3718,10 @@ static int lfsr_btree_commit(lfs_t *lfs, lfsr_btree_t *btree,
}
// commit our attrs
for (lfs_size_t i = 0; i < attr_count; i++) {
err = lfsr_rbyd_appendattr(lfs, &rbyd, attrs[i].rid,
attrs[i].tag, attrs[i].delta, attrs[i].data);
if (err) {
return err;
}
err = lfsr_rbyd_appendattrs(lfs, &rbyd, -1, -1,
attrs, attr_count);
if (err) {
return err;
}
err = lfsr_rbyd_appendcksum(lfs, &rbyd);
@@ -3766,16 +3794,14 @@ static int lfsr_btree_commit(lfs_t *lfs, lfsr_btree_t *btree,
// erased bytes? note that the btree trunk field prevents this from
// interacting with other references to the rbyd
lfsr_rbyd_t rbyd_ = rbyd;
for (lfs_size_t i = 0; i < attr_count; i++) {
err = lfsr_rbyd_appendattr(lfs, &rbyd_, attrs[i].rid - bid,
attrs[i].tag, attrs[i].delta, attrs[i].data);
if (err && err != LFS_ERR_RANGE) {
// TODO wait should we also move if there is corruption here?
return err;
}
if (err) {
goto compact;
}
err = lfsr_rbyd_appendattrs(lfs, &rbyd_, bid, -1,
attrs, attr_count);
if (err && err != LFS_ERR_RANGE) {
// TODO wait should we also move if there is corruption here?
return err;
}
if (err) {
goto compact;
}
err = lfsr_rbyd_appendcksum(lfs, &rbyd_);
@@ -3916,27 +3942,10 @@ static int lfsr_btree_commit(lfs_t *lfs, lfsr_btree_t *btree,
}
// try to compact
lfs_ssize_t rid_ = 0;
lfsr_tag_t tag = 0;
while (true) {
lfs_size_t weight;
lfsr_data_t data;
err = lfsr_rbyd_lookupnext(lfs, &rbyd,
rid_, lfsr_tag_next(tag),
&rid_, &tag, &weight, &data);
if (err && err != LFS_ERR_NOENT) {
return err;
}
if (err == LFS_ERR_NOENT) {
break;
}
// write the tag
err = lfsr_rbyd_appendcompactattr(lfs, &rbyd_, tag, weight, data);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
err = lfsr_rbyd_appendcompactrbyd(lfs, &rbyd_, -1, -1, &rbyd);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
err = lfsr_rbyd_compact(lfs, &rbyd_);
@@ -3947,13 +3956,11 @@ static int lfsr_btree_commit(lfs_t *lfs, lfsr_btree_t *btree,
// append any pending attrs, it's up to upper
// layers to make sure these always fit
for (lfs_size_t i = 0; i < attr_count; i++) {
err = lfsr_rbyd_appendattr(lfs, &rbyd_, attrs[i].rid - bid,
attrs[i].tag, attrs[i].delta, attrs[i].data);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
err = lfsr_rbyd_appendattrs(lfs, &rbyd_, bid, -1,
attrs, attr_count);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
// finalize commit
@@ -3982,27 +3989,10 @@ static int lfsr_btree_commit(lfs_t *lfs, lfsr_btree_t *btree,
}
// copy over tags < split_rid
rid_ = 0;
tag = 0;
while (true) {
lfs_size_t weight;
lfsr_data_t data;
err = lfsr_rbyd_lookupnext(lfs, &rbyd,
rid_, lfsr_tag_next(tag),
&rid_, &tag, &weight, &data);
if (err && err != LFS_ERR_NOENT) {
return err;
}
if (err == LFS_ERR_NOENT || rid_ >= (lfs_ssize_t)split_rid) {
break;
}
// write the tag
err = lfsr_rbyd_appendcompactattr(lfs, &rbyd_, tag, weight, data);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
err = lfsr_rbyd_appendcompactrbyd(lfs, &rbyd_, -1, split_rid, &rbyd);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
err = lfsr_rbyd_compact(lfs, &rbyd_);
@@ -4015,23 +4005,11 @@ static int lfsr_btree_commit(lfs_t *lfs, lfsr_btree_t *btree,
//
// upper layers should make sure this can't fail by limiting the
// maximum commit size
lfs_ssize_t split_rid_ = split_rid;
for (lfs_size_t i = 0; i < attr_count; i++) {
if (attrs[i].rid - (lfs_ssize_t)bid < split_rid_) {
err = lfsr_rbyd_appendattr(lfs, &rbyd_,
attrs[i].rid - bid,
attrs[i].tag, attrs[i].delta, attrs[i].data);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
}
// we need to make sure we keep split_rid updated with
// weight changes
if (attrs[i].rid - (lfs_ssize_t)bid < split_rid_) {
split_rid_ += attrs[i].delta;
}
err = lfsr_rbyd_appendattrs(lfs, &rbyd_, bid, bid+split_rid,
attrs, attr_count);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
// finalize commit
@@ -4042,27 +4020,10 @@ static int lfsr_btree_commit(lfs_t *lfs, lfsr_btree_t *btree,
}
// copy over tags >= split_rid
rid_ = split_rid;
tag = 0;
while (true) {
lfs_size_t weight;
lfsr_data_t data;
err = lfsr_rbyd_lookupnext(lfs, &rbyd,
rid_, lfsr_tag_next(tag),
&rid_, &tag, &weight, &data);
if (err && err != LFS_ERR_NOENT) {
return err;
}
if (err == LFS_ERR_NOENT) {
break;
}
// write the tag
err = lfsr_rbyd_appendcompactattr(lfs, &sibling, tag, weight, data);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
err = lfsr_rbyd_appendcompactrbyd(lfs, &sibling, split_rid, -1, &rbyd);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
err = lfsr_rbyd_compact(lfs, &sibling);
@@ -4075,23 +4036,11 @@ static int lfsr_btree_commit(lfs_t *lfs, lfsr_btree_t *btree,
//
// upper layers should make sure this can't fail by limiting the
// maximum commit size
split_rid_ = split_rid;
for (lfs_size_t i = 0; i < attr_count; i++) {
if (attrs[i].rid - (lfs_ssize_t)bid >= split_rid_) {
err = lfsr_rbyd_appendattr(lfs, &sibling,
attrs[i].rid - bid - split_rid_,
attrs[i].tag, attrs[i].delta, attrs[i].data);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
}
// we need to make sure we keep split_rid updated with
// weight changes
if (attrs[i].rid - (lfs_ssize_t)bid < split_rid_) {
split_rid_ += attrs[i].delta;
}
err = lfsr_rbyd_appendattrs(lfs, &sibling, bid+split_rid, -1,
attrs, attr_count);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
// finalize commit
@@ -4187,27 +4136,22 @@ static int lfsr_btree_commit(lfs_t *lfs, lfsr_btree_t *btree,
}
// merge the siblings together
rid_ = 0;
tag = 0;
while (true) {
lfs_size_t weight;
lfsr_data_t data;
err = lfsr_rbyd_lookupnext(lfs, &rbyd,
rid_, lfsr_tag_next(tag),
&rid_, &tag, &weight, &data);
if (err && err != LFS_ERR_NOENT) {
return err;
}
if (err == LFS_ERR_NOENT) {
break;
}
err = lfsr_rbyd_appendcompactrbyd(lfs, &rbyd_, -1, -1, &rbyd);
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;
}
err = lfsr_rbyd_appendcompactrbyd(lfs, &rbyd_, -1, -1, &sibling);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
err = lfsr_rbyd_compact(lfs, &rbyd_);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
// bring in name that previously split the siblings
@@ -4218,63 +4162,31 @@ static int lfsr_btree_commit(lfs_t *lfs, lfsr_btree_t *btree,
return err;
}
lfs_size_t split_weight = 0;
if (lfsr_tag_suptype(split_tag) == LFSR_TAG_NAME) {
// lookup the weight of the previously-split entry
err = lfsr_rbyd_lookupnext(lfs, &sibling,
0, LFSR_TAG_NAME,
NULL, NULL, &split_weight, NULL);
// lookup the rid (weight really) of the previously-split entry
lfs_ssize_t split_rid;
err = lfsr_rbyd_lookupnext(lfs, &rbyd_,
rbyd.weight, LFSR_TAG_NAME,
&split_rid, NULL, NULL, NULL);
if (err) {
LFS_ASSERT(err != LFS_ERR_NOENT);
return err;
}
err = lfsr_rbyd_appendcompactattr(lfs, &rbyd_,
split_tag, split_weight, split_data);
err = lfsr_rbyd_appendattr(lfs, &rbyd_,
split_rid, split_tag, 0, split_data);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
}
rid_ = split_weight-1;
// TODO inclusive?
tag = LFSR_TAG_STRUCT-1;
while (true) {
lfs_size_t weight;
lfsr_data_t data;
err = lfsr_rbyd_lookupnext(lfs, &sibling,
rid_, lfsr_tag_next(tag),
&rid_, &tag, &weight, &data);
if (err && err != LFS_ERR_NOENT) {
return err;
}
if (err == LFS_ERR_NOENT) {
break;
}
// write the tag
err = lfsr_rbyd_appendcompactattr(lfs, &rbyd_, tag, weight, data);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
}
err = lfsr_rbyd_compact(lfs, &rbyd_);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
// append any pending attrs, it's up to upper
// layers to make sure these always fit
for (lfs_size_t i = 0; i < attr_count; i++) {
err = lfsr_rbyd_appendattr(lfs, &rbyd_, attrs[i].rid - bid,
attrs[i].tag, attrs[i].delta, attrs[i].data);
if (err) {
return err;
}
err = lfsr_rbyd_appendattrs(lfs, &rbyd_, bid, -1,
attrs, attr_count);
if (err) {
return err;
}
// finalize the commit
@@ -4935,52 +4847,6 @@ static int lfsr_mtree_seek(lfs_t *lfs, lfsr_mdir_t *mdir, lfs_off_t off) {
}
// low-level mdir attr handling, special attrs are handled here
static int lfsr_mdir_appendattr(lfs_t *lfs, lfsr_rbyd_t *rbyd,
lfs_ssize_t rid, lfsr_tag_t tag, lfs_ssize_t delta,
lfsr_data_t data) {
// this is a bit of a hack, but ignore any gstate tags here,
// these need to be handled specially by upper-layers
if (lfsr_tag_suptype(tag) == LFSR_TAG_GSTATE) {
// do nothing
return 0;
// move tags copy over any tags associated with the source's rid
} else if (lfsr_tag_suptype(tag) == LFSR_TAG_MOVE) {
// weighted moves are not supported
LFS_ASSERT(delta == 0);
const lfsr_mdir_t *mdir = (const lfsr_mdir_t*)data.u.b.buffer;
// skip the name tag, this is always replaced by upper layers
lfsr_tag_t tag = LFSR_TAG_STRUCT-1;
while (true) {
lfsr_data_t data;
int err = lfsr_mdir_lookupnext(lfs, mdir,
mdir->mid, lfsr_tag_next(tag),
&tag, &data);
if (err && err != LFS_ERR_NOENT) {
return err;
}
if (err == LFS_ERR_NOENT) {
break;
}
// append the attr
err = lfsr_rbyd_appendattr(lfs, rbyd, rid, tag, 0, data);
if (err) {
return err;
}
}
return 0;
// write out normal tags normally
} else {
LFS_ASSERT(!lfsr_tag_isinternal(tag));
return lfsr_rbyd_appendattr(lfs, rbyd, rid, tag, delta, data);
}
}
static int lfsr_mdir_alloc(lfs_t *lfs, lfsr_mdir_t *mdir, lfs_ssize_t mid) {
// assign the mid
mdir->mid = mid;
@@ -5083,8 +4949,9 @@ static int lfsr_mdir_swap(lfs_t *lfs, lfsr_mdir_t *mdir_,
// low-level mdir commit, does not handle mtree/mlist/compaction/etc
static int lfsr_mdir_commit__(lfs_t *lfs, lfsr_mdir_t *mdir,
lfs_ssize_t start_rid, lfs_ssize_t end_rid, bool gstate,
lfs_ssize_t start_rid, lfs_ssize_t end_rid,
const lfsr_attr_t *attrs, lfs_size_t attr_count) {
// TODO do we need to copy here?
// try to append a commit
lfsr_mdir_t mdir_ = *mdir;
// TODO handle this differently?
@@ -5104,11 +4971,51 @@ static int lfsr_mdir_commit__(lfs_t *lfs, lfsr_mdir_t *mdir,
// treat end_rid=-1 as "unbounded" in such a way that rid=-1
// is still included
&& (lfs_size_t)(rid + 1) <= (lfs_size_t)end_rid) {
int err = lfsr_mdir_appendattr(lfs, &mdir_.u.r.rbyd,
rid - lfs_smax32(start_rid, 0),
attrs[i].tag, attrs[i].delta, attrs[i].data);
if (err) {
return err;
// this is a bit of a hack, but ignore any gstate tags here,
// these need to be handled specially by upper-layers
if (lfsr_tag_suptype(attrs[i].tag) == LFSR_TAG_GSTATE) {
// do nothing
// move tags copy over any tags associated with the source's rid
} else if (lfsr_tag_suptype(attrs[i].tag) == LFSR_TAG_MOVE) {
// weighted moves are not supported
LFS_ASSERT(attrs[i].delta == 0);
const lfsr_mdir_t *mdir
= (const lfsr_mdir_t*)attrs[i].data.u.b.buffer;
// skip the name tag, this is always replaced by upper layers
lfsr_tag_t tag = LFSR_TAG_STRUCT-1;
while (true) {
lfsr_data_t data;
int err = lfsr_mdir_lookupnext(lfs, mdir,
mdir->mid, lfsr_tag_next(tag),
&tag, &data);
if (err && err != LFS_ERR_NOENT) {
return err;
}
if (err == LFS_ERR_NOENT) {
break;
}
// append the attr
err = lfsr_rbyd_appendattr(lfs, &mdir_.u.r.rbyd,
rid - lfs_smax32(start_rid, 0),
tag, 0, data);
if (err) {
return err;
}
}
// write out normal tags normally
} else {
LFS_ASSERT(!lfsr_tag_isinternal(attrs[i].tag));
int err = lfsr_rbyd_appendattr(lfs, &mdir_.u.r.rbyd,
rid - lfs_smax32(start_rid, 0),
attrs[i].tag, attrs[i].delta, attrs[i].data);
if (err) {
return err;
}
}
}
@@ -5138,7 +5045,7 @@ static int lfsr_mdir_commit__(lfs_t *lfs, lfsr_mdir_t *mdir,
lfs_cache_zero(lfs, &lfs->pcache);
} else {
if (gstate) {
if (start_rid == -1) {
// only append gstate if we are not dropping
int err = lfsr_rbyd_appendgdelta(lfs, &mdir_.u.r.rbyd);
if (err) {
@@ -5152,7 +5059,7 @@ static int lfsr_mdir_commit__(lfs_t *lfs, lfsr_mdir_t *mdir,
return err;
}
if (gstate) {
if (start_rid == -1) {
// success? gstate is committed
lfsr_fs_flushgdelta(lfs);
}
@@ -5163,13 +5070,30 @@ static int lfsr_mdir_commit__(lfs_t *lfs, lfsr_mdir_t *mdir,
return 0;
}
static int lfsr_mdir_compact__(lfs_t *lfs, lfsr_mdir_t *mdir_,
lfs_ssize_t start_rid, lfs_ssize_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);
if (err) {
return err;
}
err = lfsr_rbyd_compact(lfs, &mdir_->u.r.rbyd);
if (err) {
return err;
}
return 0;
}
// mid-level mdir commit, this one will at least compact on overflow
static int lfsr_mdir_commit_(lfs_t *lfs, lfsr_mdir_t *mdir,
lfs_ssize_t start_rid, lfs_ssize_t end_rid,
lfs_size_t *split_rid_,
const lfsr_attr_t *attrs, lfs_size_t attr_count) {
// try to commit
int err = lfsr_mdir_commit__(lfs, mdir, start_rid, end_rid, true,
int err = lfsr_mdir_commit__(lfs, mdir, start_rid, end_rid,
attrs, attr_count);
if (err && err != LFS_ERR_RANGE) {
return err;
@@ -5203,52 +5127,8 @@ compact:;
return err;
}
// consume any gstate on original rbyd, we need this even if we drop
// our mdir to avoid losing info
//
// if succesful, this should get immediately appended to our new commit
err = lfsr_fs_consumegdelta(lfs, mdir);
if (err) {
return err;
}
// copy over attrs
lfs_ssize_t rid = start_rid;
lfsr_tag_t tag = 0;
while (true) {
lfs_size_t weight;
lfsr_data_t data;
int err = lfsr_rbyd_lookupnext(lfs, &mdir->u.r.rbyd,
rid, lfsr_tag_next(tag),
&rid, &tag, &weight, &data);
if (err && err != LFS_ERR_NOENT) {
return err;
}
// 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 (err == LFS_ERR_NOENT
|| (lfs_size_t)(rid + 1) > (lfs_size_t)end_rid) {
break;
}
// TODO is this really the best way to do this?
// this is a bit of a hack, but ignore any gstate tags here,
// these need to be handled specially by upper-layers
if (lfsr_tag_suptype(tag) == LFSR_TAG_GSTATE) {
continue;
}
// 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;
}
}
err = lfsr_rbyd_compact(lfs, &mdir_.u.r.rbyd);
// compact our rbyd
err = lfsr_mdir_compact__(lfs, &mdir_, start_rid, end_rid, mdir);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
@@ -5259,14 +5139,6 @@ compact:;
// upper layers should make sure this can't fail by limiting the
// maximum commit size
err = lfsr_mdir_commit__(lfs, &mdir_, start_rid, end_rid,
// only append gstate if 1. we are not dropped, 2. we have not
// been relocated/split/etc, unless we are an mroot
//
// this pushes gstate up into the mroot when relocating, and
// helps avoid corner case issues when splitting/dropping
(lfsr_mdir_cmp(&mdir_, mdir) == 0
|| mdir_.mid == -1
|| lfsr_mtree_isinlined(lfs)),
attrs, attr_count);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
@@ -5374,38 +5246,13 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
return err;
}
// copy over attrs
lfs_ssize_t rid = 0;
lfsr_tag_t tag = 0;
while (true) {
lfs_size_t weight;
lfsr_data_t data;
int err = lfsr_rbyd_lookupnext(lfs, &mdir->u.r.rbyd,
rid, lfsr_tag_next(tag),
&rid, &tag, &weight, &data);
if (err && err != LFS_ERR_NOENT) {
return err;
}
if (err == LFS_ERR_NOENT || rid >= (lfs_ssize_t)split_rid) {
break;
}
// 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;
}
}
err = lfsr_rbyd_compact(lfs, &mdir_.u.r.rbyd);
err = lfsr_mdir_compact__(lfs, &mdir_, 0, split_rid, mdir);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
err = lfsr_mdir_commit__(lfs, &mdir_, 0, split_rid, false,
err = lfsr_mdir_commit__(lfs, &mdir_, 0, split_rid,
attrs, attr_count);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
@@ -5419,38 +5266,13 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
return err;
}
// copy over attrs
rid = split_rid;
tag = 0;
while (true) {
lfs_size_t weight;
lfsr_data_t data;
int err = lfsr_rbyd_lookupnext(lfs, &mdir->u.r.rbyd,
rid, lfsr_tag_next(tag),
&rid, &tag, &weight, &data);
if (err && err != LFS_ERR_NOENT) {
return err;
}
if (err == LFS_ERR_NOENT) {
break;
}
// write the tag
err = lfsr_rbyd_appendcompactattr(lfs, &msibling_.u.r.rbyd,
tag, weight, data);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
}
err = lfsr_rbyd_compact(lfs, &msibling_.u.r.rbyd);
err = lfsr_mdir_compact__(lfs, &msibling_, split_rid, -1, mdir);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
err = lfsr_mdir_commit__(lfs, &msibling_, split_rid, -1, false,
err = lfsr_mdir_commit__(lfs, &msibling_, split_rid, -1,
attrs, attr_count);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
@@ -5773,18 +5595,19 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
}
// copy only the config over
lfs_ssize_t rid = -1;
lfsr_tag_t tag = 0;
while (true) {
lfs_ssize_t rid;
lfs_size_t weight;
lfsr_data_t data;
int err = lfsr_rbyd_lookupnext(lfs, &mchildroot.u.r.rbyd,
rid, lfsr_tag_next(tag),
-1, lfsr_tag_next(tag),
&rid, &tag, &weight, &data);
if (err && err != LFS_ERR_NOENT) {
return err;
}
if (err == LFS_ERR_NOENT || rid >= 0 || tag >= LFSR_TAG_GSTATE) {
// TODO use suptype == CONFIG here?
if (err == LFS_ERR_NOENT || rid != -1 || tag >= LFSR_TAG_GSTATE) {
break;
}
@@ -5797,23 +5620,16 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
}
}
// and our new mroot
err = lfsr_rbyd_appendcompactattr(lfs, &mparentroot.u.r.rbyd,
LFSR_TAG_MROOT, 0,
LFSR_DATA_BUF(mchildroot_buf, mchildroot_dsize));
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
err = lfsr_rbyd_compact(lfs, &mparentroot.u.r.rbyd);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
// finalize commit
err = lfsr_rbyd_appendcksum(lfs, &mparentroot.u.r.rbyd);
// and commit our new mroot
err = lfsr_mdir_commit__(lfs, &mparentroot, -1, -1, LFSR_ATTRS(
LFSR_ATTR(-1,
WIDE(MROOT), 0, BUF(mchildroot_buf, mchildroot_dsize))));
if (err) {
return err;
}