Skip to content

Commit

Permalink
Revised free-list structure to adopt a lazy scanning allocator of sorts
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
geky committed Mar 26, 2017
1 parent ed674e8 commit f566846
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 65 deletions.
72 changes: 14 additions & 58 deletions lfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ static lfs_error_t lfs_bd_cmp(lfs_t *lfs,


static lfs_error_t lfs_alloc(lfs_t *lfs, lfs_ino_t *ino);
static lfs_error_t lfs_free(lfs_t *lfs, lfs_ino_t ino);


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

// Memory managment
static lfs_error_t lfs_alloc(lfs_t *lfs, lfs_ino_t *ino) {
// TODO save slot for freeing?
lfs_error_t err = lfs_ifind(lfs, lfs->free.d.head,
lfs->free.end, lfs->free.off, ino);
if (err) {
return err;
}

lfs->free.off = lfs_inext(lfs, lfs->free.off);

return lfs->ops->erase(lfs->bd, *ino, 0, lfs->info.erase_size);
}

static lfs_error_t lfs_free(lfs_t *lfs, lfs_ino_t ino) {
return lfs_iappend(lfs, &lfs->free.d.head, &lfs->free.end, ino);
}

static lfs_error_t lfs_free_flush(lfs_t *lfs) {
lfs_size_t wcount = lfs->info.erase_size/4;

for (lfs_word_t i = lfs->free.begin / wcount;
i + wcount < lfs->free.off; i += wcount) {
lfs_ino_t block;
int err = lfs_ifind_block(lfs, lfs->free.d.head,
lfs->free.end, i, &block);
if (err) {
return err;
}
if (lfs->free.d.begin != lfs->free.d.end) {
*ino = lfs->free.d.begin;
lfs->free.d.begin += 1;

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

if (lfs->free.off != lfs->free.d.off || lfs->free.end != lfs->free.d.end) {
// TODO handle overflow?
lfs->free.d.rev += 1;
}
lfs->free.d.off = lfs->free.off;
lfs->free.d.end = lfs->free.end;

return 0;
// TODO find next stride of free blocks
// TODO verify no strides exist where begin > current begin
// note: begin = 0 is invalid (superblock)
return LFS_ERROR_NO_SPACE;
}

lfs_error_t lfs_check(lfs_t *lfs, lfs_ino_t block) {
Expand Down Expand Up @@ -349,7 +320,6 @@ lfs_error_t lfs_dir_make(lfs_t *lfs, lfs_dir_t *dir, lfs_ino_t parent[2]) {
dir->d.size = sizeof(struct lfs_disk_dir);
dir->d.tail[0] = 0;
dir->d.tail[1] = 0;
lfs_free_flush(lfs);
dir->d.free = lfs->free.d;

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

cwd.d.rev += 1;
cwd.d.size += entry.d.len;
lfs_free_flush(lfs);
cwd.d.free = lfs->free.d;

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

cwd.d.rev += 1;
cwd.d.size += file->entry.d.len;
lfs_free_flush(lfs);
cwd.d.free = lfs->free.d;

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

cwd.d.rev += 1;
lfs_free_flush(lfs);
cwd.d.free = lfs->free.d;

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

// TODO make sure that erase clobbered blocks

{ // Create free list
lfs->free = (lfs_free_t){
.begin = 1,
.off = 1,
.end = 1,
{
lfs_size_t block_count = lfs->info.total_size / lfs->info.erase_size;

.d.rev = 1,
.d.head = 2,
.d.off = 1,
.d.end = 1,
// Create free list
lfs->free = (lfs_free_t){
.d.begin = 2,
.d.end = block_count,
};

lfs_size_t block_count = lfs->info.total_size / lfs->info.erase_size;
for (lfs_ino_t i = 3; i < block_count; i++) {
lfs_error_t err = lfs_free(lfs, i);
if (err) {
return err;
}
}
}

{
Expand Down
9 changes: 2 additions & 7 deletions lfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ enum lfs_error {
LFS_ERROR_EXISTS = -5,
LFS_ERROR_NOT_DIR = -6,
LFS_ERROR_INVALID = -7,
LFS_ERROR_NO_SPACE = -8,
};

enum lfs_type {
Expand All @@ -38,14 +39,8 @@ enum lfs_open_flags {
};

typedef struct lfs_free {
lfs_word_t begin;
lfs_word_t off;
lfs_word_t end;

lfs_disk_struct lfs_disk_free {
lfs_word_t rev;
lfs_ino_t head;
lfs_word_t off;
lfs_word_t begin;
lfs_word_t end;
} d;
} lfs_free_t;
Expand Down

0 comments on commit f566846

Please sign in to comment.