From 17b24c8d21fc5546a025e2cee7775fd54517371c Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Wed, 9 Sep 2026 18:06:09 +0200 Subject: [PATCH] Allocate content extents by run instead of by block --- cmdline/elem.c | 32 +++++++++++++++----------------- cmdline/elem.h | 4 ++-- cmdline/scan.c | 2 +- cmdline/state.c | 30 ++++++++++++++++-------------- 4 files changed, 34 insertions(+), 34 deletions(-) diff --git a/cmdline/elem.c b/cmdline/elem.c index 428aadea..9d1c124d 100644 --- a/cmdline/elem.c +++ b/cmdline/elem.c @@ -1495,34 +1495,40 @@ block_off_t fs_file2par_find(struct snapraid_disk* disk, struct snapraid_file* f } /** - * Allocate a file block mapping it to a parity position. + * Allocate a run of file blocks mapping it to a run of parity positions. * * If the block is contiguous with the previous block of the file and follows * the current extent in parity space, the existing extent is extended in-place. * Otherwise, a new extent is created and inserted into the parity and file trees. * * Callers ensure allocations are sequential and do not overwrite live blocks. - * To keep per-block allocation fast, full overlap checks across all existing + * To keep run allocation fast, full overlap checks across all existing * extents are omitted here; global non-overlap and complete coverage invariants * are verified in batch by fs_check() at major checkpoints (after read/scan, * before write). */ -void fs_allocate(struct snapraid_disk* disk, block_off_t parity_pos, struct snapraid_file* file, block_off_t file_pos) +void fs_allocate(struct snapraid_disk* disk, block_off_t parity_pos, struct snapraid_file* file, block_off_t file_pos, block_off_t count) { struct snapraid_extent* extent; struct snapraid_extent* parity_extent; struct snapraid_extent* file_extent; - if (parity_pos >= BLOCK_MAX) { + if (count > BLOCK_MAX || parity_pos > BLOCK_MAX - count) { /* LCOV_EXCL_START */ #if SIZE_MAX == UINT32_MAX - log_fatal(ESOFT, "Parity position %" PRIu64 " is too large for a 32-bit build. Use a 64-bit build or increase the block size.\n", parity_pos); + log_fatal(ESOFT, "Parity range %" PRIu64 ":%" PRIu64 " is too large for a 32-bit build. Use a 64-bit build or increase the block size.\n", parity_pos, count); #else - log_fatal(ESOFT, "Parity position %" PRIu64 " is too large. Increase the block size.\n", parity_pos); + log_fatal(ESOFT, "Parity range %" PRIu64 ":%" PRIu64 " is too large. Increase the block size.\n", parity_pos, count); #endif exit(EXIT_FAILURE); /* LCOV_EXCL_STOP */ } + if (count > file->blockmax || file_pos > file->blockmax - count) { + /* LCOV_EXCL_START */ + log_fatal(EINTERNAL, "Internal inconsistency: Allocating file '%s' at position '%" PRIu64 ":%" PRIu64 "/%" PRIu64 "' in disk '%s'\n", file->sub, file_pos, count, file->blockmax, disk->name); + os_abort(); + /* LCOV_EXCL_STOP */ + } fs_lock(disk); @@ -1531,14 +1537,6 @@ void fs_allocate(struct snapraid_disk* disk, block_off_t parity_pos, struct snap extent = fs_file2extent_get_unlock(disk, &disk->fs_last, file, file_pos - 1); if (extent != 0 && parity_pos == extent->parity_pos + extent->count) { - /* check if the position will go outside the limit */ - if (extent->parity_pos + extent->count == POS_NULL) { - /* LCOV_EXCL_START */ - log_fatal(EINTERNAL, "Internal inconsistency: Parity position overflow allocating file '%s' at position '%" PRIu64 "/%" PRIu64 "' with count '%" PRIu64 "' in disk '%s'\n", file->sub, file_pos, file->blockmax, extent->count, disk->name); - os_abort(); - /* LCOV_EXCL_STOP */ - } - /* ensure that we are extending the extent at the end */ if (file_pos != extent->file_pos + extent->count) { /* LCOV_EXCL_START */ @@ -1547,8 +1545,8 @@ void fs_allocate(struct snapraid_disk* disk, block_off_t parity_pos, struct snap /* LCOV_EXCL_STOP */ } - /* extend the existing extent */ - ++extent->count; + /* extend the existing extent with the whole run */ + extent->count += count; fs_unlock(disk); return; @@ -1556,7 +1554,7 @@ void fs_allocate(struct snapraid_disk* disk, block_off_t parity_pos, struct snap } /* a extent doesn't exist, and we have to create a new one */ - extent = extent_alloc(parity_pos, file, file_pos, 1); + extent = extent_alloc(parity_pos, file, file_pos, count); /* insert the extent in the trees */ parity_extent = tommy_tree_insert(&disk->fs_parity, &extent->parity_node, extent); diff --git a/cmdline/elem.h b/cmdline/elem.h index 3ee1c0b4..79e9839a 100644 --- a/cmdline/elem.h +++ b/cmdline/elem.h @@ -1154,14 +1154,14 @@ int fs_is_empty(struct snapraid_disk* disk, block_off_t blockmax); int fs_check(struct snapraid_disk* disk); /** - * Allocate a parity position for the specified file position. + * Allocate a run of parity positions for the specified file positions. * * After this call you can use the par2file/par2block operations * to query the relation. * * \note This function is NOT thread-safe as it uses the disk cache. + */ -void fs_allocate(struct snapraid_disk* disk, block_off_t parity_pos, struct snapraid_file* file, block_off_t file_pos); +void fs_allocate(struct snapraid_disk* disk, block_off_t parity_pos, struct snapraid_file* file, block_off_t file_pos, block_off_t count); /** * Deallocate the parity position. diff --git a/cmdline/scan.c b/cmdline/scan.c index be036ab5..5800b7a5 100644 --- a/cmdline/scan.c +++ b/cmdline/scan.c @@ -346,7 +346,7 @@ static void scan_file_allocate(struct snapraid_scan* scan, struct snapraid_file* } /* store in the disk map, after invalidating all the other blocks */ - fs_allocate(disk, parity_pos, file, i); + fs_allocate(disk, parity_pos, file, i, 1); /* set the new free position */ disk->first_free_block = parity_pos + 1; diff --git a/cmdline/state.c b/cmdline/state.c index 0a9a6ca4..28b4edd5 100644 --- a/cmdline/state.c +++ b/cmdline/state.c @@ -2440,6 +2440,8 @@ static void state_read_content(struct snapraid_state* state, const char* path, S while (v_idx < file->blockmax) { block_off_t v_pos; block_off_t v_count; + block_off_t v_file_pos; + block_off_t v_end; /* get the "subcommand */ c = sgetc(f); @@ -2460,7 +2462,7 @@ static void state_read_content(struct snapraid_state* state, const char* path, S /* LCOV_EXCL_STOP */ } - if (v_count > file->blockmax || v_idx > file->blockmax - v_count) { + if (v_count == 0 || v_count > file->blockmax || v_idx > file->blockmax - v_count) { /* LCOV_EXCL_START */ decoding_error(path, f); log_fatal(EINTERNAL, "Internal inconsistency: Block number out of range\n"); @@ -2476,8 +2478,11 @@ static void state_read_content(struct snapraid_state* state, const char* path, S /* LCOV_EXCL_STOP */ } + v_file_pos = v_idx; + v_end = v_idx + v_count; + /* fill the blocks in the run */ - while (v_count) { + while (v_idx < v_end) { struct snapraid_block* block = fs_file2block_get(file, v_idx); switch (c) { @@ -2532,14 +2537,12 @@ static void state_read_content(struct snapraid_state* state, const char* path, S block_state_set(block, BLOCK_STATE_CHG); } - /* set the parity association */ - fs_allocate(disk, v_pos, file, v_idx); - /* go to the next block */ ++v_idx; - ++v_pos; - --v_count; } + + /* set the parity association for the whole run */ + fs_allocate(disk, v_pos, file, v_file_pos, v_count); } /* stat */ @@ -2729,7 +2732,7 @@ static void state_read_content(struct snapraid_state* state, const char* path, S /* LCOV_EXCL_STOP */ } - if (v_count > blockmax || v_pos > blockmax - v_count) { + if (v_count == 0 || v_count > blockmax || v_pos > blockmax - v_count) { /* LCOV_EXCL_START */ decoding_error(path, f); log_fatal(EINTERNAL, "Internal inconsistency: Hole size %" PRIu64 "/%" PRIu64 "!\n", blockmax, v_pos + v_count); @@ -2767,7 +2770,7 @@ static void state_read_content(struct snapraid_state* state, const char* path, S /* process all blocks */ v_idx = 0; - while (v_count) { + while (v_idx < v_count) { struct snapraid_block* block = fs_file2block_get(deleted, v_idx); /* set the block as deleted */ @@ -2782,14 +2785,13 @@ static void state_read_content(struct snapraid_state* state, const char* path, S /* LCOV_EXCL_STOP */ } - /* insert the block in the block array */ - fs_allocate(disk, v_pos, deleted, v_idx); - /* go to next block */ - ++v_pos; ++v_idx; - --v_count; } + + /* insert the whole deleted run in the extent map */ + fs_allocate(disk, v_pos, deleted, 0, v_count); + v_pos += v_count; break; case 'O' : /* go to the next run */