Skip to content

Commit 73e87a2

Browse files
gekycmonr
authored andcommitted
littlefs: Fix incorrect lookahead population before ack
Rather than tracking all in-flight blocks blocks during a lookahead, littlefs uses an ack scheme to mark the first allocated block that hasn't reached the disk yet. littlefs assumes all blocks since the last ack are bad or in-flight, and uses this to know when it's out of storage. However, these unacked allocations were still being populated in the lookahead buffer. If the whole block device fits in the lookahead buffer, _and_ littlefs managed to scan around the whole storage while an unacked block was still in-flight, it would assume the block was free and misallocate it. The fix is to only fill the lookahead buffer up to the last ack. The internal free structure was restructured to simplify the runtime calculation of lookahead size.
1 parent eb4a7bc commit 73e87a2

File tree

3 files changed

+55
-22
lines changed

3 files changed

+55
-22
lines changed

features/filesystem/littlefs/littlefs/lfs.c

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ static int lfs_alloc_lookahead(void *p, lfs_block_t block) {
278278
% (lfs_soff_t)(lfs->cfg->block_count))
279279
+ lfs->cfg->block_count) % lfs->cfg->block_count;
280280

281-
if (off < lfs->cfg->lookahead) {
281+
if (off < lfs->free.size) {
282282
lfs->free.buffer[off / 32] |= 1U << (off % 32);
283283
}
284284

@@ -287,18 +287,7 @@ static int lfs_alloc_lookahead(void *p, lfs_block_t block) {
287287

288288
static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
289289
while (true) {
290-
while (true) {
291-
// check if we have looked at all blocks since last ack
292-
if (lfs->free.begin + lfs->free.off == lfs->free.end) {
293-
LFS_WARN("No more free space %ld", lfs->free.end);
294-
return LFS_ERR_NOSPC;
295-
}
296-
297-
if (lfs->free.off >= lfs_min(
298-
lfs->cfg->lookahead, lfs->cfg->block_count)) {
299-
break;
300-
}
301-
290+
while (lfs->free.off != lfs->free.size) {
302291
lfs_block_t off = lfs->free.off;
303292
lfs->free.off += 1;
304293

@@ -309,7 +298,15 @@ static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
309298
}
310299
}
311300

312-
lfs->free.begin += lfs_min(lfs->cfg->lookahead, lfs->cfg->block_count);
301+
// check if we have looked at all blocks since last ack
302+
if (lfs->free.off == lfs->free.ack - lfs->free.begin) {
303+
LFS_WARN("No more free space %ld", lfs->free.off + lfs->free.begin);
304+
return LFS_ERR_NOSPC;
305+
}
306+
307+
lfs->free.begin += lfs->free.size;
308+
lfs->free.size = lfs_min(lfs->cfg->lookahead,
309+
lfs->free.ack - lfs->free.begin);
313310
lfs->free.off = 0;
314311

315312
// find mask of free blocks from tree
@@ -322,7 +319,7 @@ static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
322319
}
323320

324321
static void lfs_alloc_ack(lfs_t *lfs) {
325-
lfs->free.end = lfs->free.begin + lfs->free.off + lfs->cfg->block_count;
322+
lfs->free.ack = lfs->free.off-1 + lfs->free.begin + lfs->cfg->block_count;
326323
}
327324

328325

@@ -1966,11 +1963,11 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) {
19661963
// create free lookahead
19671964
memset(lfs->free.buffer, 0, lfs->cfg->lookahead/8);
19681965
lfs->free.begin = 0;
1966+
lfs->free.size = lfs_min(lfs->cfg->lookahead, lfs->cfg->block_count);
19691967
lfs->free.off = 0;
1970-
lfs->free.end = lfs->free.begin + lfs->free.off + lfs->cfg->block_count;
1968+
lfs_alloc_ack(lfs);
19711969

19721970
// create superblock dir
1973-
lfs_alloc_ack(lfs);
19741971
lfs_dir_t superdir;
19751972
err = lfs_dir_alloc(lfs, &superdir);
19761973
if (err) {
@@ -2042,10 +2039,11 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
20422039
return err;
20432040
}
20442041

2045-
// setup free lookahead, rewind so first allocation triggers a scan
2046-
lfs->free.begin = -lfs_min(lfs->cfg->lookahead, lfs->cfg->block_count);
2047-
lfs->free.off = -lfs->free.begin;
2048-
lfs->free.end = lfs->free.begin + lfs->free.off + lfs->cfg->block_count;
2042+
// setup free lookahead
2043+
lfs->free.begin = 0;
2044+
lfs->free.size = 0;
2045+
lfs->free.off = 0;
2046+
lfs_alloc_ack(lfs);
20492047

20502048
// load superblock
20512049
lfs_dir_t dir;

features/filesystem/littlefs/littlefs/lfs.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,8 +239,9 @@ typedef struct lfs_superblock {
239239

240240
typedef struct lfs_free {
241241
lfs_block_t begin;
242-
lfs_block_t end;
242+
lfs_block_t size;
243243
lfs_block_t off;
244+
lfs_block_t ack;
244245
uint32_t *buffer;
245246
} lfs_free_t;
246247

features/filesystem/littlefs/littlefs/tests/test_alloc.sh

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,40 @@ tests/test.py << TEST
266266
lfs_mkdir(&lfs, "exhaustiondir2") => LFS_ERR_NOSPC;
267267
TEST
268268

269+
echo "--- Split dir test ---"
270+
rm -rf blocks
271+
tests/test.py << TEST
272+
lfs_format(&lfs, &cfg) => 0;
273+
TEST
274+
tests/test.py << TEST
275+
lfs_mount(&lfs, &cfg) => 0;
276+
277+
// create one block whole for half a directory
278+
lfs_file_open(&lfs, &file[0], "bump", LFS_O_WRONLY | LFS_O_CREAT) => 0;
279+
lfs_file_write(&lfs, &file[0], (void*)"hi", 2) => 2;
280+
lfs_file_close(&lfs, &file[0]) => 0;
281+
282+
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
283+
size = strlen("blahblahblahblah");
284+
memcpy(buffer, "blahblahblahblah", size);
285+
for (lfs_size_t i = 0;
286+
i < (cfg.block_count-6)*(cfg.block_size-8);
287+
i += size) {
288+
lfs_file_write(&lfs, &file[0], buffer, size) => size;
289+
}
290+
lfs_file_close(&lfs, &file[0]) => 0;
291+
292+
// open whole
293+
lfs_remove(&lfs, "bump") => 0;
294+
295+
lfs_mkdir(&lfs, "splitdir") => 0;
296+
lfs_file_open(&lfs, &file[0], "splitdir/bump",
297+
LFS_O_WRONLY | LFS_O_CREAT) => 0;
298+
lfs_file_write(&lfs, &file[0], buffer, size) => LFS_ERR_NOSPC;
299+
lfs_file_close(&lfs, &file[0]) => 0;
300+
301+
lfs_unmount(&lfs) => 0;
302+
TEST
269303

270304
echo "--- Results ---"
271305
tests/stats.py

0 commit comments

Comments
 (0)