فهرست منبع

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.
Christopher Haster 7 سال پیش
والد
کامیت
e611cf5050
3فایلهای تغییر یافته به همراه54 افزوده شده و 21 حذف شده
  1. 18 20
      lfs.c
  2. 2 1
      lfs.h
  3. 34 0
      tests/test_alloc.sh

+ 18 - 20
lfs.c

@@ -278,7 +278,7 @@ static int lfs_alloc_lookahead(void *p, lfs_block_t block) {
                 % (lfs_soff_t)(lfs->cfg->block_count))
                 % (lfs_soff_t)(lfs->cfg->block_count))
             + lfs->cfg->block_count) % lfs->cfg->block_count;
             + lfs->cfg->block_count) % lfs->cfg->block_count;
 
 
-    if (off < lfs->cfg->lookahead) {
+    if (off < lfs->free.size) {
         lfs->free.buffer[off / 32] |= 1U << (off % 32);
         lfs->free.buffer[off / 32] |= 1U << (off % 32);
     }
     }
 
 
@@ -287,18 +287,7 @@ static int lfs_alloc_lookahead(void *p, lfs_block_t block) {
 
 
 static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
 static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
     while (true) {
     while (true) {
-        while (true) {
-            // check if we have looked at all blocks since last ack
-            if (lfs->free.begin + lfs->free.off == lfs->free.end) {
-                LFS_WARN("No more free space %d", lfs->free.end);
-                return LFS_ERR_NOSPC;
-            }
-
-            if (lfs->free.off >= lfs_min(
-                    lfs->cfg->lookahead, lfs->cfg->block_count)) {
-                break;
-            }
-
+        while (lfs->free.off != lfs->free.size) {
             lfs_block_t off = lfs->free.off;
             lfs_block_t off = lfs->free.off;
             lfs->free.off += 1;
             lfs->free.off += 1;
 
 
@@ -309,7 +298,15 @@ static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
             }
             }
         }
         }
 
 
-        lfs->free.begin += lfs_min(lfs->cfg->lookahead, lfs->cfg->block_count);
+        // check if we have looked at all blocks since last ack
+        if (lfs->free.off == lfs->free.ack - lfs->free.begin) {
+            LFS_WARN("No more free space %d", lfs->free.off + lfs->free.begin);
+            return LFS_ERR_NOSPC;
+        }
+
+        lfs->free.begin += lfs->free.size;
+        lfs->free.size = lfs_min(lfs->cfg->lookahead,
+                lfs->free.ack - lfs->free.begin);
         lfs->free.off = 0;
         lfs->free.off = 0;
 
 
         // find mask of free blocks from tree
         // find mask of free blocks from tree
@@ -322,7 +319,7 @@ static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
 }
 }
 
 
 static void lfs_alloc_ack(lfs_t *lfs) {
 static void lfs_alloc_ack(lfs_t *lfs) {
-    lfs->free.end = lfs->free.begin + lfs->free.off + lfs->cfg->block_count;
+    lfs->free.ack = lfs->free.off-1 + lfs->free.begin + lfs->cfg->block_count;
 }
 }
 
 
 
 
@@ -2035,11 +2032,11 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) {
     // create free lookahead
     // create free lookahead
     memset(lfs->free.buffer, 0, lfs->cfg->lookahead/8);
     memset(lfs->free.buffer, 0, lfs->cfg->lookahead/8);
     lfs->free.begin = 0;
     lfs->free.begin = 0;
+    lfs->free.size = lfs_min(lfs->cfg->lookahead, lfs->cfg->block_count);
     lfs->free.off = 0;
     lfs->free.off = 0;
-    lfs->free.end = lfs->free.begin + lfs->free.off + lfs->cfg->block_count;
+    lfs_alloc_ack(lfs);
 
 
     // create superblock dir
     // create superblock dir
-    lfs_alloc_ack(lfs);
     lfs_dir_t superdir;
     lfs_dir_t superdir;
     err = lfs_dir_alloc(lfs, &superdir);
     err = lfs_dir_alloc(lfs, &superdir);
     if (err) {
     if (err) {
@@ -2112,9 +2109,10 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
     }
     }
 
 
     // setup free lookahead
     // setup free lookahead
-    lfs->free.begin = -lfs_min(lfs->cfg->lookahead, lfs->cfg->block_count);
-    lfs->free.off = -lfs->free.begin;
-    lfs->free.end = lfs->free.begin + lfs->free.off + lfs->cfg->block_count;
+    lfs->free.begin = 0;
+    lfs->free.size = 0;
+    lfs->free.off = 0;
+    lfs_alloc_ack(lfs);
 
 
     // load superblock
     // load superblock
     lfs_dir_t dir;
     lfs_dir_t dir;

+ 2 - 1
lfs.h

@@ -260,8 +260,9 @@ typedef struct lfs_superblock {
 
 
 typedef struct lfs_free {
 typedef struct lfs_free {
     lfs_block_t begin;
     lfs_block_t begin;
-    lfs_block_t end;
+    lfs_block_t size;
     lfs_block_t off;
     lfs_block_t off;
+    lfs_block_t ack;
     uint32_t *buffer;
     uint32_t *buffer;
 } lfs_free_t;
 } lfs_free_t;
 
 

+ 34 - 0
tests/test_alloc.sh

@@ -266,6 +266,40 @@ tests/test.py << TEST
     lfs_mkdir(&lfs, "exhaustiondir2") => LFS_ERR_NOSPC;
     lfs_mkdir(&lfs, "exhaustiondir2") => LFS_ERR_NOSPC;
 TEST
 TEST
 
 
+echo "--- Split dir test ---"
+rm -rf blocks
+tests/test.py << TEST
+    lfs_format(&lfs, &cfg) => 0;
+TEST
+tests/test.py << TEST
+    lfs_mount(&lfs, &cfg) => 0;
+
+    // create one block whole for half a directory
+    lfs_file_open(&lfs, &file[0], "bump", LFS_O_WRONLY | LFS_O_CREAT) => 0;
+    lfs_file_write(&lfs, &file[0], (void*)"hi", 2) => 2;
+    lfs_file_close(&lfs, &file[0]) => 0;
+
+    lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
+    size = strlen("blahblahblahblah");
+    memcpy(buffer, "blahblahblahblah", size);
+    for (lfs_size_t i = 0;
+            i < (cfg.block_count-6)*(cfg.block_size-8);
+            i += size) {
+        lfs_file_write(&lfs, &file[0], buffer, size) => size;
+    }
+    lfs_file_close(&lfs, &file[0]) => 0;
+
+    // open whole
+    lfs_remove(&lfs, "bump") => 0;
+
+    lfs_mkdir(&lfs, "splitdir") => 0;
+    lfs_file_open(&lfs, &file[0], "splitdir/bump",
+            LFS_O_WRONLY | LFS_O_CREAT) => 0;
+    lfs_file_write(&lfs, &file[0], buffer, size) => LFS_ERR_NOSPC;
+    lfs_file_close(&lfs, &file[0]) => 0;
+
+    lfs_unmount(&lfs) => 0;
+TEST
 
 
 echo "--- Results ---"
 echo "--- Results ---"
 tests/stats.py
 tests/stats.py