Browse Source

Cleaned up block allocator

Removed scanning for stride
- Adds complexity with questionable benefit
- Can be added as an optimization later

Fixed handling around device boundaries and where lookahead may not be a
factor of the device size (consider small devices with only a few
blocks)

Added support for configuration with optional dynamic memory as found in
the caching configuration
Christopher Haster 8 years ago
parent
commit
aa872657d2
4 changed files with 91 additions and 94 deletions
  1. 74 84
      lfs.c
  2. 12 5
      lfs.h
  3. 0 5
      lfs_config.h
  4. 5 0
      tests/template.fmt

+ 74 - 84
lfs.c

@@ -189,103 +189,60 @@ static int lfs_bd_cmp(lfs_t *lfs, lfs_block_t block,
 static int lfs_alloc_lookahead(void *p, lfs_block_t block) {
     lfs_t *lfs = p;
 
-    lfs_block_t off = block - lfs->free.begin;
-    if (off < LFS_CFG_LOOKAHEAD) {
-        lfs->lookahead[off / 32] |= 1U << (off % 32);
+    lfs_block_t off = (block - lfs->free.start) % lfs->cfg->block_count;
+    if (off < lfs->cfg->lookahead) {
+        lfs->free.lookahead[off / 32] |= 1U << (off % 32);
     }
 
     return 0;
 }
 
-static int lfs_alloc_stride(void *p, lfs_block_t block) {
-    lfs_t *lfs = p;
-
-    lfs_block_t noff = block - lfs->free.begin;
-    lfs_block_t off = lfs->free.end - lfs->free.begin;
-    if (noff < off) {
-        lfs->free.end = noff + lfs->free.begin;
-    }
-
-    return 0;
-}
-
-static int lfs_alloc_scan(lfs_t *lfs) {
-    lfs_block_t start = lfs->free.begin;
+static int lfs_alloc_scan(lfs_t *lfs, lfs_block_t *block) {
+    lfs_block_t end = lfs->free.start + lfs->cfg->block_count;
 
     while (true) {
-        // mask out blocks in lookahead region
-        memset(lfs->lookahead, 0, sizeof(lfs->lookahead));
-        int err = lfs_traverse(lfs, lfs_alloc_lookahead, lfs);
-        if (err) {
-            return err;
-        }
-
-        // check if we've found a free block
-        for (uint32_t off = 0; off < LFS_CFG_LOOKAHEAD; off++) {
-            if (lfs->lookahead[off / 32] & (1U << (off % 32))) {
-                continue;
+        while (lfs->free.off < lfs->cfg->lookahead) {
+            lfs_block_t off = lfs->free.off;
+            lfs->free.off += 1;
+
+            if (!(lfs->free.lookahead[off / 32] & (1U << (off % 32)))) {
+                // found a free block
+                *block = (lfs->free.start + off) % lfs->cfg->block_count;
+                return 0;
             }
-
-            // found free block, now find stride of free blocks
-            // since this is relatively cheap (stress on relatively)
-            lfs->free.begin += off;
-            lfs->free.end = lfs->cfg->block_count; // before superblock
-
-            // find maximum stride in tree
-            return lfs_traverse(lfs, lfs_alloc_stride, lfs);
         }
 
-        // continue to next lookahead unless we've searched the whole device
-        if (start-1 - lfs->free.begin < LFS_CFG_LOOKAHEAD) {
-            return 0;
+        // could not find block
+        lfs->free.start += lfs->cfg->lookahead;
+        lfs->free.off = 0;
+        if (lfs_scmp(lfs->free.start, end) > 0) {
+            return LFS_ERROR_NO_SPACE;
         }
 
-        // continue to next lookahead region
-        lfs->free.begin += LFS_CFG_LOOKAHEAD;
-    }
-}
-
-static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
-    // If we don't remember any free blocks we will need to start searching
-    if (lfs->free.begin == lfs->free.end) {
-        int err = lfs_alloc_scan(lfs);
+        // find mask of free blocks from tree
+        memset(lfs->free.lookahead, 0, lfs->cfg->lookahead/8);
+        int err = lfs_traverse(lfs, lfs_alloc_lookahead, lfs);
         if (err) {
             return err;
         }
-
-        if (lfs->free.begin == lfs->free.end) {
-            // Still can't allocate a block? check for orphans
-            int err = lfs_deorphan(lfs);
-            if (err) {
-                return err;
-            }
-
-            err = lfs_alloc_scan(lfs);
-            if (err) {
-                return err;
-            }
-
-            if (lfs->free.begin == lfs->free.end) {
-                // Ok, it's true, we're out of space
-                return LFS_ERROR_NO_SPACE;
-            }
-        }
     }
-
-    // Take first available block
-    *block = lfs->free.begin;
-    lfs->free.begin += 1;
-    return 0;
 }
 
-static int lfs_alloc_erased(lfs_t *lfs, lfs_block_t *block) {
-    // TODO rm me?
-    int err = lfs_alloc(lfs, block);
+static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
+    // try to scan for free block
+    int err = lfs_alloc_scan(lfs, block);
+    if (err != LFS_ERROR_NO_SPACE) {
+        return err;
+    }
+
+    // still can't allocate a block? check for orphans
+    err = lfs_deorphan(lfs);
     if (err) {
         return err;
     }
 
-    return lfs_bd_erase(lfs, *block);
+    // scan again or die trying
+    return lfs_alloc_scan(lfs, block);
 }
 
 
@@ -343,7 +300,12 @@ static int lfs_index_append(lfs_t *lfs, lfs_block_t *headp,
 
     while (ioff % lfs->words == 0) {
         lfs_block_t nhead;
-        int err = lfs_alloc_erased(lfs, &nhead);
+        int err = lfs_alloc(lfs, &nhead);
+        if (err) {
+            return err;
+        }
+
+        err = lfs_bd_erase(lfs, nhead);
         if (err) {
             return err;
         }
@@ -480,7 +442,7 @@ static int lfs_dir_alloc(lfs_t *lfs, lfs_dir_t *dir) {
 static int lfs_dir_fetch(lfs_t *lfs,
         lfs_dir_t *dir, const lfs_block_t pair[2]) {
     // copy out pair, otherwise may be aliasing dir
-    const lfs_block_t tpair[2] = {pair[0], pair[1]}; 
+    const lfs_block_t tpair[2] = {pair[0], pair[1]};
     bool valid = false;
 
     // check both blocks for the most recent revision
@@ -1048,7 +1010,12 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
         lfs_off_t woff = file->size % lfs->cfg->block_size;
 
         if (file->size == 0) {
-            int err = lfs_alloc_erased(lfs, &file->wblock);
+            int err = lfs_alloc(lfs, &file->wblock);
+            if (err) {
+                return err;
+            }
+
+            err = lfs_bd_erase(lfs, file->wblock);
             if (err) {
                 return err;
             }
@@ -1056,7 +1023,12 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
             file->head = file->wblock;
             file->windex = 0;
         } else if (woff == 0) {
-            int err = lfs_alloc_erased(lfs, &file->wblock);
+            int err = lfs_alloc(lfs, &file->wblock);
+            if (err) {
+                return err;
+            }
+
+            err = lfs_bd_erase(lfs, file->wblock);
             if (err) {
                 return err;
             }
@@ -1124,9 +1096,9 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
 static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
     lfs->cfg = cfg;
     lfs->words = lfs->cfg->block_size / sizeof(uint32_t);
-    lfs->rcache.off = -1;
-    lfs->pcache.off = -1;
 
+    // setup read cache
+    lfs->rcache.off = -1;
     if (lfs->cfg->read_buffer) {
         lfs->rcache.buffer = lfs->cfg->read_buffer;
     } else {
@@ -1136,6 +1108,8 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
         }
     }
 
+    // setup program cache
+    lfs->pcache.off = -1;
     if (lfs->cfg->prog_buffer) {
         lfs->pcache.buffer = lfs->cfg->prog_buffer;
     } else {
@@ -1145,6 +1119,16 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
         }
     }
 
+    // setup lookahead
+    if (lfs->cfg->lookahead_buffer) {
+        lfs->free.lookahead = lfs->cfg->lookahead_buffer;
+    } else {
+        lfs->free.lookahead = malloc(lfs->cfg->lookahead/8);
+        if (!lfs->free.lookahead) {
+            return LFS_ERROR_NO_MEM;
+        }
+    }
+
     return 0;
 }
 
@@ -1167,9 +1151,10 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) {
         return err;
     }
 
-    // Create free list
-    lfs->free.begin = 0;
-    lfs->free.end = lfs->cfg->block_count-1;
+    // Create free lookahead
+    memset(lfs->free.lookahead, 0, lfs->cfg->lookahead/8);
+    lfs->free.start = 0;
+    lfs->free.off = 0;
 
     // Create superblock dir
     lfs_dir_t superdir;
@@ -1235,6 +1220,11 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
         return err;
     }
 
+    // setup free lookahead
+    lfs->free.start = -lfs->cfg->lookahead;
+    lfs->free.off = lfs->cfg->lookahead;
+
+    // load superblock
     lfs_dir_t dir;
     lfs_superblock_t superblock;
     err = lfs_dir_fetch(lfs, &dir, (const lfs_block_t[2]){0, 1});

+ 12 - 5
lfs.h

@@ -76,11 +76,18 @@ struct lfs_config {
     // Number of erasable blocks on the device.
     lfs_size_t block_count;
 
+    // Number of blocks to lookahead during block allocation.
+    lfs_size_t lookahead;
+
     // Optional, statically allocated read buffer. Must be read sized.
     void *read_buffer;
 
     // Optional, statically allocated program buffer. Must be program sized.
     void *prog_buffer;
+
+    // Optional, statically allocated lookahead buffer.
+    // Must be 1 bit per lookahead block.
+    void *lookahead_buffer;
 };
 
 // File info structure
@@ -161,10 +168,6 @@ typedef struct lfs {
     lfs_size_t words;       // number of 32-bit words that can fit in a block
 
     lfs_block_t root[2];
-    struct {
-        lfs_block_t begin;
-        lfs_block_t end;
-    } free;
 
     struct {
         lfs_block_t block;
@@ -172,7 +175,11 @@ typedef struct lfs {
         uint8_t *buffer;
     } rcache, pcache;
 
-    uint32_t lookahead[LFS_CFG_LOOKAHEAD/32];
+    struct {
+        lfs_block_t start;
+        lfs_block_t off;
+        uint32_t *lookahead;
+    } free;
 } lfs_t;
 
 

+ 0 - 5
lfs_config.h

@@ -23,11 +23,6 @@ typedef uint32_t lfs_block_t;
 #define LFS_NAME_MAX 255
 #endif
 
-// Lookahead distance
-#ifndef LFS_CFG_LOOKAHEAD
-#define LFS_CFG_LOOKAHEAD 128
-#endif
-
 // Logging operations
 #include <stdio.h>
 #define LFS_ERROR(fmt, ...) printf("lfs error: " fmt "\n", __VA_ARGS__)

+ 5 - 0
tests/template.fmt

@@ -70,6 +70,10 @@ uintmax_t res;
 #define LFS_BLOCK_COUNT 1024
 #endif
 
+#ifndef LFS_LOOKAHEAD
+#define LFS_LOOKAHEAD 128
+#endif
+
 const struct lfs_config cfg = {{
     .context = &bd,
     .read  = &lfs_emubd_read,
@@ -81,6 +85,7 @@ const struct lfs_config cfg = {{
     .prog_size   = LFS_PROG_SIZE,
     .block_size  = LFS_BLOCK_SIZE,
     .block_count = LFS_BLOCK_COUNT,
+    .lookahead   = LFS_LOOKAHEAD,
 }};