Switched to separate-tag encoding of forward-looking CRCs

Previously forward-looking CRCs was just two new CRC types, one for
commits with forward-looking CRCs, one without. These both contained the
CRC needed to complete the current commit (note that the commit CRC
must come last!).

         [--   32   --|--   32   --|--   32   --|--   32   --]
with:    [  crc3 tag  | nprog size |  nprog crc | commit crc ]
without: [  crc2 tag  | commit crc ]

This meant there had to be several checks for the two possible structure
sizes, messying up the implementation.

         [--   32   --|--   32   --|--   32   --|--   32   --|--   32   --]
with:    [nprogcrc tag| nprog size |  nprog crc | commit tag | commit crc ]
without: [ commit tag | commit crc ]

But we already have a mechanism for storing optional metadata! The
different metadata tags! So why not use a separate tage for the
forward-looking CRC, separate from the commit CRC?

I wasn't sure this would actually help that much, there are still
necessary conditions for wether or not a forward-looking CRC is there,
but in the end it simplified the code quite nicely, and resulted in a ~200 byte
code-cost saving.
This commit is contained in:
Christopher Haster
2020-12-06 23:54:55 -06:00
parent 7535795a44
commit 97b5d04bf4
3 changed files with 82 additions and 80 deletions

130
lfs.c
View File

