From 1f9c3c04b1f936d925f9101ca2ea84d71ce2c3e5 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Wed, 20 Dec 2023 00:23:04 -0600 Subject: [PATCH] Reworked the block allocator so the logic is hopefully simpler Some of this is just better documentation, some of this is reworking the logic to be more intention driven... if that makes sense... --- lfs.c | 64 +++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 24 deletions(-) diff --git a/lfs.c b/lfs.c index 27a66e9c..f6001a59 100644 --- a/lfs.c +++ b/lfs.c @@ -607,9 +607,10 @@ static int lfs_alloc_lookahead(void *p, lfs_block_t block) { } #endif -// indicate allocated blocks have been committed into the filesystem, this -// is to prevent blocks from being garbage collected in the middle of a -// commit operation +// allocations should call this when all allocated blocks are committed to +// the filesystem +// +// after a checkpoint, the block allocator may realloc any untracked blocks static void lfs_alloc_ckpoint(lfs_t *lfs) { lfs->lookahead.ckpoint = lfs->block_count; } @@ -624,14 +625,16 @@ static void lfs_alloc_drop(lfs_t *lfs) { #ifndef LFS_READONLY static int lfs_fs_rawgc(lfs_t *lfs) { - // Move free offset at the first unused block (lfs->lookahead.next) - // lfs->lookahead.next is equal lfs->lookahead.size when all blocks are used + // move lookahead buffer to the first unused block + // + // note we limit the lookahead buffer to at most the amount of blocks + // checkpointed, this prevents the math in lfs_alloc from underflowing lfs->lookahead.start = (lfs->lookahead.start + lfs->lookahead.next) % lfs->block_count; + lfs->lookahead.next = 0; lfs->lookahead.size = lfs_min( 8*lfs->cfg->lookahead_size, lfs->lookahead.ckpoint); - lfs->lookahead.next = 0; // find mask of free blocks from tree memset(lfs->lookahead.buffer, 0, lfs->cfg->lookahead_size); @@ -648,35 +651,48 @@ static int lfs_fs_rawgc(lfs_t *lfs) { #ifndef LFS_READONLY static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) { while (true) { - while (lfs->lookahead.next != lfs->lookahead.size) { - lfs_block_t off = lfs->lookahead.next; - lfs->lookahead.next += 1; - lfs->lookahead.ckpoint -= 1; - - if (!(lfs->lookahead.buffer[off / 32] & (1U << (off % 32)))) { + // scan our lookahead buffer for free blocks + while (lfs->lookahead.next < lfs->lookahead.size) { + if (!(lfs->lookahead.buffer[lfs->lookahead.next / 32] + & (1U << (lfs->lookahead.next % 32)))) { // found a free block - *block = (lfs->lookahead.start + off) % lfs->block_count; + *block = (lfs->lookahead.start + lfs->lookahead.next) + % lfs->block_count; - // eagerly find next off so an alloc ack can - // discredit old lookahead blocks - while (lfs->lookahead.next != lfs->lookahead.size && - (lfs->lookahead.buffer[lfs->lookahead.next / 32] - & (1U << (lfs->lookahead.next % 32)))) { + // eagerly find next free block to maximize how many blocks + // lfs_alloc_ckpoint makes available for scanning + while (true) { lfs->lookahead.next += 1; lfs->lookahead.ckpoint -= 1; - } - return 0; + if (lfs->lookahead.next >= lfs->lookahead.size + || !(lfs->lookahead.buffer[lfs->lookahead.next / 32] + & (1U << (lfs->lookahead.next % 32)))) { + return 0; + } + } } + + lfs->lookahead.next += 1; + lfs->lookahead.ckpoint -= 1; } - // check if we have looked at all blocks since last ack - if (lfs->lookahead.ckpoint == 0) { - LFS_ERROR("No more free space %"PRIu32, - lfs->lookahead.next + lfs->lookahead.start); + // In order to keep our block allocator from spinning forever when our + // filesystem is full, we mark points where there are no in-flight + // allocations with a checkpoint before starting a set of allocations. + // + // If we've looked at all blocks since the last checkpoint, we report + // the filesystem as out of storage. + // + if (lfs->lookahead.ckpoint <= 0) { + LFS_ERROR("No more free space 0x%"PRIx32, + (lfs->lookahead.start + lfs->lookahead.next) + % lfs->cfg->block_count); return LFS_ERR_NOSPC; } + // No blocks in our lookahead buffer, we need to scan the filesystem for + // unused blocks in the next lookahead window. int err = lfs_fs_rawgc(lfs); if(err) { return err;