From 25fe6a252c4c7d861b58594b0647d34d8541de63 Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Mon, 5 Oct 2015 22:48:16 +0200 Subject: [PATCH] Extend filesystem support for deallocation in the middle This could happen in some special cases --- Makefile.am | 27 +++++++++++++++++++++++++++ cmdline/elem.c | 33 ++++++++++++++++++++++++++++----- cmdline/elem.h | 2 +- cmdline/state.c | 2 +- 4 files changed, 57 insertions(+), 7 deletions(-) diff --git a/Makefile.am b/Makefile.am index e1bdf58e..c406f2b7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -227,6 +227,33 @@ if HAVE_SYMLINK endif $(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check $(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -a check + echo -- Filesystem allocation test + head -c 8192 /dev/zero > bench/disk1/TEST + head -c 8192 /dev/zero > bench/disk2/TEST + $(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync +# Now delete the file, and do a partial sync to force a deallocation in the middle of a file + rm bench/disk1/TEST + $(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -E -S 1 -B 1 sync +# Now force a deallocation at the end + $(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -E -S 7 -B 1 sync + rm bench/disk2/TEST + $(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -E sync + echo -- Filesystem fragmentation test + head -c 8192 /dev/zero > bench/disk1/TEST1 + head -c 8192 /dev/zero > bench/disk1/TEST2 + head -c 8192 /dev/zero > bench/disk1/TEST3 + head -c 8192 /dev/zero > bench/disk1/TEST4 + head -c 8192 /dev/zero > bench/disk1/TEST5 + head -c 8192 /dev/zero > bench/disk1/TEST6 + $(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync +# Now delete some files, and create a bigger one to fill the holes + rm bench/disk1/TEST1 + rm bench/disk1/TEST3 + rm bench/disk1/TEST5 + head -c 65536 /dev/zero > bench/disk1/TESTX + $(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync + rm -r bench/disk1/TEST* + $(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -E sync echo --- Create some files $(TESTENV) ./mktest$(EXEEXT) generate 1 6 $(CHECKCOUNT) $(CHECKSIZE) echo DUP > bench/disk1/DUP1 diff --git a/cmdline/elem.c b/cmdline/elem.c index 2b5628b3..6e79039a 100644 --- a/cmdline/elem.c +++ b/cmdline/elem.c @@ -1166,7 +1166,7 @@ void fs_allocate(struct snapraid_disk* disk, block_off_t parity_pos, struct snap /* a chunk doesn't exist, and we have to create a new one */ chunk = chunk_alloc(parity_pos, file, file_pos, 1); - /* insert te chunk in the trees */ + /* insert the chunk in the trees */ parity_chunk = tommy_tree_insert(&disk->fs_parity, &chunk->parity_node, chunk); file_chunk = tommy_tree_insert(&disk->fs_file, &chunk->file_node, chunk); @@ -1184,6 +1184,10 @@ void fs_allocate(struct snapraid_disk* disk, block_off_t parity_pos, struct snap void fs_deallocate(struct snapraid_disk* disk, block_off_t parity_pos) { struct snapraid_chunk* chunk; + struct snapraid_chunk* second_chunk; + struct snapraid_chunk* parity_chunk; + struct snapraid_chunk* file_chunk; + block_off_t first_count, second_count; chunk = fs_par2chunk_get(disk, parity_pos); if (!chunk) { @@ -1221,10 +1225,29 @@ void fs_deallocate(struct snapraid_disk* disk, block_off_t parity_pos) return; } - /* LCOV_EXCL_START */ - log_fatal("Internal inconsistency when deallocating a chunk in the middle in disk '%s'\n", disk->name); - exit(EXIT_FAILURE); - /* LCOV_EXCL_STOP */ + /* otherwise it's in the middle */ + first_count = parity_pos - chunk->parity_pos; + second_count = chunk->count - first_count - 1; + + /* adjust the first chunk */ + chunk->count = first_count; + + /* allocate the second chunk */ + second_chunk = chunk_alloc(chunk->parity_pos + first_count + 1, chunk->file, chunk->file_pos + first_count + 1, second_count); + + /* insert the chunk in the trees */ + parity_chunk = tommy_tree_insert(&disk->fs_parity, &second_chunk->parity_node, second_chunk); + file_chunk = tommy_tree_insert(&disk->fs_file, &second_chunk->file_node, second_chunk); + + if (parity_chunk != second_chunk || file_chunk != second_chunk) { + /* LCOV_EXCL_START */ + log_fatal("Internal inconsistency when splitting a chunk in disk '%s'\n", disk->name); + exit(EXIT_FAILURE); + /* LCOV_EXCL_STOP */ + } + + /* store the last accessed chunk */ + disk->fs_last = second_chunk; } struct snapraid_block* fs_par2block_get(struct snapraid_disk* disk, block_off_t parity_pos) diff --git a/cmdline/elem.h b/cmdline/elem.h index fb15a9f3..bac5dd34 100644 --- a/cmdline/elem.h +++ b/cmdline/elem.h @@ -894,7 +894,7 @@ void fs_allocate(struct snapraid_disk* disk, block_off_t parity_pos, struct snap /** * Deallocate the parity position. * - * After this call you can the par2file/par2block operations + * After this call the par2file/par2block operations * won't find anymore the parity association. */ void fs_deallocate(struct snapraid_disk* disk, block_off_t pos); diff --git a/cmdline/state.c b/cmdline/state.c index 6cc1f4fe..ba176bb2 100644 --- a/cmdline/state.c +++ b/cmdline/state.c @@ -1403,7 +1403,7 @@ static int position_is_required(struct snapraid_state* state, block_off_t pos) * Checks if the info block is REQUIREQ. * * This is used to ensure that we keep the last check used for scrubbing. - * and the we add it when importing old context files. + * and that we add it when importing old context files. * * Note that you can have position without info blocks, for example * if all the blocks are not synced.