From d04c1392c0b5bdd517a7032b158a689b5ab3d79e Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Sun, 22 Nov 2020 00:40:58 -0600 Subject: [PATCH] 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 --- lfs.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lfs.c b/lfs.c index eb832fa..3899c28 100644 --- a/lfs.c +++ b/lfs.c @@ -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);