Fixed allocation-eviction issue when erase state is multiple of block_cycles+1

This rather interesting corner-case arises in lfs_dir_alloc anytime the
uninitialized revision count happens to be a multiple of block_cycles+1.

For example, the source of the bug found by tim-nordell-nimbelink:

rev = 2742492087
block_cycles = 100

2742492087 % (100+1) = 0

The reason for this weird block_cycles+1 case is due to a fix for a
previous bug in fe957de. To avoid aliasing, which would cause metadata
pairs to wear unevenly, block_cycles incremented to the next odd number.

Normally, littlefs tweaks the revision count of blocks during
lfs_dir_alloc in order to make sure evictions can't happen on the first
compact. Otherwise, higher-level logic such as lfs_format would break.

However, this wasn't updated with the aliasing fix in fe957de, so
lfs_dir_alloc was only rounding the revision count to the nearest even
number.

The current fix is to change the logic in lfs_dir_alloc to explicitly
check for the eviction condition and increment if eviction would occur.

Found by tim-nordell-nimbelink
This commit is contained in:
Christopher Haster
2020-11-22 00:40:58 -06:00
parent 4c9146ea53
commit d04c1392c0

8
lfs.c
View File

@@ -1375,8 +1375,12 @@ static int lfs_dir_alloc(lfs_t *lfs, lfs_mdir_t *dir) {
return err;
}
// make sure we don't immediately evict
dir->rev += dir->rev & 1;
// make sure we don't immediately evict, see lfs_dir_compact for why
// this check is so complicated
if (lfs->cfg->block_cycles > 0 &&
(dir->rev + 1) % ((lfs->cfg->block_cycles+1)|1) == 0) {
dir->rev += 1;
}
// set defaults
dir->off = sizeof(dir->rev);