mirror of
https://github.com/eledio-devices/thirdparty-littlefs.git
synced 2025-10-30 16:15:40 +01:00
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:
130
lfs.c
130
lfs.c
@@ -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;
|
||||
}
|
||||
|
||||
2
lfs.h
2
lfs.h
@@ -113,6 +113,8 @@ enum lfs_type {
|
||||
LFS_TYPE_SOFTTAIL = 0x600,
|
||||
LFS_TYPE_HARDTAIL = 0x601,
|
||||
LFS_TYPE_MOVESTATE = 0x7ff,
|
||||
LFS_TYPE_COMMITCRC = 0x502,
|
||||
LFS_TYPE_NPROGCRC = 0x5ff,
|
||||
|
||||
// internal chip sources
|
||||
LFS_FROM_NOOP = 0x000,
|
||||
|
||||
@@ -24,6 +24,7 @@ TAG_TYPES = {
|
||||
'gstate': (0x700, 0x700),
|
||||
'movestate': (0x7ff, 0x7ff),
|
||||
'crc': (0x700, 0x500),
|
||||
'nprogcrc': (0x7ff, 0x5ff),
|
||||
}
|
||||
|
||||
class Tag:
|
||||
@@ -125,9 +126,10 @@ class Tag:
|
||||
return ntag
|
||||
|
||||
def typerepr(self):
|
||||
if self.is_('crc') and getattr(self, 'crc', 0xffffffff) != 0xffffffff:
|
||||
if (self.is_('crc') and not self.is_('nprogcrc') and
|
||||
getattr(self, 'crc', 0xffffffff) != 0xffffffff):
|
||||
crc_status = ' (bad)'
|
||||
elif self.is_('crc') and getattr(self, 'erased', False):
|
||||
elif self.is_('nprogcrc') and getattr(self, 'erased', False):
|
||||
crc_status = ' (era)'
|
||||
else:
|
||||
crc_status = ''
|
||||
@@ -186,6 +188,8 @@ class MetadataPair:
|
||||
|
||||
self.rev, = struct.unpack('<I', block[0:4])
|
||||
crc = binascii.crc32(block[0:4])
|
||||
etag = None
|
||||
estate = None
|
||||
|
||||
# parse tags
|
||||
corrupt = False
|
||||
@@ -199,9 +203,7 @@ class MetadataPair:
|
||||
tag = Tag((int(tag) ^ ntag) & 0x7fffffff)
|
||||
tag.off = off + 4
|
||||
tag.data = block[off+4:off+tag.dsize]
|
||||
if tag.is_('crc 0x3'):
|
||||
crc = binascii.crc32(block[off:off+4*4], crc)
|
||||
elif tag.is_('crc'):
|
||||
if tag.is_('crc') and not tag.is_('nprogcrc'):
|
||||
crc = binascii.crc32(block[off:off+2*4], crc)
|
||||
else:
|
||||
crc = binascii.crc32(block[off:off+tag.dsize], crc)
|
||||
@@ -210,25 +212,29 @@ class MetadataPair:
|
||||
|
||||
self.all_.append(tag)
|
||||
|
||||
if tag.is_('crc'):
|
||||
if tag.is_('nprogcrc') and len(tag.data) == 8:
|
||||
etag = tag
|
||||
estate = struct.unpack('<II', tag.data)
|
||||
elif tag.is_('crc'):
|
||||
# is valid commit?
|
||||
if crc != 0xffffffff:
|
||||
corrupt = True
|
||||
if not corrupt:
|
||||
self.log = self.all_.copy()
|
||||
|
||||
# end of commit?
|
||||
if tag.is_('crc 0x3'):
|
||||
esize, ecrc = struct.unpack('<II', tag.data[:8])
|
||||
if estate:
|
||||
esize, ecrc = estate
|
||||
dcrc = 0xffffffff ^ binascii.crc32(block[off:off+esize])
|
||||
if ecrc == dcrc:
|
||||
tag.erased = True
|
||||
etag.erased = True
|
||||
corrupt = True
|
||||
elif tag.is_('crc 0x2'):
|
||||
elif not (tag.is_('crc 0x0') or tag.is_('crc 0x1')):
|
||||
corrupt = True
|
||||
|
||||
# reset tag parsing
|
||||
crc = 0
|
||||
etag = None
|
||||
estate = None
|
||||
|
||||
# find active ids
|
||||
self.ids = list(it.takewhile(
|
||||
@@ -305,7 +311,7 @@ class MetadataPair:
|
||||
f.write('\n')
|
||||
|
||||
for tag in tags:
|
||||
f.write("%08x: %08x %-13s %4s %4s" % (
|
||||
f.write("%08x: %08x %-14s %3s %4s" % (
|
||||
tag.off, tag,
|
||||
tag.typerepr(), tag.idrepr(), tag.sizerepr()))
|
||||
if truncate:
|
||||
|
||||
Reference in New Issue
Block a user