rdonly: Dropped rbyd.eoff when LFS3_RDONLY

rbyd.eoff has the relatively unique property of only being useful in
rdwr mode. In rdonly mode we don't care where the next erased-state
starts because we're never going to use it.

Since rbyds are used everywhere, dropping rbyd.eoff has the potential to
save a significant amount of RAM.

---

At least on paper. We were using the field in lfs3_rbyd_fetch to keep
track of the most recent valid commit perturb/eoff, which was a bit
tricky to disentangle.

Disentangling lfs3_rbyd_fetch does add a bit of code to the default
build, but saves code, stack, and ctx in the rdonly mode:

                   code          stack          ctx
  rdonly before:  10640            816          524
  rdonly after:   10616 (-0.2%)    808 (-1.0%)  508 (-3.1%)

  default before: 37320           2280          636
  default after:  37352 (+0.1%)   2280 (+0.0%)  636 (+0.0%)

In theory we could ifdef the crap out of lfs3_rbyd_fetch to claw back
this code, but 1. 32 bytes of code is really not that much code, 2. the
more rdonly and default diverge the more likely rdonly breaks, and 3. I
think the new code is a bit more readable since it avoids masking
perturb/eoff together until the last minute.
This commit is contained in:
Christopher Haster
2025-06-05 19:41:43 -05:00
parent 7cc87a4fe6
commit 0096305968
2 changed files with 79 additions and 56 deletions
+77 -55
View File
@@ -2656,17 +2656,23 @@ static inline lfs3_size_t lfs3_rbyd_trunk(const lfs3_rbyd_t *rbyd) {
return rbyd->trunk & ~LFS3_RBYD_ISSHRUB; return rbyd->trunk & ~LFS3_RBYD_ISSHRUB;
} }
#ifndef LFS3_RDONLY
static inline bool lfs3_rbyd_isfetched(const lfs3_rbyd_t *rbyd) { static inline bool lfs3_rbyd_isfetched(const lfs3_rbyd_t *rbyd) {
return !lfs3_rbyd_trunk(rbyd) || rbyd->eoff; return !lfs3_rbyd_trunk(rbyd) || rbyd->eoff;
} }
#endif
#ifndef LFS3_RDONLY
static inline bool lfs3_rbyd_isperturb(const lfs3_rbyd_t *rbyd) { static inline bool lfs3_rbyd_isperturb(const lfs3_rbyd_t *rbyd) {
return rbyd->eoff & LFS3_RBYD_ISPERTURB; return rbyd->eoff & LFS3_RBYD_ISPERTURB;
} }
#endif
#ifndef LFS3_RDONLY
static inline lfs3_size_t lfs3_rbyd_eoff(const lfs3_rbyd_t *rbyd) { static inline lfs3_size_t lfs3_rbyd_eoff(const lfs3_rbyd_t *rbyd) {
return rbyd->eoff & ~LFS3_RBYD_ISPERTURB; return rbyd->eoff & ~LFS3_RBYD_ISPERTURB;
} }
#endif
static inline int lfs3_rbyd_cmp( static inline int lfs3_rbyd_cmp(
const lfs3_rbyd_t *a, const lfs3_rbyd_t *a,
@@ -2748,26 +2754,33 @@ static int lfs3_rbyd_fetch_(lfs3_t *lfs3,
// set up some initial state // set up some initial state
rbyd->blocks[0] = block; rbyd->blocks[0] = block;
rbyd->trunk = (trunk & LFS3_RBYD_ISSHRUB) | 0; rbyd->trunk = (trunk & LFS3_RBYD_ISSHRUB) | 0;
rbyd->weight = 0;
#ifndef LFS3_RDONLY
rbyd->eoff = 0; rbyd->eoff = 0;
#endif
// ignore the shrub bit here // ignore the shrub bit here
trunk &= ~LFS3_RBYD_ISSHRUB; trunk &= ~LFS3_RBYD_ISSHRUB;
// keep track of last commit off and perturb bit
lfs3_size_t eoff = 0;
bool perturb = false;
// checksum the revision count to get the cksum started // checksum the revision count to get the cksum started
uint32_t cksum = 0; uint32_t cksum_ = 0;
int err = lfs3_bd_cksum(lfs3, block, 0, -1, sizeof(uint32_t), int err = lfs3_bd_cksum(lfs3, block, 0, -1, sizeof(uint32_t),
&cksum); &cksum_);
if (err) { if (err) {
return err; return err;
} }
// temporary state until we validate a cksum // temporary state until we validate a cksum
uint32_t cksum_ = cksum; lfs3_size_t off_ = sizeof(uint32_t);
lfs3_size_t off = sizeof(uint32_t); uint32_t cksum__ = cksum_;
lfs3_size_t trunk_ = 0; lfs3_size_t trunk_ = 0;
lfs3_size_t trunk__ = 0; lfs3_size_t trunk__ = 0;
lfs3_rid_t weight = 0;
lfs3_rid_t weight_ = 0; lfs3_rid_t weight_ = 0;
lfs3_rid_t weight__ = 0;
#ifndef LFS3_RDONLY #ifndef LFS3_RDONLY
// assume unerased until proven otherwise // assume unerased until proven otherwise
@@ -2779,34 +2792,34 @@ static int lfs3_rbyd_fetch_(lfs3_t *lfs3,
uint32_t gcksumdelta_ = 0; uint32_t gcksumdelta_ = 0;
// scan tags, checking valid bits, cksums, etc // scan tags, checking valid bits, cksums, etc
while (off < lfs3->cfg->block_size while (off_ < lfs3->cfg->block_size
&& (!trunk || lfs3_rbyd_eoff(rbyd) <= trunk)) { && (!trunk || eoff <= trunk)) {
// read next tag // read next tag
lfs3_tag_t tag; lfs3_tag_t tag;
lfs3_rid_t weight__; lfs3_rid_t weight;
lfs3_size_t size; lfs3_size_t size;
lfs3_ssize_t d = lfs3_bd_readtag(lfs3, block, off, -1, lfs3_ssize_t d = lfs3_bd_readtag(lfs3, block, off_, -1,
&tag, &weight__, &size, &tag, &weight, &size,
&cksum_); &cksum__);
if (d < 0) { if (d < 0) {
if (d == LFS3_ERR_CORRUPT) { if (d == LFS3_ERR_CORRUPT) {
break; break;
} }
return d; return d;
} }
lfs3_size_t off_ = off + d; lfs3_size_t off__ = off_ + d;
// readtag should already check we're in-bounds // readtag should already check we're in-bounds
LFS3_ASSERT(lfs3_tag_isalt(tag) LFS3_ASSERT(lfs3_tag_isalt(tag)
|| off_ + size <= lfs3->cfg->block_size); || off__ + size <= lfs3->cfg->block_size);
// take care of cksum // take care of cksum
if (!lfs3_tag_isalt(tag)) { if (!lfs3_tag_isalt(tag)) {
// not an end-of-commit cksum // not an end-of-commit cksum
if (lfs3_tag_suptype(tag) != LFS3_TAG_CKSUM) { if (lfs3_tag_suptype(tag) != LFS3_TAG_CKSUM) {
// cksum the entry, hopefully leaving it in the cache // cksum the entry, hopefully leaving it in the cache
err = lfs3_bd_cksum(lfs3, block, off_, -1, size, err = lfs3_bd_cksum(lfs3, block, off__, -1, size,
&cksum_); &cksum__);
if (err) { if (err) {
if (err == LFS3_ERR_CORRUPT) { if (err == LFS3_ERR_CORRUPT) {
break; break;
@@ -2820,10 +2833,10 @@ static int lfs3_rbyd_fetch_(lfs3_t *lfs3,
tag == LFS3_TAG_ECKSUM)) { tag == LFS3_TAG_ECKSUM)) {
#ifndef LFS3_RDONLY #ifndef LFS3_RDONLY
err = lfs3_data_readecksum(lfs3, err = lfs3_data_readecksum(lfs3,
&LFS3_DATA_DISK(block, off_, &LFS3_DATA_DISK(block, off__,
// note this size is to make the hint do // note this size is to make the hint do
// what we want // what we want
lfs3->cfg->block_size - off_), lfs3->cfg->block_size - off__),
&ecksum_); &ecksum_);
if (err) { if (err) {
if (err == LFS3_ERR_CORRUPT) { if (err == LFS3_ERR_CORRUPT) {
@@ -2836,10 +2849,10 @@ static int lfs3_rbyd_fetch_(lfs3_t *lfs3,
// found gcksumdelta? save for later // found gcksumdelta? save for later
} else if (tag == LFS3_TAG_GCKSUMDELTA) { } else if (tag == LFS3_TAG_GCKSUMDELTA) {
err = lfs3_data_readle32(lfs3, err = lfs3_data_readle32(lfs3,
&LFS3_DATA_DISK(block, off_, &LFS3_DATA_DISK(block, off__,
// note this size is to make the hint do // note this size is to make the hint do
// what we want // what we want
lfs3->cfg->block_size - off_), lfs3->cfg->block_size - off__),
&gcksumdelta_); &gcksumdelta_);
if (err) { if (err) {
if (err == LFS3_ERR_CORRUPT) { if (err == LFS3_ERR_CORRUPT) {
@@ -2863,56 +2876,57 @@ static int lfs3_rbyd_fetch_(lfs3_t *lfs3,
} }
// check checksum // check checksum
uint32_t cksum__ = 0; uint32_t cksum___ = 0;
err = lfs3_bd_read(lfs3, block, off_, -1, err = lfs3_bd_read(lfs3, block, off__, -1,
&cksum__, sizeof(uint32_t)); &cksum___, sizeof(uint32_t));
if (err) { if (err) {
if (err == LFS3_ERR_CORRUPT) { if (err == LFS3_ERR_CORRUPT) {
break; break;
} }
return err; return err;
} }
cksum__ = lfs3_fromle32(&cksum__); cksum___ = lfs3_fromle32(&cksum___);
if (cksum_ != cksum__) { if (cksum__ != cksum___) {
// uh oh, checksums don't match // uh oh, checksums don't match
break; break;
} }
// save what we've found so far // save what we've found so far
rbyd->eoff eoff = off__ + size;
= ((lfs3_size_t)lfs3_tag_perturb(tag)
<< (8*sizeof(lfs3_size_t)-1))
| (off_ + size);
rbyd->cksum = cksum;
rbyd->trunk = (LFS3_RBYD_ISSHRUB & rbyd->trunk) | trunk_; rbyd->trunk = (LFS3_RBYD_ISSHRUB & rbyd->trunk) | trunk_;
rbyd->weight = weight; rbyd->weight = weight_;
#ifndef LFS3_RDONLY rbyd->cksum = cksum_;
ecksum = ecksum_;
ecksum_.cksize = -1;
#endif
if (gcksumdelta) { if (gcksumdelta) {
*gcksumdelta = gcksumdelta_; *gcksumdelta = gcksumdelta_;
} }
gcksumdelta_ = 0; gcksumdelta_ = 0;
// update perturb bit
perturb = lfs3_tag_perturb(tag);
#ifndef LFS3_RDONLY
rbyd->eoff
= ((lfs3_size_t)perturb << (8*sizeof(lfs3_size_t)-1))
| eoff;
ecksum = ecksum_;
ecksum_.cksize = -1;
#endif
// revert to canonical checksum and perturb if necessary // revert to canonical checksum and perturb if necessary
cksum_ = cksum cksum__ = cksum_ ^ ((perturb) ? LFS3_CRC32C_ODDZERO : 0);
^ ((lfs3_rbyd_isperturb(rbyd))
? LFS3_CRC32C_ODDZERO
: 0);
} }
} }
// found a trunk? // found a trunk?
if (lfs3_tag_istrunk(tag)) { if (lfs3_tag_istrunk(tag)) {
if (!(trunk && off > trunk && !trunk__)) { if (!(trunk && off_ > trunk && !trunk__)) {
// start of trunk? // start of trunk?
if (!trunk__) { if (!trunk__) {
// keep track of trunk's entry point // keep track of trunk's entry point
trunk__ = off; trunk__ = off_;
// reset weight // reset weight
weight_ = 0; weight__ = 0;
} }
// derive weight of the tree from alt pointers // derive weight of the tree from alt pointers
@@ -2921,14 +2935,14 @@ static int lfs3_rbyd_fetch_(lfs3_t *lfs3,
// may be overeagerly parsing an invalid commit, it's ok for // may be overeagerly parsing an invalid commit, it's ok for
// this to overflow/underflow as long as we throw it out later // this to overflow/underflow as long as we throw it out later
// on a bad cksum // on a bad cksum
weight_ += weight__; weight__ += weight;
// end of trunk? // end of trunk?
if (!lfs3_tag_isalt(tag)) { if (!lfs3_tag_isalt(tag)) {
// update trunk and weight, unless we are a shrub trunk // update trunk and weight, unless we are a shrub trunk
if (!lfs3_tag_isshrub(tag) || trunk__ == trunk) { if (!lfs3_tag_isshrub(tag) || trunk__ == trunk) {
trunk_ = trunk__; trunk_ = trunk__;
weight = weight_; weight_ = weight__;
} }
trunk__ = 0; trunk__ = 0;
} }
@@ -2937,18 +2951,15 @@ static int lfs3_rbyd_fetch_(lfs3_t *lfs3,
// update canonical checksum, xoring out any perturb // update canonical checksum, xoring out any perturb
// state, we don't want erased-state affecting our // state, we don't want erased-state affecting our
// canonical checksum // canonical checksum
cksum = cksum_ cksum_ = cksum__ ^ ((perturb) ? LFS3_CRC32C_ODDZERO : 0);
^ ((lfs3_rbyd_isperturb(rbyd))
? LFS3_CRC32C_ODDZERO
: 0);
} }
// skip data // skip data
if (!lfs3_tag_isalt(tag)) { if (!lfs3_tag_isalt(tag)) {
off_ += size; off__ += size;
} }
off = off_; off_ = off__;
} }
// no valid commits? // no valid commits?
@@ -2956,9 +2967,9 @@ static int lfs3_rbyd_fetch_(lfs3_t *lfs3,
return LFS3_ERR_CORRUPT; return LFS3_ERR_CORRUPT;
} }
#ifndef LFS3_RDONLY
// did we end on a valid commit? we may have erased-state // did we end on a valid commit? we may have erased-state
bool erased = false; bool erased = false;
#ifndef LFS3_RDONLY
if (ecksum.cksize != -1) { if (ecksum.cksize != -1) {
// check the erased-state checksum // check the erased-state checksum
err = lfs3_rbyd_ckecksum(lfs3, rbyd, &ecksum); err = lfs3_rbyd_ckecksum(lfs3, rbyd, &ecksum);
@@ -2969,21 +2980,23 @@ static int lfs3_rbyd_fetch_(lfs3_t *lfs3,
// found valid erased-state? // found valid erased-state?
erased = (err != LFS3_ERR_CORRUPT); erased = (err != LFS3_ERR_CORRUPT);
} }
#endif
// used eoff=-1 to indicate when there is no erased-state // used eoff=-1 to indicate when there is no erased-state
if (!erased) { if (!erased) {
rbyd->eoff = -1; rbyd->eoff = -1;
} }
#endif
#ifdef LFS3_DBGRBYDFETCHES #ifdef LFS3_DBGRBYDFETCHES
LFS3_DEBUG("Fetched rbyd 0x%"PRIx32".%"PRIx32" w%"PRId32", " LFS3_DEBUG("Fetched rbyd 0x%"PRIx32".%"PRIx32" w%"PRId32", "
"eoff %"PRId32", cksum %"PRIx32, "eoff %"PRId32", cksum %"PRIx32,
rbyd->blocks[0], lfs3_rbyd_trunk(rbyd), rbyd->blocks[0], lfs3_rbyd_trunk(rbyd),
rbyd->weight, rbyd->weight,
(lfs3_rbyd_eoff(rbyd) >= lfs3->cfg->block_size) LFS3_IFDEF_RDONLY(
? -1 -1,
: (lfs3_ssize_t)lfs3_rbyd_eoff(rbyd), (lfs3_rbyd_eoff(rbyd) >= lfs3->cfg->block_size)
? -1
: (lfs3_ssize_t)lfs3_rbyd_eoff(rbyd)),
rbyd->cksum); rbyd->cksum);
#endif #endif
@@ -4996,9 +5009,12 @@ static lfs3_data_t lfs3_data_frombranch(const lfs3_rbyd_t *branch,
static int lfs3_data_readbranch(lfs3_t *lfs3, static int lfs3_data_readbranch(lfs3_t *lfs3,
lfs3_data_t *data, lfs3_bid_t weight, lfs3_data_t *data, lfs3_bid_t weight,
lfs3_rbyd_t *branch) { lfs3_rbyd_t *branch) {
// setting off to 0 here will trigger asserts if we try to append #ifndef LFS3_RDONLY
// setting eoff to 0 here will trigger asserts if we try to append
// without fetching first // without fetching first
branch->eoff = 0; branch->eoff = 0;
#endif
branch->weight = weight; branch->weight = weight;
int err = lfs3_data_readleb128(lfs3, data, &branch->blocks[0]); int err = lfs3_data_readleb128(lfs3, data, &branch->blocks[0]);
@@ -5031,7 +5047,9 @@ static int lfs3_branch_fetch(lfs3_t *lfs3, lfs3_rbyd_t *branch,
branch->blocks[0] = block; branch->blocks[0] = block;
branch->trunk = trunk; branch->trunk = trunk;
branch->weight = weight; branch->weight = weight;
#ifndef LFS3_RDONLY
branch->eoff = 0; branch->eoff = 0;
#endif
branch->cksum = cksum; branch->cksum = cksum;
#ifdef LFS3_CKFETCHES #ifdef LFS3_CKFETCHES
@@ -6316,8 +6334,10 @@ static int lfs3_data_readshrub(lfs3_t *lfs3, lfs3_data_t *data,
lfs3_shrub_t *shrub) { lfs3_shrub_t *shrub) {
// copy the mdir block // copy the mdir block
shrub->blocks[0] = mdir->rbyd.blocks[0]; shrub->blocks[0] = mdir->rbyd.blocks[0];
#ifndef LFS3_RDONLY
// force estimate recalculation if we write to this shrub // force estimate recalculation if we write to this shrub
shrub->eoff = -1; shrub->eoff = -1;
#endif
int err = lfs3_data_readleb128(lfs3, data, &shrub->weight); int err = lfs3_data_readleb128(lfs3, data, &shrub->weight);
if (err) { if (err) {
@@ -6449,8 +6469,10 @@ static void lfs3_bshrub_init(lfs3_bshrub_t *bshrub) {
bshrub->shrub.weight = 0; bshrub->shrub.weight = 0;
bshrub->shrub.blocks[0] = -1; bshrub->shrub.blocks[0] = -1;
bshrub->shrub.trunk = 0; bshrub->shrub.trunk = 0;
#ifndef LFS3_RDONLY
// force estimate recalculation // force estimate recalculation
bshrub->shrub.eoff = -1; bshrub->shrub.eoff = -1;
#endif
} }
static inline bool lfs3_bshrub_isbnull(const lfs3_bshrub_t *bshrub) { static inline bool lfs3_bshrub_isbnull(const lfs3_bshrub_t *bshrub) {
+2 -1
View File
@@ -682,12 +682,13 @@ typedef struct lfs3_rbyd {
// sign(trunk)=0 => normal rbyd // sign(trunk)=0 => normal rbyd
// sign(trunk)=1 => shrub rbyd // sign(trunk)=1 => shrub rbyd
lfs3_size_t trunk; lfs3_size_t trunk;
// TODO can we actually get rid of eoff when LFS3_RDONLY? #ifndef LFS3_RDONLY
// sign(eoff) => perturb bit // sign(eoff) => perturb bit
// eoff=0, trunk=0 => not yet committed // eoff=0, trunk=0 => not yet committed
// eoff=0, trunk>0 => not yet fetched // eoff=0, trunk>0 => not yet fetched
// eoff>=block_size => rbyd not erased/needs compaction // eoff>=block_size => rbyd not erased/needs compaction
lfs3_size_t eoff; lfs3_size_t eoff;
#endif
uint32_t cksum; uint32_t cksum;
} lfs3_rbyd_t; } lfs3_rbyd_t;