Merge pull request #242 from ARMmbed/fix-2048-erase-size

Fix issues with large prog sizes (prog_size > 1KiB)
This commit is contained in:
Christopher Haster
2019-07-28 21:35:22 -05:00
committed by GitHub
7 changed files with 148 additions and 68 deletions

111
lfs.c
View File

@@ -1227,65 +1227,85 @@ static int lfs_dir_commitattr(lfs_t *lfs, struct lfs_commit *commit,
static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
// align to program units
lfs_off_t off = lfs_alignup(commit->off + 2*sizeof(uint32_t),
const lfs_off_t off1 = commit->off + sizeof(lfs_tag_t);
const lfs_off_t end = lfs_alignup(off1 + sizeof(uint32_t),
lfs->cfg->prog_size);
// read erased state from next program unit
lfs_tag_t tag;
int err = lfs_bd_read(lfs,
NULL, &lfs->rcache, sizeof(tag),
commit->block, off, &tag, sizeof(tag));
if (err && err != LFS_ERR_CORRUPT) {
return err;
}
// create crc tags to fill up remainder of commit, note that
// padding is not crcd, which lets fetches skip padding but
// makes committing a bit more complicated
while (commit->off < end) {
lfs_off_t off = commit->off + sizeof(lfs_tag_t);
lfs_off_t noff = lfs_min(end - off, 0x3fe) + off;
if (noff < end) {
noff = lfs_min(noff, end - 2*sizeof(uint32_t));
}
// build crc tag
bool reset = ~lfs_frombe32(tag) >> 31;
tag = LFS_MKTAG(LFS_TYPE_CRC + reset, 0x3ff,
off - (commit->off+sizeof(lfs_tag_t)));
// read erased state from next program unit
lfs_tag_t tag = 0xffffffff;
int err = lfs_bd_read(lfs,
NULL, &lfs->rcache, sizeof(tag),
commit->block, noff, &tag, sizeof(tag));
if (err && err != LFS_ERR_CORRUPT) {
return err;
}
// write out crc
uint32_t footer[2];
footer[0] = lfs_tobe32(tag ^ commit->ptag);
commit->crc = lfs_crc(commit->crc, &footer[0], sizeof(footer[0]));
footer[1] = lfs_tole32(commit->crc);
err = lfs_bd_prog(lfs,
&lfs->pcache, &lfs->rcache, false,
commit->block, commit->off, &footer, sizeof(footer));
if (err) {
return err;
}
commit->off += sizeof(tag)+lfs_tag_size(tag);
commit->ptag = tag ^ (reset << 31);
// build crc tag
bool reset = ~lfs_frombe32(tag) >> 31;
tag = LFS_MKTAG(LFS_TYPE_CRC + reset, 0x3ff, noff - off);
// flush buffers
err = lfs_bd_sync(lfs, &lfs->pcache, &lfs->rcache, false);
if (err) {
return err;
}
// successful commit, check checksum to make sure
uint32_t crc = 0xffffffff;
lfs_size_t size = commit->off - lfs_tag_size(tag) - commit->begin;
for (lfs_off_t i = 0; i < size; i++) {
// leave it up to caching to make this efficient
uint8_t dat;
err = lfs_bd_read(lfs,
NULL, &lfs->rcache, size-i,
commit->block, commit->begin+i, &dat, 1);
// write out crc
uint32_t footer[2];
footer[0] = lfs_tobe32(tag ^ commit->ptag);
commit->crc = lfs_crc(commit->crc, &footer[0], sizeof(footer[0]));
footer[1] = lfs_tole32(commit->crc);
err = lfs_bd_prog(lfs,
&lfs->pcache, &lfs->rcache, false,
commit->block, commit->off, &footer, sizeof(footer));
if (err) {
return err;
}
crc = lfs_crc(crc, &dat, 1);
commit->off += sizeof(tag)+lfs_tag_size(tag);
commit->ptag = tag ^ (reset << 31);
commit->crc = 0xffffffff; // reset crc for next "commit"
}
// flush buffers
int err = lfs_bd_sync(lfs, &lfs->pcache, &lfs->rcache, false);
if (err) {
return err;
}
if (crc != commit->crc) {
return LFS_ERR_CORRUPT;
// successful commit, check checksums to make sure
lfs_off_t off = commit->begin;
lfs_off_t noff = off1;
while (off < end) {
uint32_t crc = 0xffffffff;
for (lfs_off_t i = off; i < noff+sizeof(uint32_t); i++) {
// leave it up to caching to make this efficient
uint8_t dat;
err = lfs_bd_read(lfs,
NULL, &lfs->rcache, noff+sizeof(uint32_t)-i,
commit->block, i, &dat, 1);
if (err) {
return err;
}
crc = lfs_crc(crc, &dat, 1);
}
// detected write error?
if (crc != 0) {
return LFS_ERR_CORRUPT;
}
// skip padding
off = lfs_min(end - noff, 0x3fe) + noff;
if (off < end) {
off = lfs_min(off, end - 2*sizeof(uint32_t));
}
noff = off + sizeof(uint32_t);
}
return 0;
@@ -1578,11 +1598,11 @@ static int lfs_dir_compact(lfs_t *lfs,
}
// successful compaction, swap dir pair to indicate most recent
LFS_ASSERT(commit.off % lfs->cfg->prog_size == 0);
lfs_pair_swap(dir->pair);
dir->count = end - begin;
dir->off = commit.off;
dir->etag = commit.ptag;
dir->erased = (dir->off % lfs->cfg->prog_size == 0);
// note we able to have already handled move here
if (lfs_gstate_hasmovehere(&lfs->gpending, dir->pair)) {
lfs_gstate_xormove(&lfs->gpending,
@@ -1749,6 +1769,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
}
// successful commit, update dir
LFS_ASSERT(commit.off % lfs->cfg->prog_size == 0);
dir->off = commit.off;
dir->etag = commit.ptag;