mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 16:14:16 +01:00 
			
		
		
		
	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
This commit is contained in:
		
							
								
								
									
										162
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										162
									
								
								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) { | static int lfs_alloc_lookahead(void *p, lfs_block_t block) { | ||||||
|     lfs_t *lfs = p; |     lfs_t *lfs = p; | ||||||
|  |  | ||||||
|     lfs_block_t off = block - lfs->free.begin; |     lfs_block_t off = (block - lfs->free.start) % lfs->cfg->block_count; | ||||||
|     if (off < LFS_CFG_LOOKAHEAD) { |     if (off < lfs->cfg->lookahead) { | ||||||
|         lfs->lookahead[off / 32] |= 1U << (off % 32); |         lfs->free.lookahead[off / 32] |= 1U << (off % 32); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int lfs_alloc_stride(void *p, lfs_block_t block) { | static int lfs_alloc_scan(lfs_t *lfs, lfs_block_t *block) { | ||||||
|     lfs_t *lfs = p; |     lfs_block_t end = lfs->free.start + lfs->cfg->block_count; | ||||||
|  |  | ||||||
|     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; |  | ||||||
|  |  | ||||||
|     while (true) { |     while (true) { | ||||||
|         // mask out blocks in lookahead region |         while (lfs->free.off < lfs->cfg->lookahead) { | ||||||
|         memset(lfs->lookahead, 0, sizeof(lfs->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; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // 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; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // 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); |         int err = lfs_traverse(lfs, lfs_alloc_lookahead, lfs); | ||||||
|         if (err) { |         if (err) { | ||||||
|             return 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; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             // 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; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // continue to next lookahead region |  | ||||||
|         lfs->free.begin += LFS_CFG_LOOKAHEAD; |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) { | 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 |     // try to scan for free block | ||||||
|     if (lfs->free.begin == lfs->free.end) { |     int err = lfs_alloc_scan(lfs, block); | ||||||
|         int err = lfs_alloc_scan(lfs); |     if (err != LFS_ERROR_NO_SPACE) { | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // still can't allocate a block? check for orphans | ||||||
|  |     err = lfs_deorphan(lfs); | ||||||
|     if (err) { |     if (err) { | ||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|         if (lfs->free.begin == lfs->free.end) { |     // scan again or die trying | ||||||
|             // Still can't allocate a block? check for orphans |     return lfs_alloc_scan(lfs, block); | ||||||
|             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); |  | ||||||
|     if (err) { |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return lfs_bd_erase(lfs, *block); |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -343,7 +300,12 @@ static int lfs_index_append(lfs_t *lfs, lfs_block_t *headp, | |||||||
|  |  | ||||||
|     while (ioff % lfs->words == 0) { |     while (ioff % lfs->words == 0) { | ||||||
|         lfs_block_t nhead; |         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) { |         if (err) { | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
| @@ -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; |         lfs_off_t woff = file->size % lfs->cfg->block_size; | ||||||
|  |  | ||||||
|         if (file->size == 0) { |         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) { |             if (err) { | ||||||
|                 return 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->head = file->wblock; | ||||||
|             file->windex = 0; |             file->windex = 0; | ||||||
|         } else if (woff == 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) { |             if (err) { | ||||||
|                 return 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) { | static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { | ||||||
|     lfs->cfg = cfg; |     lfs->cfg = cfg; | ||||||
|     lfs->words = lfs->cfg->block_size / sizeof(uint32_t); |     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) { |     if (lfs->cfg->read_buffer) { | ||||||
|         lfs->rcache.buffer = lfs->cfg->read_buffer; |         lfs->rcache.buffer = lfs->cfg->read_buffer; | ||||||
|     } else { |     } 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) { |     if (lfs->cfg->prog_buffer) { | ||||||
|         lfs->pcache.buffer = lfs->cfg->prog_buffer; |         lfs->pcache.buffer = lfs->cfg->prog_buffer; | ||||||
|     } else { |     } 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; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1167,9 +1151,10 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) { | |||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Create free list |     // Create free lookahead | ||||||
|     lfs->free.begin = 0; |     memset(lfs->free.lookahead, 0, lfs->cfg->lookahead/8); | ||||||
|     lfs->free.end = lfs->cfg->block_count-1; |     lfs->free.start = 0; | ||||||
|  |     lfs->free.off = 0; | ||||||
|  |  | ||||||
|     // Create superblock dir |     // Create superblock dir | ||||||
|     lfs_dir_t superdir; |     lfs_dir_t superdir; | ||||||
| @@ -1235,6 +1220,11 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { | |||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // setup free lookahead | ||||||
|  |     lfs->free.start = -lfs->cfg->lookahead; | ||||||
|  |     lfs->free.off = lfs->cfg->lookahead; | ||||||
|  |  | ||||||
|  |     // load superblock | ||||||
|     lfs_dir_t dir; |     lfs_dir_t dir; | ||||||
|     lfs_superblock_t superblock; |     lfs_superblock_t superblock; | ||||||
|     err = lfs_dir_fetch(lfs, &dir, (const lfs_block_t[2]){0, 1}); |     err = lfs_dir_fetch(lfs, &dir, (const lfs_block_t[2]){0, 1}); | ||||||
|   | |||||||
							
								
								
									
										17
									
								
								lfs.h
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								lfs.h
									
									
									
									
									
								
							| @@ -76,11 +76,18 @@ struct lfs_config { | |||||||
|     // Number of erasable blocks on the device. |     // Number of erasable blocks on the device. | ||||||
|     lfs_size_t block_count; |     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. |     // Optional, statically allocated read buffer. Must be read sized. | ||||||
|     void *read_buffer; |     void *read_buffer; | ||||||
|  |  | ||||||
|     // Optional, statically allocated program buffer. Must be program sized. |     // Optional, statically allocated program buffer. Must be program sized. | ||||||
|     void *prog_buffer; |     void *prog_buffer; | ||||||
|  |  | ||||||
|  |     // Optional, statically allocated lookahead buffer. | ||||||
|  |     // Must be 1 bit per lookahead block. | ||||||
|  |     void *lookahead_buffer; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| // File info structure | // 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_size_t words;       // number of 32-bit words that can fit in a block | ||||||
|  |  | ||||||
|     lfs_block_t root[2]; |     lfs_block_t root[2]; | ||||||
|     struct { |  | ||||||
|         lfs_block_t begin; |  | ||||||
|         lfs_block_t end; |  | ||||||
|     } free; |  | ||||||
|  |  | ||||||
|     struct { |     struct { | ||||||
|         lfs_block_t block; |         lfs_block_t block; | ||||||
| @@ -172,7 +175,11 @@ typedef struct lfs { | |||||||
|         uint8_t *buffer; |         uint8_t *buffer; | ||||||
|     } rcache, pcache; |     } rcache, pcache; | ||||||
|  |  | ||||||
|     uint32_t lookahead[LFS_CFG_LOOKAHEAD/32]; |     struct { | ||||||
|  |         lfs_block_t start; | ||||||
|  |         lfs_block_t off; | ||||||
|  |         uint32_t *lookahead; | ||||||
|  |     } free; | ||||||
| } lfs_t; | } lfs_t; | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -23,11 +23,6 @@ typedef uint32_t lfs_block_t; | |||||||
| #define LFS_NAME_MAX 255 | #define LFS_NAME_MAX 255 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| // Lookahead distance |  | ||||||
| #ifndef LFS_CFG_LOOKAHEAD |  | ||||||
| #define LFS_CFG_LOOKAHEAD 128 |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| // Logging operations | // Logging operations | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #define LFS_ERROR(fmt, ...) printf("lfs error: " fmt "\n", __VA_ARGS__) | #define LFS_ERROR(fmt, ...) printf("lfs error: " fmt "\n", __VA_ARGS__) | ||||||
|   | |||||||
| @@ -70,6 +70,10 @@ uintmax_t res; | |||||||
| #define LFS_BLOCK_COUNT 1024 | #define LFS_BLOCK_COUNT 1024 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | #ifndef LFS_LOOKAHEAD | ||||||
|  | #define LFS_LOOKAHEAD 128 | ||||||
|  | #endif | ||||||
|  |  | ||||||
| const struct lfs_config cfg = {{ | const struct lfs_config cfg = {{ | ||||||
|     .context = &bd, |     .context = &bd, | ||||||
|     .read  = &lfs_emubd_read, |     .read  = &lfs_emubd_read, | ||||||
| @@ -81,6 +85,7 @@ const struct lfs_config cfg = {{ | |||||||
|     .prog_size   = LFS_PROG_SIZE, |     .prog_size   = LFS_PROG_SIZE, | ||||||
|     .block_size  = LFS_BLOCK_SIZE, |     .block_size  = LFS_BLOCK_SIZE, | ||||||
|     .block_count = LFS_BLOCK_COUNT, |     .block_count = LFS_BLOCK_COUNT, | ||||||
|  |     .lookahead   = LFS_LOOKAHEAD, | ||||||
| }}; | }}; | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user