mirror of
https://github.com/eledio-devices/thirdparty-littlefs.git
synced 2025-11-02 00:38:28 +01:00
WIP Modified lfs_dir_compact to avoid redundant erases during split
This commit is contained in:
176
lfs.c
176
lfs.c
@@ -39,6 +39,9 @@ static int lfs_bd_read(lfs_t *lfs,
|
|||||||
void *buffer, lfs_size_t size) {
|
void *buffer, lfs_size_t size) {
|
||||||
uint8_t *data = buffer;
|
uint8_t *data = buffer;
|
||||||
LFS_ASSERT(block != 0xffffffff);
|
LFS_ASSERT(block != 0xffffffff);
|
||||||
|
if (off+size > lfs->cfg->block_size) {
|
||||||
|
return LFS_ERR_CORRUPT;
|
||||||
|
}
|
||||||
|
|
||||||
while (size > 0) {
|
while (size > 0) {
|
||||||
if (pcache && block == pcache->block &&
|
if (pcache && block == pcache->block &&
|
||||||
@@ -452,6 +455,7 @@ struct lfs_commit {
|
|||||||
|
|
||||||
lfs_off_t begin;
|
lfs_off_t begin;
|
||||||
lfs_off_t end;
|
lfs_off_t end;
|
||||||
|
lfs_off_t ack;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct lfs_diskoff {
|
struct lfs_diskoff {
|
||||||
@@ -503,6 +507,24 @@ static int32_t lfs_commit_get(lfs_t *lfs, lfs_block_t block, lfs_off_t off,
|
|||||||
return LFS_ERR_NOENT;
|
return LFS_ERR_NOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int lfs_commit_prog(lfs_t *lfs, struct lfs_commit *commit,
|
||||||
|
const void *buffer, lfs_size_t size) {
|
||||||
|
lfs_off_t skip = lfs_min(lfs_max(commit->ack, commit->off)
|
||||||
|
- commit->off, size);
|
||||||
|
int err = lfs_bd_prog(lfs,
|
||||||
|
&lfs->pcache, &lfs->rcache, false,
|
||||||
|
commit->block, commit->off + skip,
|
||||||
|
(const uint8_t*)buffer + skip, size - skip);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
commit->crc = lfs_crc32(commit->crc, buffer, size);
|
||||||
|
commit->off += size;
|
||||||
|
commit->ack = lfs_max(commit->off, commit->ack);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int lfs_commit_attrs(lfs_t *lfs, struct lfs_commit *commit,
|
static int lfs_commit_attrs(lfs_t *lfs, struct lfs_commit *commit,
|
||||||
uint16_t id, const struct lfs_attr *attrs);
|
uint16_t id, const struct lfs_attr *attrs);
|
||||||
|
|
||||||
@@ -532,21 +554,14 @@ static int lfs_commit_attr(lfs_t *lfs, struct lfs_commit *commit,
|
|||||||
|
|
||||||
// write out tag
|
// write out tag
|
||||||
uint32_t ntag = lfs_tole32((tag & 0x7fffffff) ^ commit->ptag);
|
uint32_t ntag = lfs_tole32((tag & 0x7fffffff) ^ commit->ptag);
|
||||||
commit->crc = lfs_crc32(commit->crc, &ntag, sizeof(ntag));
|
int err = lfs_commit_prog(lfs, commit, &ntag, sizeof(ntag));
|
||||||
int err = lfs_bd_prog(lfs,
|
|
||||||
&lfs->pcache, &lfs->rcache, false,
|
|
||||||
commit->block, commit->off, &ntag, sizeof(ntag));
|
|
||||||
if (err) {
|
if (err) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
commit->off += sizeof(ntag);
|
|
||||||
|
|
||||||
if (!(tag & 0x80000000)) {
|
if (!(tag & 0x80000000)) {
|
||||||
// from memory
|
// from memory
|
||||||
commit->crc = lfs_crc32(commit->crc, buffer, size);
|
err = lfs_commit_prog(lfs, commit, buffer, size);
|
||||||
err = lfs_bd_prog(lfs,
|
|
||||||
&lfs->pcache, &lfs->rcache, false,
|
|
||||||
commit->block, commit->off, buffer, size);
|
|
||||||
if (err) {
|
if (err) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -563,17 +578,13 @@ static int lfs_commit_attr(lfs_t *lfs, struct lfs_commit *commit,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
commit->crc = lfs_crc32(commit->crc, &dat, 1);
|
err = lfs_commit_prog(lfs, commit, &dat, 1);
|
||||||
err = lfs_bd_prog(lfs,
|
|
||||||
&lfs->pcache, &lfs->rcache, false,
|
|
||||||
commit->block, commit->off+i, &dat, 1);
|
|
||||||
if (err) {
|
if (err) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
commit->off += size;
|
|
||||||
commit->ptag = tag & 0x7fffffff;
|
commit->ptag = tag & 0x7fffffff;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -677,14 +688,12 @@ static int lfs_commit_crc(lfs_t *lfs, struct lfs_commit *commit) {
|
|||||||
|
|
||||||
// read erased state from next program unit
|
// read erased state from next program unit
|
||||||
uint32_t tag = 0;
|
uint32_t tag = 0;
|
||||||
if (off < lfs->cfg->block_size) {
|
|
||||||
int err = lfs_bd_read(lfs,
|
int err = lfs_bd_read(lfs,
|
||||||
&lfs->pcache, &lfs->rcache, lfs->cfg->block_size,
|
&lfs->pcache, &lfs->rcache, sizeof(tag),
|
||||||
commit->block, off, &tag, sizeof(tag));
|
commit->block, off, &tag, sizeof(tag));
|
||||||
if (err) {
|
if (err && err != LFS_ERR_CORRUPT) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// build crc tag
|
// build crc tag
|
||||||
tag = lfs_fromle32(tag);
|
tag = lfs_fromle32(tag);
|
||||||
@@ -697,7 +706,7 @@ static int lfs_commit_crc(lfs_t *lfs, struct lfs_commit *commit) {
|
|||||||
footer[0] = lfs_tole32(tag ^ commit->ptag);
|
footer[0] = lfs_tole32(tag ^ commit->ptag);
|
||||||
commit->crc = lfs_crc32(commit->crc, &footer[0], sizeof(footer[0]));
|
commit->crc = lfs_crc32(commit->crc, &footer[0], sizeof(footer[0]));
|
||||||
footer[1] = lfs_tole32(commit->crc);
|
footer[1] = lfs_tole32(commit->crc);
|
||||||
int err = lfs_bd_prog(lfs,
|
err = lfs_bd_prog(lfs,
|
||||||
&lfs->pcache, &lfs->rcache, false,
|
&lfs->pcache, &lfs->rcache, false,
|
||||||
commit->block, commit->off, &footer, sizeof(footer));
|
commit->block, commit->off, &footer, sizeof(footer));
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -824,12 +833,6 @@ static int32_t lfs_dir_fetchmatch(lfs_t *lfs,
|
|||||||
lfs_global_zero(&templocals);
|
lfs_global_zero(&templocals);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
// reached end of block
|
|
||||||
if (off+sizeof(uint32_t) >= lfs->cfg->block_size) {
|
|
||||||
dir->erased = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// extract next tag
|
// extract next tag
|
||||||
uint32_t tag;
|
uint32_t tag;
|
||||||
int err = lfs_bd_read(lfs,
|
int err = lfs_bd_read(lfs,
|
||||||
@@ -1076,16 +1079,42 @@ static int lfs_dir_compact(lfs_t *lfs,
|
|||||||
lfs_global_zero(&dir->locals);
|
lfs_global_zero(&dir->locals);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
// last complete id
|
// setup compaction
|
||||||
dir->count = end - begin;
|
bool splitted = false;
|
||||||
int16_t ack = -1;
|
|
||||||
bool exhausted = false;
|
bool exhausted = false;
|
||||||
|
|
||||||
|
struct lfs_commit commit;
|
||||||
|
commit.block = dir->pair[1];
|
||||||
|
commit.ack = 0;
|
||||||
|
|
||||||
|
commit:
|
||||||
|
// setup erase state
|
||||||
|
exhausted = false;
|
||||||
|
dir->count = end - begin;
|
||||||
|
int16_t ackid = -1;
|
||||||
|
|
||||||
|
// setup commit state
|
||||||
|
commit.off = 0;
|
||||||
|
commit.crc = 0xffffffff;
|
||||||
|
commit.ptag = 0;
|
||||||
|
|
||||||
|
// space is complicated, we need room for tail, crc, globals,
|
||||||
|
// cleanup delete, and we cap at half a block to give room
|
||||||
|
// for metadata updates
|
||||||
|
commit.begin = 0;
|
||||||
|
commit.end = lfs_min(
|
||||||
|
lfs_alignup(lfs->cfg->block_size/2, lfs->cfg->prog_size),
|
||||||
|
lfs->cfg->block_size - 38);
|
||||||
|
|
||||||
|
if (!splitted) {
|
||||||
// increment revision count
|
// increment revision count
|
||||||
dir->rev += 1;
|
dir->rev += 1;
|
||||||
if (lfs->cfg->block_cycles && dir->rev % lfs->cfg->block_cycles == 0) {
|
if (lfs->cfg->block_cycles &&
|
||||||
if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) {
|
dir->rev % lfs->cfg->block_cycles == 0) {
|
||||||
// we're writing too much to the superblock, should we expand?
|
if (lfs_pair_cmp(dir->pair,
|
||||||
|
(const lfs_block_t[2]){0, 1}) == 0) {
|
||||||
|
// we're writing too much to the superblock,
|
||||||
|
// should we expand?
|
||||||
lfs_ssize_t res = lfs_fs_size(lfs);
|
lfs_ssize_t res = lfs_fs_size(lfs);
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
return res;
|
return res;
|
||||||
@@ -1093,7 +1122,8 @@ static int lfs_dir_compact(lfs_t *lfs,
|
|||||||
|
|
||||||
// do we have enough space to expand?
|
// do we have enough space to expand?
|
||||||
if (res < lfs->cfg->block_count/2) {
|
if (res < lfs->cfg->block_count/2) {
|
||||||
LFS_DEBUG("Expanding superblock at rev %"PRIu32, dir->rev);
|
LFS_DEBUG("Expanding superblock at rev %"PRIu32,
|
||||||
|
dir->rev);
|
||||||
exhausted = true;
|
exhausted = true;
|
||||||
goto split;
|
goto split;
|
||||||
}
|
}
|
||||||
@@ -1112,14 +1142,11 @@ static int lfs_dir_compact(lfs_t *lfs,
|
|||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// write out header
|
// write out header
|
||||||
uint32_t crc = 0xffffffff;
|
|
||||||
uint32_t rev = lfs_tole32(dir->rev);
|
uint32_t rev = lfs_tole32(dir->rev);
|
||||||
crc = lfs_crc32(crc, &rev, sizeof(rev));
|
int err = lfs_commit_prog(lfs, &commit, &rev, sizeof(rev));
|
||||||
err = lfs_bd_prog(lfs,
|
|
||||||
&lfs->pcache, &lfs->rcache, false,
|
|
||||||
dir->pair[1], 0, &rev, sizeof(rev));
|
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err == LFS_ERR_CORRUPT) {
|
if (err == LFS_ERR_CORRUPT) {
|
||||||
goto relocate;
|
goto relocate;
|
||||||
@@ -1127,28 +1154,13 @@ static int lfs_dir_compact(lfs_t *lfs,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup compaction
|
|
||||||
struct lfs_commit commit = {
|
|
||||||
.block = dir->pair[1],
|
|
||||||
.off = sizeof(dir->rev),
|
|
||||||
.crc = crc,
|
|
||||||
.ptag = 0,
|
|
||||||
|
|
||||||
// space is complicated, we need room for tail, crc, globals,
|
|
||||||
// and we cap at half a block to give room for metadata updates
|
|
||||||
.begin = 0,
|
|
||||||
.end = lfs_min(
|
|
||||||
lfs_alignup(lfs->cfg->block_size/2, lfs->cfg->prog_size),
|
|
||||||
lfs->cfg->block_size - 34),
|
|
||||||
};
|
|
||||||
|
|
||||||
// commit with a move
|
// commit with a move
|
||||||
for (uint16_t id = begin; id < end; id++) {
|
for (uint16_t id = begin; id < end || commit.off < commit.ack; id++) {
|
||||||
err = lfs_commit_move(lfs, &commit,
|
err = lfs_commit_move(lfs, &commit,
|
||||||
0x003ff000, LFS_MKTAG(0, id, 0),
|
0x003ff000, LFS_MKTAG(0, id, 0),
|
||||||
0x003ff000, LFS_MKTAG(0, id - begin, 0),
|
0x003ff000, LFS_MKTAG(0, id - begin, 0),
|
||||||
source, attrs);
|
source, attrs);
|
||||||
if (err) {
|
if (err && !(splitted && err == LFS_ERR_NOSPC)) {
|
||||||
if (err == LFS_ERR_NOSPC) {
|
if (err == LFS_ERR_NOSPC) {
|
||||||
goto split;
|
goto split;
|
||||||
} else if (err == LFS_ERR_CORRUPT) {
|
} else if (err == LFS_ERR_CORRUPT) {
|
||||||
@@ -1157,12 +1169,25 @@ static int lfs_dir_compact(lfs_t *lfs,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
ack = id;
|
ackid = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// reopen reserved space at the end
|
// reopen reserved space at the end
|
||||||
commit.end = lfs->cfg->block_size - 8;
|
commit.end = lfs->cfg->block_size - 8;
|
||||||
|
|
||||||
|
if (ackid >= end) {
|
||||||
|
// extra garbage attributes were written out during split,
|
||||||
|
// need to clean up
|
||||||
|
err = lfs_commit_attr(lfs, &commit,
|
||||||
|
LFS_MKTAG(LFS_TYPE_DELETE, ackid, 0), NULL);
|
||||||
|
if (err) {
|
||||||
|
if (err == LFS_ERR_CORRUPT) {
|
||||||
|
goto relocate;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) {
|
if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) {
|
||||||
// move over (duplicate) superblock if we are root
|
// move over (duplicate) superblock if we are root
|
||||||
err = lfs_commit_move(lfs, &commit,
|
err = lfs_commit_move(lfs, &commit,
|
||||||
@@ -1178,8 +1203,8 @@ static int lfs_dir_compact(lfs_t *lfs,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!relocated) {
|
if (!relocated) {
|
||||||
// commit any globals, unless we're relocating, in which case our
|
// commit any globals, unless we're relocating,
|
||||||
// parent will steal our globals
|
// in which case our parent will steal our globals
|
||||||
err = lfs_commit_globals(lfs, &commit, &dir->locals);
|
err = lfs_commit_globals(lfs, &commit, &dir->locals);
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err == LFS_ERR_CORRUPT) {
|
if (err == LFS_ERR_CORRUPT) {
|
||||||
@@ -1222,8 +1247,13 @@ static int lfs_dir_compact(lfs_t *lfs,
|
|||||||
split:
|
split:
|
||||||
// commit no longer fits, need to split dir,
|
// commit no longer fits, need to split dir,
|
||||||
// drop caches and create tail
|
// drop caches and create tail
|
||||||
|
splitted = !exhausted;
|
||||||
|
if (lfs->pcache.block != 0xffffffff) {
|
||||||
|
commit.ack -= lfs->pcache.size;
|
||||||
lfs_cache_drop(lfs, &lfs->pcache);
|
lfs_cache_drop(lfs, &lfs->pcache);
|
||||||
if (!exhausted && ack < 0) {
|
}
|
||||||
|
|
||||||
|
if (!exhausted && ackid < 0) {
|
||||||
// If we can't fit in this block, we won't fit in next block
|
// If we can't fit in this block, we won't fit in next block
|
||||||
return LFS_ERR_NOSPC;
|
return LFS_ERR_NOSPC;
|
||||||
}
|
}
|
||||||
@@ -1234,25 +1264,26 @@ split:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tail.split = dir->split;
|
||||||
|
tail.tail[0] = dir->tail[0];
|
||||||
|
tail.tail[1] = dir->tail[1];
|
||||||
|
|
||||||
|
err = lfs_dir_compact(lfs, &tail, attrs, source, ackid+1, end);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
end = ackid+1;
|
||||||
|
dir->tail[0] = tail.pair[0];
|
||||||
|
dir->tail[1] = tail.pair[1];
|
||||||
|
dir->split = true;
|
||||||
|
|
||||||
if (exhausted) {
|
if (exhausted) {
|
||||||
lfs->root[0] = tail.pair[0];
|
lfs->root[0] = tail.pair[0];
|
||||||
lfs->root[1] = tail.pair[1];
|
lfs->root[1] = tail.pair[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
tail.split = dir->split;
|
goto commit;
|
||||||
tail.tail[0] = dir->tail[0];
|
|
||||||
tail.tail[1] = dir->tail[1];
|
|
||||||
|
|
||||||
err = lfs_dir_compact(lfs, &tail, attrs, source, ack+1, end);
|
|
||||||
if (err) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
end = ack+1;
|
|
||||||
dir->tail[0] = tail.pair[0];
|
|
||||||
dir->tail[1] = tail.pair[1];
|
|
||||||
dir->split = true;
|
|
||||||
continue;
|
|
||||||
|
|
||||||
relocate:
|
relocate:
|
||||||
// commit was corrupted, drop caches and prepare to relocate block
|
// commit was corrupted, drop caches and prepare to relocate block
|
||||||
@@ -1363,6 +1394,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
|
|||||||
|
|
||||||
.begin = dir->off,
|
.begin = dir->off,
|
||||||
.end = lfs->cfg->block_size - 8,
|
.end = lfs->cfg->block_size - 8,
|
||||||
|
.ack = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const lfs_mattr_t *a = attrs; a; a = a->next) {
|
for (const lfs_mattr_t *a = attrs; a; a = a->next) {
|
||||||
|
|||||||
Reference in New Issue
Block a user