Inlined lfsr_mtree_split_ into lfsr_mdir_commit, simplified a couple things

This really just plays out what the compiler is already doing, so the
code/stack cost is more-or-less unchanged.

We can at least deduplicate the mroot_ copying of the potentially-failed
mdir_.
This commit is contained in:
Christopher Haster
2023-07-31 02:06:57 -05:00
parent 08ab470c2b
commit 7387e7f2bb
+170 -196
View File
@@ -5514,6 +5514,7 @@ static int lfsr_mdir_commit_(lfs_t *lfs, lfsr_mdir_t *mdir,
// drop commit if weight goes to zero
if (mdir_.mid >= 0 && mdir_.rbyd.weight == 0) {
// TODO move this up into lfsr_mdir_commit?
// consume gstate so we don't lose any info
int err = lfsr_fs_consumegdelta(lfs, mdir);
if (err) {
@@ -5580,176 +5581,10 @@ compact:;
return 0;
}
// low-level mdir split, note this is really an operation on the mtree
static int lfsr_mtree_split_(lfs_t *lfs, lfsr_btree_t *mtree_,
lfsr_mdir_t *mdir_, lfsr_mdir_t *msibling_,
lfs_ssize_t start_id, lfs_ssize_t end_id,
const lfsr_mdir_t *mdir, lfs_size_t split_id,
const lfsr_attr_t *attrs, lfs_size_t attr_count) {
// if we're the mroot, create a new mtree, assume the upper layers
// will take care of grafting our mtree into the mroot as needed
lfs_ssize_t mid = mdir->mid;
if (mid == LFSR_MID_MROOT) {
mid = 0;
// create a null entry in our btree first. don't worry! thanks
// to inlining this doesn't allocate anything yet
//
// the reason for this is twofold:
//
// 1. it makes it so the split logic is the same whether or not
// we're uninlining
// 2. it makes it so we can actually split, lfsr_btree_split
// currently doesn't support an empty tree
//
int err = lfsr_btree_push(lfs, mtree_, 0, LFSR_TAG_MDIR, 1,
LFSR_DATA_NULL);
if (err) {
return err;
}
// if we're not the mroot, we need to consume the gstate so
// we don't lose any info during the split
//
// we do this here so we don't have to worry about corner cases
// with dropping mdirs during a split
} else {
int err = lfsr_fs_consumegdelta(lfs, mdir_);
if (err) {
return err;
}
}
// compact into new mdir tags < split_id
int err = lfsr_mdir_compact_(lfs, mdir_, mid, start_id, split_id,
mdir, attrs, attr_count, NULL, 0);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
// compact into new mdir tags >= split_id
err = lfsr_mdir_compact_(lfs, msibling_, mid, split_id, end_id,
mdir, attrs, attr_count, NULL, 0);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
LFS_DEBUG("Splitting mdir %"PRId32" 0x{%"PRIx32",%"PRIx32"} "
"-> 0x{%"PRIx32",%"PRIx32"}"
", 0x{%"PRIx32",%"PRIx32"}",
mdir->mid,
mdir->rbyd.block, mdir->redund_block,
mdir_->rbyd.block, mdir_->redund_block,
msibling_->rbyd.block, msibling_->redund_block);
// because of defered commits, both children can still be reduced
// to zero, need to catch this here
// both siblings reduced to zero
if (mdir_->rbyd.weight == 0 && msibling_->rbyd.weight == 0) {
LFS_DEBUG("Dropping mdir %"PRId32" 0x{%"PRIx32",%"PRIx32"}",
mdir_->mid,
mdir_->rbyd.block, mdir_->redund_block);
LFS_DEBUG("Dropping mdir %"PRId32" 0x{%"PRIx32",%"PRIx32"}",
msibling_->mid,
msibling_->rbyd.block, msibling_->redund_block);
mdir_->rbyd.trunk = 0;
msibling_->rbyd.trunk = 0;
// update our mtree
int err = lfsr_btree_pop(lfs, mtree_, mid);
if (err) {
return err;
}
// one sibling reduced to zero
} else if (mdir_->rbyd.weight == 0) {
LFS_DEBUG("Dropping mdir %"PRId32" 0x{%"PRIx32",%"PRIx32"}",
mdir_->mid,
mdir_->rbyd.block, mdir_->redund_block);
mdir_->rbyd.trunk = 0;
// update our mtree
uint8_t buf[LFSR_MPTR_DSIZE];
lfs_ssize_t d = lfsr_mdir_todisk(lfs, msibling_, buf);
if (d < 0) {
return d;
}
int err = lfsr_btree_set(lfs, mtree_, mid, LFSR_TAG_MDIR, 1,
LFSR_DATA_BUF(buf, d));
if (err) {
return err;
}
// other sibling reduced to zero
} else if (msibling_->rbyd.weight == 0) {
LFS_DEBUG("Dropping mdir %"PRId32" 0x{%"PRIx32",%"PRIx32"}",
msibling_->mid,
msibling_->rbyd.block, msibling_->redund_block);
msibling_->rbyd.trunk = 0;
// update our mtree
uint8_t buf[LFSR_MPTR_DSIZE];
lfs_ssize_t d = lfsr_mdir_todisk(lfs, mdir_, buf);
if (d < 0) {
return d;
}
int err = lfsr_btree_set(lfs, mtree_, mid, LFSR_TAG_MDIR, 1,
LFSR_DATA_BUF(buf, d));
if (err) {
return err;
}
// no siblings reduced to zero
} else {
// adjust our sibling's mid, do this here in case other sibling
// was dropped
msibling_->mid += 1;
// update out mtree
// lookup first name in sibling to use as the split name
//
// note we need to do this after playing out pending attrs in
// case they introduce a new name!
lfsr_tag_t stag;
lfsr_data_t sdata;
int err = lfsr_mdir_lookupnext(lfs, msibling_, 0, LFSR_TAG_NAME,
NULL, &stag, &sdata);
if (err) {
LFS_ASSERT(err != LFS_ERR_NOENT);
return err;
}
uint8_t buf1[LFSR_MPTR_DSIZE];
lfs_ssize_t d1 = lfsr_mdir_todisk(lfs, mdir_, buf1);
if (d1 < 0) {
return d1;
}
uint8_t buf2[LFSR_MPTR_DSIZE];
lfs_ssize_t d2 = lfsr_mdir_todisk(lfs, msibling_, buf2);
if (d2 < 0) {
return d2;
}
err = lfsr_btree_split(lfs, mtree_, mid,
(lfsr_tag_suptype(stag) == LFSR_TAG_NAME
? sdata
: LFSR_DATA_NULL),
LFSR_TAG_MDIR, 1, LFSR_DATA_BUF(buf1, d1),
LFSR_TAG_MDIR, 1, LFSR_DATA_BUF(buf2, d2));
if (err) {
return err;
}
}
return 0;
}
// high-level mdir commit
//
// this is also responsible for updating any opened mdirs, lfs_t, gstate, etc
//
static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, lfs_ssize_t *rid,
const lfsr_attr_t *attrs, lfs_size_t attr_count) {
LFS_ASSERT(mdir->mid != LFSR_MID_RM);
@@ -5785,39 +5620,181 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, lfs_ssize_t *rid,
}
// handle possible mtree updates, this gets a bit messy
lfsr_mdir_t mroot_ = lfs->mroot;
lfsr_btree_t mtree_ = lfs->mtree;
//
// note we need to make sure mroot_ is the most recent version of the
// mroot here, so failed commits are propagated correctly
//
// TODO wait, do we need to update lfs->mroot and mdir eagerly
// for the same reason?
lfsr_mdir_t mroot_ = (mdir->mid == LFSR_MID_MROOT ? mdir_ : lfs->mroot);
lfsr_mdir_t msibling_ = {.mid=LFSR_MID_RM, .rbyd.weight = 0};
lfsr_btree_t mtree_ = lfs->mtree;
bool dirtymroot = false;
bool dirtymtree = false;
// need to split?
if (err == LFS_ERR_RANGE) {
// inlined in mroot?
//
// we need to uninline before we split, and it's possible uninlining
// makes the mdir small enough that we don't even need to split
// if we're the mroot, create a new mtree, assume the upper layers
// will take care of grafting our mtree into the mroot as needed
if (mdir->mid == LFSR_MID_MROOT) {
// wait, not inlined? this shouldn't happen, most likely too many
// attributes in mroot
LFS_ASSERT(lfsr_btree_weight(&mtree_) == 0);
// TODO wait, do we need to update lfs->mroot and mdir eagerly
// for the same reason?
// Create a null entry in our btree first. Don't worry! Thanks
// to inlining this doesn't allocate anything yet.
//
// we need to update the mroot to track that a prog failed
mroot_ = mdir_;
// The reason for this is twofold:
//
// 1. It makes it so the split logic is the same whether or not
// we're uninlining.
//
// 2. It makes it so we can actually split, lfsr_btree_split
// currently doesn't support an empty tree.
//
LFS_ASSERT(lfsr_btree_weight(&mtree_) == 0);
int err = lfsr_btree_push(lfs, &mtree_, 0, LFSR_TAG_MDIR, 1,
LFSR_DATA_NULL);
if (err) {
return err;
}
// if we're not the mroot, we need to consume the gstate so
// we don't lose any info during the split
//
// we do this here so we don't have to worry about corner cases
// with dropping mdirs during a split
} else {
int err = lfsr_fs_consumegdelta(lfs, &mdir_);
if (err) {
return err;
}
}
// let lfsr_mtree_split_ do most of the work
err = lfsr_mtree_split_(lfs, &mtree_,
&mdir_, &msibling_, 0, -1,
mdir, split_id,
attrs, attr_count);
// compact into new mdir tags < split_id
lfs_ssize_t mid = lfs_smax32(mdir->mid, 0);
int err = lfsr_mdir_compact_(lfs, &mdir_, mid, 0, split_id,
mdir, attrs, attr_count, NULL, 0);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
// compact into new mdir tags >= split_id
err = lfsr_mdir_compact_(lfs, &msibling_, mid, split_id, -1,
mdir, attrs, attr_count, NULL, 0);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
LFS_DEBUG("Splitting mdir %"PRId32" 0x{%"PRIx32",%"PRIx32"} "
"-> 0x{%"PRIx32",%"PRIx32"}"
", 0x{%"PRIx32",%"PRIx32"}",
mdir->mid,
mdir->rbyd.block, mdir->redund_block,
mdir_.rbyd.block, mdir_.redund_block,
msibling_.rbyd.block, msibling_.redund_block);
// because of defered commits, both children can still be reduced
// to zero, need to catch this here
// both siblings reduced to zero
if (mdir_.rbyd.weight == 0 && msibling_.rbyd.weight == 0) {
LFS_DEBUG("Dropping mdir %"PRId32" 0x{%"PRIx32",%"PRIx32"}",
mdir_.mid,
mdir_.rbyd.block, mdir_.redund_block);
LFS_DEBUG("Dropping mdir %"PRId32" 0x{%"PRIx32",%"PRIx32"}",
msibling_.mid,
msibling_.rbyd.block, msibling_.redund_block);
mdir_.rbyd.trunk = 0;
msibling_.rbyd.trunk = 0;
// update our mtree
int err = lfsr_btree_pop(lfs, &mtree_, mid);
if (err) {
return err;
}
// one sibling reduced to zero
} else if (mdir_.rbyd.weight == 0) {
LFS_DEBUG("Dropping mdir %"PRId32" 0x{%"PRIx32",%"PRIx32"}",
mdir_.mid,
mdir_.rbyd.block, mdir_.redund_block);
mdir_.rbyd.trunk = 0;
// update our mtree
uint8_t buf[LFSR_MPTR_DSIZE];
lfs_ssize_t d = lfsr_mdir_todisk(lfs, &msibling_, buf);
if (d < 0) {
return d;
}
int err = lfsr_btree_set(lfs, &mtree_, mid, LFSR_TAG_MDIR, 1,
LFSR_DATA_BUF(buf, d));
if (err) {
return err;
}
// other sibling reduced to zero
} else if (msibling_.rbyd.weight == 0) {
LFS_DEBUG("Dropping mdir %"PRId32" 0x{%"PRIx32",%"PRIx32"}",
msibling_.mid,
msibling_.rbyd.block, msibling_.redund_block);
msibling_.rbyd.trunk = 0;
// update our mtree
uint8_t buf[LFSR_MPTR_DSIZE];
lfs_ssize_t d = lfsr_mdir_todisk(lfs, &mdir_, buf);
if (d < 0) {
return d;
}
int err = lfsr_btree_set(lfs, &mtree_, mid, LFSR_TAG_MDIR, 1,
LFSR_DATA_BUF(buf, d));
if (err) {
return err;
}
// no siblings reduced to zero
} else {
// adjust our sibling's mid, do this here in case other sibling
// was dropped
msibling_.mid += 1;
// update out mtree
// lookup first name in sibling to use as the split name
//
// note we need to do this after playing out pending attrs in
// case they introduce a new name!
lfsr_tag_t stag;
lfsr_data_t sdata;
int err = lfsr_mdir_lookupnext(lfs, &msibling_, 0, LFSR_TAG_NAME,
NULL, &stag, &sdata);
if (err) {
LFS_ASSERT(err != LFS_ERR_NOENT);
return err;
}
uint8_t buf1[LFSR_MPTR_DSIZE];
lfs_ssize_t d1 = lfsr_mdir_todisk(lfs, &mdir_, buf1);
if (d1 < 0) {
return d1;
}
uint8_t buf2[LFSR_MPTR_DSIZE];
lfs_ssize_t d2 = lfsr_mdir_todisk(lfs, &msibling_, buf2);
if (d2 < 0) {
return d2;
}
err = lfsr_btree_split(lfs, &mtree_, mid,
(lfsr_tag_suptype(stag) == LFSR_TAG_NAME
? sdata
: LFSR_DATA_NULL),
LFSR_TAG_MDIR, 1, LFSR_DATA_BUF(buf1, d1),
LFSR_TAG_MDIR, 1, LFSR_DATA_BUF(buf2, d2));
if (err) {
return err;
}
}
dirtymtree = true;
// mdir reduced to zero? need to drop?
@@ -5828,7 +5805,7 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, lfs_ssize_t *rid,
mdir_.rbyd.trunk = 0;
// update our mtree
err = lfsr_btree_pop(lfs, &mtree_, mdir->mid);
int err = lfsr_btree_pop(lfs, &mtree_, mdir->mid);
if (err) {
return err;
}
@@ -5839,7 +5816,8 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, lfs_ssize_t *rid,
} else if (lfsr_mdir_cmp(mdir, &mdir_) != 0) {
// relocate mroot
if (mdir->mid == LFSR_MID_MROOT) {
mroot_ = mdir_;
// if we're relocating our root, just mark the root as dirty
// and let our dirtymroot code handle this
dirtymroot = true;
// relocate a normal mdir
@@ -5857,7 +5835,7 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, lfs_ssize_t *rid,
return d;
}
err = lfsr_btree_set(lfs, &mtree_, mdir->mid, LFSR_TAG_MDIR, 1,
int err = lfsr_btree_set(lfs, &mtree_, mdir->mid, LFSR_TAG_MDIR, 1,
LFSR_DATA_BUF(buf, d));
if (err) {
return err;
@@ -5865,10 +5843,6 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, lfs_ssize_t *rid,
dirtymtree = true;
}
// update the root
} else if (mdir->mid == LFSR_MID_MROOT) {
mroot_ = mdir_;
}
// before we continue we need to update our grm in case of splits/drops