Fixed issue with splitting metadata-pairs in full filesystem

Depending on your perspective, this may not be a necessary operation,
given that a nearly-full filesystem is already prone to ENOSPC errors,
especially a COW filesystem. However, splitting metadata-pairs can
happen in really unfortunate situations, such as removing files.

The solution here is to allow "overcompaction", that is, a compaction
without bounds checking to allow splitting. This unfortunately pushes
our metadata-pairs past their reasonable limit of saturation, which
means writes get exponentially costly. However it does allow littlefs to
continue working in extreme situations.
This commit is contained in:
Christopher Haster
2018-09-15 02:28:45 -05:00
parent 29b881017d
commit cafe6ab466

19
lfs.c
View File

@@ -1290,6 +1290,7 @@ static int lfs_dir_compact(lfs_t *lfs,
// setup compaction
bool splitted = false;
bool exhausted = false;
bool overcompacting = false;
struct lfs_commit commit;
commit.block = dir->pair[1];
@@ -1310,9 +1311,11 @@ commit:
// 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);
commit.end = lfs->cfg->block_size - 38;
if (!overcompacting) {
commit.end = lfs_min(commit.end,
lfs_alignup(lfs->cfg->block_size/2, lfs->cfg->prog_size));
}
if (!splitted) {
// increment revision count
@@ -1369,8 +1372,9 @@ commit:
0x003ff000, LFS_MKTAG(0, id, 0),
-LFS_MKTAG(0, begin, 0),
source, attrs);
if (err && !(splitted && err == LFS_ERR_NOSPC)) {
if (err == LFS_ERR_NOSPC) {
if (err && !(splitted && !overcompacting &&
err == LFS_ERR_NOSPC)) {
if (!overcompacting && err == LFS_ERR_NOSPC) {
goto split;
} else if (err == LFS_ERR_CORRUPT) {
goto relocate;
@@ -1457,6 +1461,11 @@ split:
lfs_mdir_t tail;
err = lfs_dir_alloc(lfs, &tail);
if (err) {
if (err == LFS_ERR_NOSPC) {
// No space to expand? Try overcompacting
overcompacting = true;
goto commit;
}
return err;
}