Skip to content

Commit f566846

Browse files
committed
Revised free-list structure to adopt a lazy scanning allocator of sorts
The free-list structure, while efficient for allocations, had one big issue: complexity. Storing free blocks as a simple fifo made sense when dealing with a single file, but as soon as you have two files open for writing, updating the free list atomicly when the two files can not necessarily even be written atomicly proved problematic. It's a solvable problem, but requires many writes to keep track of everything. Now changing direction to pursue a more "drop it on the floor" strategy. Since allocated blocks are tracked by the filesystem, we can simply subtract from all available blocks the blocks we know of to allocate new blocks. This is very expensive (O(blocks in use * blocks on device)), but greatly simplifies any interactions that result in deallocated blocks. Additionally, it's impossible to corrupt the free list structure during a power failure. Anything blocks that aren't tracked are simply "dropped on the floor", and can be allocated later. There's still a bit of work around the actually allocator to make it run in a somewhat reasonable frame of time while still avoiding dynamic allocations. Currently looking at a bit-vector of free blocks so at least strides of blocks can be skipped in a single filesystem iteration.
1 parent ed674e8 commit f566846

File tree

2 files changed

+16
-65
lines changed

2 files changed

+16
-65
lines changed

lfs.c

Lines changed: 14 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ static lfs_error_t lfs_bd_cmp(lfs_t *lfs,
5252

5353

5454
static lfs_error_t lfs_alloc(lfs_t *lfs, lfs_ino_t *ino);
55-
static lfs_error_t lfs_free(lfs_t *lfs, lfs_ino_t ino);
5655

5756

5857
// Next index offset
@@ -161,45 +160,17 @@ static lfs_error_t lfs_iappend(lfs_t *lfs, lfs_ino_t *headp,
161160

162161
// Memory managment
163162
static lfs_error_t lfs_alloc(lfs_t *lfs, lfs_ino_t *ino) {
164-
// TODO save slot for freeing?
165-
lfs_error_t err = lfs_ifind(lfs, lfs->free.d.head,
166-
lfs->free.end, lfs->free.off, ino);
167-
if (err) {
168-
return err;
169-
}
170-
171-
lfs->free.off = lfs_inext(lfs, lfs->free.off);
172-
173-
return lfs->ops->erase(lfs->bd, *ino, 0, lfs->info.erase_size);
174-
}
175-
176-
static lfs_error_t lfs_free(lfs_t *lfs, lfs_ino_t ino) {
177-
return lfs_iappend(lfs, &lfs->free.d.head, &lfs->free.end, ino);
178-
}
179-
180-
static lfs_error_t lfs_free_flush(lfs_t *lfs) {
181-
lfs_size_t wcount = lfs->info.erase_size/4;
182-
183-
for (lfs_word_t i = lfs->free.begin / wcount;
184-
i + wcount < lfs->free.off; i += wcount) {
185-
lfs_ino_t block;
186-
int err = lfs_ifind_block(lfs, lfs->free.d.head,
187-
lfs->free.end, i, &block);
188-
if (err) {
189-
return err;
190-
}
163+
if (lfs->free.d.begin != lfs->free.d.end) {
164+
*ino = lfs->free.d.begin;
165+
lfs->free.d.begin += 1;
191166

192-
lfs_free(lfs, block);
167+
return lfs->ops->erase(lfs->bd, *ino, 0, lfs->info.erase_size);
193168
}
194169

195-
if (lfs->free.off != lfs->free.d.off || lfs->free.end != lfs->free.d.end) {
196-
// TODO handle overflow?
197-
lfs->free.d.rev += 1;
198-
}
199-
lfs->free.d.off = lfs->free.off;
200-
lfs->free.d.end = lfs->free.end;
201-
202-
return 0;
170+
// TODO find next stride of free blocks
171+
// TODO verify no strides exist where begin > current begin
172+
// note: begin = 0 is invalid (superblock)
173+
return LFS_ERROR_NO_SPACE;
203174
}
204175

205176
lfs_error_t lfs_check(lfs_t *lfs, lfs_ino_t block) {
@@ -349,7 +320,6 @@ lfs_error_t lfs_dir_make(lfs_t *lfs, lfs_dir_t *dir, lfs_ino_t parent[2]) {
349320
dir->d.size = sizeof(struct lfs_disk_dir);
350321
dir->d.tail[0] = 0;
351322
dir->d.tail[1] = 0;
352-
lfs_free_flush(lfs);
353323
dir->d.free = lfs->free.d;
354324

355325
if (parent) {
@@ -514,7 +484,6 @@ lfs_error_t lfs_mkdir(lfs_t *lfs, const char *path) {
514484

515485
cwd.d.rev += 1;
516486
cwd.d.size += entry.d.len;
517-
lfs_free_flush(lfs);
518487
cwd.d.free = lfs->free.d;
519488

520489
return lfs_pair_commit(lfs, entry.dir,
@@ -565,7 +534,6 @@ lfs_error_t lfs_file_open(lfs_t *lfs, lfs_file_t *file,
565534

566535
cwd.d.rev += 1;
567536
cwd.d.size += file->entry.d.len;
568-
lfs_free_flush(lfs);
569537
cwd.d.free = lfs->free.d;
570538

571539
return lfs_pair_commit(lfs, file->entry.dir,
@@ -612,7 +580,6 @@ lfs_error_t lfs_file_close(lfs_t *lfs, lfs_file_t *file) {
612580
file->entry.d.u.file.size = file->size;
613581

614582
cwd.d.rev += 1;
615-
lfs_free_flush(lfs);
616583
cwd.d.free = lfs->free.d;
617584

618585
return lfs_pair_commit(lfs, file->entry.dir,
@@ -732,25 +699,14 @@ lfs_error_t lfs_format(lfs_t *lfs) {
732699

733700
// TODO make sure that erase clobbered blocks
734701

735-
{ // Create free list
736-
lfs->free = (lfs_free_t){
737-
.begin = 1,
738-
.off = 1,
739-
.end = 1,
702+
{
703+
lfs_size_t block_count = lfs->info.total_size / lfs->info.erase_size;
740704

741-
.d.rev = 1,
742-
.d.head = 2,
743-
.d.off = 1,
744-
.d.end = 1,
705+
// Create free list
706+
lfs->free = (lfs_free_t){
707+
.d.begin = 2,
708+
.d.end = block_count,
745709
};
746-
747-
lfs_size_t block_count = lfs->info.total_size / lfs->info.erase_size;
748-
for (lfs_ino_t i = 3; i < block_count; i++) {
749-
lfs_error_t err = lfs_free(lfs, i);
750-
if (err) {
751-
return err;
752-
}
753-
}
754710
}
755711

756712
{

lfs.h

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ enum lfs_error {
1919
LFS_ERROR_EXISTS = -5,
2020
LFS_ERROR_NOT_DIR = -6,
2121
LFS_ERROR_INVALID = -7,
22+
LFS_ERROR_NO_SPACE = -8,
2223
};
2324

2425
enum lfs_type {
@@ -38,14 +39,8 @@ enum lfs_open_flags {
3839
};
3940

4041
typedef struct lfs_free {
41-
lfs_word_t begin;
42-
lfs_word_t off;
43-
lfs_word_t end;
44-
4542
lfs_disk_struct lfs_disk_free {
46-
lfs_word_t rev;
47-
lfs_ino_t head;
48-
lfs_word_t off;
43+
lfs_word_t begin;
4944
lfs_word_t end;
5045
} d;
5146
} lfs_free_t;

0 commit comments

Comments
 (0)