Added revision sum as tracking for filesystem stability

The idea is pretty simple: The revision count for each metadata block
only really needs 3 distinct states. The additional bit-width of the
revision count is available for other information. In this case, we can
sum all revision counts in the filesystem to encode state that can be
updated for free during any metadata block update.

The obvious state to store first is if the filesystem is in a bad state
that requires a deorphan step.

The allows us to sidestep the expensive O(n^2) operation for the O(n)
sum in the much more common case.

This patch adds the following rule:
If the sum of all revision count is odd, the filesystem was caught
mid-operation and must be deorphaned.
This commit is contained in:
Christopher Haster
2017-10-10 01:47:23 -05:00
parent 539409e2fb
commit b24ddac95e
2 changed files with 41 additions and 11 deletions

49
lfs.c
View File

@@ -358,9 +358,9 @@ static int lfs_dir_alloc(lfs_t *lfs, lfs_dir_t *dir) {
if (err) { if (err) {
return err; return err;
} }
lfs->sum += dir->d.rev;
// set defaults // set defaults
dir->d.rev += 1;
dir->d.size = sizeof(dir->d)+4; dir->d.size = sizeof(dir->d)+4;
dir->d.tail[0] = -1; dir->d.tail[0] = -1;
dir->d.tail[1] = -1; dir->d.tail[1] = -1;
@@ -431,8 +431,10 @@ struct lfs_region {
static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir, static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir,
const struct lfs_region *regions, int count) { const struct lfs_region *regions, int count) {
// increment revision count // increment rev such that, even == stable, odd == unstable
dir->d.rev += 1; uint32_t diff = 1 + ((lfs->sum & 0x1) ^ !lfs->unstable);
dir->d.rev += diff;
lfs->sum += diff;
// keep pairs in order such that pair[0] is most recent // keep pairs in order such that pair[0] is most recent
lfs_pairswap(dir->pair); lfs_pairswap(dir->pair);
@@ -626,6 +628,7 @@ static int lfs_dir_remove(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
{entry->off, lfs_entry_size(entry), NULL, 0}, {entry->off, lfs_entry_size(entry), NULL, 0},
}, 1); }, 1);
} else { } else {
lfs->sum -= dir->d.rev;
pdir.d.size &= dir->d.size | 0x7fffffff; pdir.d.size &= dir->d.size | 0x7fffffff;
pdir.d.tail[0] = dir->d.tail[0]; pdir.d.tail[0] = dir->d.tail[0];
pdir.d.tail[1] = dir->d.tail[1]; pdir.d.tail[1] = dir->d.tail[1];
@@ -788,7 +791,7 @@ static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir,
/// Top level directory operations /// /// Top level directory operations ///
int lfs_mkdir(lfs_t *lfs, const char *path) { int lfs_mkdir(lfs_t *lfs, const char *path) {
// deorphan if we haven't yet, needed at most once after poweron // deorphan if we haven't yet, needed at most once after poweron
if (!lfs->deorphaned) { if (lfs->unstable) {
int err = lfs_deorphan(lfs); int err = lfs_deorphan(lfs);
if (err) { if (err) {
return err; return err;
@@ -1161,7 +1164,7 @@ static int lfs_index_traverse(lfs_t *lfs,
int lfs_file_open(lfs_t *lfs, lfs_file_t *file, int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
const char *path, int flags) { const char *path, int flags) {
// deorphan if we haven't yet, needed at most once after poweron // deorphan if we haven't yet, needed at most once after poweron
if ((flags & 3) != LFS_O_RDONLY && !lfs->deorphaned) { if ((flags & 3) != LFS_O_RDONLY && lfs->unstable) {
int err = lfs_deorphan(lfs); int err = lfs_deorphan(lfs);
if (err) { if (err) {
return err; return err;
@@ -1654,7 +1657,7 @@ int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) {
int lfs_remove(lfs_t *lfs, const char *path) { int lfs_remove(lfs_t *lfs, const char *path) {
// deorphan if we haven't yet, needed at most once after poweron // deorphan if we haven't yet, needed at most once after poweron
if (!lfs->deorphaned) { if (lfs->unstable) {
int err = lfs_deorphan(lfs); int err = lfs_deorphan(lfs);
if (err) { if (err) {
return err; return err;
@@ -1687,6 +1690,7 @@ int lfs_remove(lfs_t *lfs, const char *path) {
} }
// remove the entry // remove the entry
lfs->unstable += (entry.d.type == LFS_TYPE_DIR);
err = lfs_dir_remove(lfs, &cwd, &entry); err = lfs_dir_remove(lfs, &cwd, &entry);
if (err) { if (err) {
return err; return err;
@@ -1700,9 +1704,11 @@ int lfs_remove(lfs_t *lfs, const char *path) {
} }
assert(res); // must have pred assert(res); // must have pred
lfs->sum -= dir.d.rev;
cwd.d.tail[0] = dir.d.tail[0]; cwd.d.tail[0] = dir.d.tail[0];
cwd.d.tail[1] = dir.d.tail[1]; cwd.d.tail[1] = dir.d.tail[1];
lfs->unstable -= 1;
int err = lfs_dir_commit(lfs, &cwd, NULL, 0); int err = lfs_dir_commit(lfs, &cwd, NULL, 0);
if (err) { if (err) {
return err; return err;
@@ -1714,7 +1720,7 @@ int lfs_remove(lfs_t *lfs, const char *path) {
int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
// deorphan if we haven't yet, needed at most once after poweron // deorphan if we haven't yet, needed at most once after poweron
if (!lfs->deorphaned) { if (lfs->unstable) {
int err = lfs_deorphan(lfs); int err = lfs_deorphan(lfs);
if (err) { if (err) {
return err; return err;
@@ -1769,6 +1775,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
} }
// mark as moving // mark as moving
lfs->unstable += 1 + (prevexists && preventry.d.type == LFS_TYPE_DIR);
oldentry.d.type |= 0x80; oldentry.d.type |= 0x80;
err = lfs_dir_update(lfs, &oldcwd, &oldentry, NULL); err = lfs_dir_update(lfs, &oldcwd, &oldentry, NULL);
if (err) { if (err) {
@@ -1804,6 +1811,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
} }
// remove old entry // remove old entry
lfs->unstable -= 1;
err = lfs_dir_remove(lfs, &oldcwd, &oldentry); err = lfs_dir_remove(lfs, &oldcwd, &oldentry);
if (err) { if (err) {
return err; return err;
@@ -1817,9 +1825,11 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
} }
assert(res); // must have pred assert(res); // must have pred
lfs->sum -= dir.d.rev;
newcwd.d.tail[0] = dir.d.tail[0]; newcwd.d.tail[0] = dir.d.tail[0];
newcwd.d.tail[1] = dir.d.tail[1]; newcwd.d.tail[1] = dir.d.tail[1];
lfs->unstable -= 1;
int err = lfs_dir_commit(lfs, &newcwd, NULL, 0); int err = lfs_dir_commit(lfs, &newcwd, NULL, 0);
if (err) { if (err) {
return err; return err;
@@ -1873,7 +1883,8 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
lfs->root[0] = 0xffffffff; lfs->root[0] = 0xffffffff;
lfs->root[1] = 0xffffffff; lfs->root[1] = 0xffffffff;
lfs->files = NULL; lfs->files = NULL;
lfs->deorphaned = false; lfs->unstable = false;
lfs->sum = 0;
return 0; return 0;
} }
@@ -2007,6 +2018,8 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
if (err || memcmp(superblock.d.magic, "littlefs", 8) != 0) { if (err || memcmp(superblock.d.magic, "littlefs", 8) != 0) {
LFS_ERROR("Invalid superblock at %d %d", dir.pair[0], dir.pair[1]); LFS_ERROR("Invalid superblock at %d %d", dir.pair[0], dir.pair[1]);
return LFS_ERR_CORRUPT; return LFS_ERR_CORRUPT;
} else if (err) {
return err;
} }
if (superblock.d.version > (0x00010001 | 0x0000ffff)) { if (superblock.d.version > (0x00010001 | 0x0000ffff)) {
@@ -2016,6 +2029,22 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
return LFS_ERR_INVAL; return LFS_ERR_INVAL;
} }
// sum rev counts in fs, even = stable, odd = unstable
lfs->sum += dir.d.rev;
while (!lfs_pairisnull(dir.d.tail)) {
int err = lfs_dir_fetch(lfs, &dir, dir.d.tail);
if (err) {
return err;
}
lfs->sum += dir.d.rev;
}
if (lfs->sum & 0x1) {
LFS_DEBUG("Power-loss detected %x", lfs->sum);
lfs->unstable += 1;
}
return 0; return 0;
} }
@@ -2212,6 +2241,7 @@ static int lfs_relocate(lfs_t *lfs,
entry.d.u.dir[0] = newpair[0]; entry.d.u.dir[0] = newpair[0];
entry.d.u.dir[1] = newpair[1]; entry.d.u.dir[1] = newpair[1];
lfs->unstable += 1;
int err = lfs_dir_update(lfs, &parent, &entry, NULL); int err = lfs_dir_update(lfs, &parent, &entry, NULL);
if (err) { if (err) {
return err; return err;
@@ -2247,8 +2277,6 @@ static int lfs_relocate(lfs_t *lfs,
} }
int lfs_deorphan(lfs_t *lfs) { int lfs_deorphan(lfs_t *lfs) {
lfs->deorphaned = true;
if (lfs_pairisnull(lfs->root)) { if (lfs_pairisnull(lfs->root)) {
return 0; return 0;
} }
@@ -2347,6 +2375,7 @@ int lfs_deorphan(lfs_t *lfs) {
memcpy(&pdir, &cwd, sizeof(pdir)); memcpy(&pdir, &cwd, sizeof(pdir));
} }
lfs->unstable -= 1;
return 0; return 0;
} }

3
lfs.h
View File

@@ -243,7 +243,8 @@ typedef struct lfs {
lfs_cache_t pcache; lfs_cache_t pcache;
lfs_free_t free; lfs_free_t free;
bool deorphaned; uint8_t unstable;
uint8_t sum;
} lfs_t; } lfs_t;