@@ -330,6 +330,10 @@ static inline uint16_t lfs_tag_type1(lfs_tag_t tag) {
return (tag & 0x70000000) >> 20;
}
static inline uint16_t lfs_tag_type2(lfs_tag_t tag) {
return (tag & 0x78000000) >> 20;
}
static inline uint16_t lfs_tag_type3(lfs_tag_t tag) {
return (tag & 0x7ff00000) >> 20;
}
@@ -916,6 +920,9 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
bool tempsplit = false;
lfs_stag_t tempbesttag = besttag;
bool hasestate = false;
struct lfs_estate estate;
dir->rev = lfs_tole32(dir->rev);
uint32_t crc = lfs_crc(0xffffffff, &dir->rev, sizeof(dir->rev));
dir->rev = lfs_fromle32(dir->rev);
@@ -947,32 +954,12 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
ptag = tag;
if (lfs_tag_type1(tag) == LFS_TYPE_CRC) {
lfs_off_t noff = off + sizeof(tag);
struct lfs_estate estate;
if (lfs_tag_chunk(tag) == 3) {
err = lfs_bd_read(lfs,
NULL, &lfs->rcache, lfs->cfg->block_size,
dir->pair[0], noff, &estate, sizeof(estate));
if (err) {
if (err == LFS_ERR_CORRUPT) {
dir->erased = false;
break;
}
return err;
}
crc = lfs_crc(crc, &estate, sizeof(estate));
lfs_estate_fromle32(&estate);
noff += sizeof(estate);
}
if (lfs_tag_type2(tag) == LFS_TYPE_CRC) {
// check the crc attr
uint32_t dcrc;
err = lfs_bd_read(lfs,
NULL, &lfs->rcache, lfs->cfg->block_size,
dir->pair[0], noff, &dcrc, sizeof(dcrc));
dir->pair[0], off+sizeof(tag), &dcrc, sizeof(dcrc));
if (err) {
if (err == LFS_ERR_CORRUPT) {
dir->erased = false;
@@ -1002,10 +989,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
dir->tail[1] = temptail[1];
dir->split = tempsplit;
// reset crc
crc = 0xffffffff;
if (lfs_tag_chunk(tag) == 3) {
if (hasestate) {
// check if the next page is erased
//
// this may look inefficient, but since cache_size is
@@ -1041,15 +1025,19 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
dir->erased = true;
break;
}
} else if (lfs_tag_chunk(tag) == 2) {
// end of block commit
dir->erased = false;
break;
} else {
} else if (lfs_tag_chunk(tag) < 2) {
// for backwards compatibility we fall through here on
// unrecognized tags, leaving it up to the CRC to reject
// bad commits
} else {
// end of block commit
dir->erased = false;
break;
}
// reset crc
crc = 0xffffffff;
hasestate = false;
} else {
// crc the entry first, hopefully leaving it in the cache
err = lfs_bd_crc(lfs,
@@ -1085,7 +1073,8 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
err = lfs_bd_read(lfs,
NULL, &lfs->rcache, lfs->cfg->block_size,
dir->pair[0], off+sizeof(tag), &temptail, 8);
dir->pair[0], off+sizeof(tag),
&temptail, sizeof(temptail));
if (err) {
if (err == LFS_ERR_CORRUPT) {
dir->erased = false;
@@ -1093,6 +1082,19 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
}
}
lfs_pair_fromle32(temptail);
} else if (lfs_tag_type1(tag) == LFS_TYPE_NPROGCRC) {
err = lfs_bd_read(lfs,
NULL, &lfs->rcache, lfs->cfg->block_size,
dir->pair[0], off+sizeof(tag),
&estate, sizeof(estate));
if (err) {
if (err == LFS_ERR_CORRUPT) {
dir->erased = false;
break;
}
}
lfs_estate_fromle32(&estate);
hasestate = true;
}
// found a match for our fetcher?
@@ -1434,13 +1436,9 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
// - 4-word crc with estate to check following prog (middle of block)
// - 2-word crc with no following prog (end of block)
const lfs_off_t end = lfs_alignup(
lfs_min(commit->off + 4*sizeof(uint32_t), lfs->cfg->block_size),
lfs_min(commit->off + 5*sizeof(uint32_t), lfs->cfg->block_size),
lfs->cfg->prog_size);
// clamp erase size to tag size, this gives us the full tag as potential
// to intentionally invalidate erase CRCs
const lfs_size_t esize = lfs_max(lfs->cfg->prog_size, sizeof(lfs_tag_t));
lfs_off_t off1 = 0;
uint32_t crc1 = 0;
@@ -1454,23 +1452,12 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
noff = lfs_min(noff, end - 2*sizeof(uint32_t));
}
// build crc tag
// clamp erase size to tag size, this gives us the full tag as potential
// to intentionally invalidate erase CRCs
const lfs_size_t esize = lfs_max(
lfs->cfg->prog_size, sizeof(lfs_tag_t));
lfs_tag_t etag = 0;
lfs_tag_t tag = LFS_MKTAG(LFS_TYPE_CRC + 2, 0x3ff, noff - off);
lfs_size_t size = 2*sizeof(uint32_t);
struct {
lfs_tag_t tag;
union {
struct {
uint32_t crc;
} crc2;
struct {
struct lfs_estate estate;
uint32_t crc;
} crc3;
} u;
} data;
// space for estate? also only emit on last commit in padding commits
if (noff <= lfs->cfg->block_size - esize) {
// first we get a tag-worth of bits, this is so we can
// tweak our current tag to force future writes to be
@@ -1491,36 +1478,43 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
return err;
}
data.u.crc3.estate.size = esize;
data.u.crc3.estate.crc = ecrc;
lfs_estate_tole32(&data.u.crc3.estate);
// indicate we include estate
tag |= LFS_MKTAG(1, 0, 0);
size += sizeof(struct lfs_estate);
struct lfs_estate estate = {.size = esize, .crc = ecrc};
lfs_estate_tole32(&estate);
err = lfs_dir_commitattr(lfs, commit,
LFS_MKTAG(LFS_TYPE_NPROGCRC, 0x3ff, sizeof(estate)),
&estate);
if (err) {
return err;
}
}
data.tag = lfs_tobe32(tag ^ commit->ptag);
commit->crc = lfs_crc(commit->crc, &data, size-sizeof(uint32_t));
((uint32_t*)&data)[size/sizeof(uint32_t) - 1] = lfs_tole32(commit->crc);
// build crc state
off = commit->off + sizeof(lfs_tag_t);
struct {
lfs_tag_t tag;
uint32_t crc;
} cstate;
lfs_tag_t ctag = LFS_MKTAG(LFS_TYPE_COMMITCRC, 0x3ff, noff-off);
cstate.tag = lfs_tobe32(ctag ^ commit->ptag);
commit->crc = lfs_crc(commit->crc, &cstate.tag, sizeof(cstate.tag));
cstate.crc = lfs_tole32(commit->crc);
int err = lfs_bd_prog(lfs,
&lfs->pcache, &lfs->rcache, false,
commit->block, commit->off, &data, size);
commit->block, commit->off, &cstate, sizeof(cstate));
if (err) {
return err;
}
// keep track of non-padding checksum to verify
if (off1 == 0) {
//off1 = commit->off + sizeof(uint32_t);
off1 = commit->off + size-sizeof(uint32_t);
off1 = off;
crc1 = commit->crc;
}
commit->off += sizeof(tag)+lfs_tag_size(tag);
commit->off = noff;
// perturb valid bit?
commit->ptag = tag | (0x80000000 & ~lfs_frombe32(etag));
commit->ptag = ctag | (0x80000000 & ~lfs_frombe32(etag));
// reset crc for next commit
commit->crc = 0xffffffff;
}