From a1ff18fcbde82daa0372b7ea3946d6b3cf9739fb Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Wed, 9 Sep 2026 21:58:54 +0200 Subject: [PATCH] Resolve file-to-parity mappings once per extent while writing content --- cmdline/elem.c | 10 +++++++++- cmdline/elem.h | 22 +++++++++++++++++++--- cmdline/selftest.c | 39 +++++++++++++++++++++++++++++++++++++++ cmdline/state.c | 18 +++++++++++++++--- 4 files changed, 82 insertions(+), 7 deletions(-) diff --git a/cmdline/elem.c b/cmdline/elem.c index 9d1c124d..494bab4b 100644 --- a/cmdline/elem.c +++ b/cmdline/elem.c @@ -1475,7 +1475,7 @@ struct snapraid_file* fs_par2file_find(struct snapraid_disk* disk, block_off_t p return file; } -block_off_t fs_file2par_find(struct snapraid_disk* disk, struct snapraid_file* file, block_off_t file_pos) +block_off_t fs_file2par_find_run(struct snapraid_disk* disk, struct snapraid_file* file, block_off_t file_pos, block_off_t* count) { struct snapraid_extent* extent; block_off_t ret; @@ -1488,12 +1488,20 @@ block_off_t fs_file2par_find(struct snapraid_disk* disk, struct snapraid_file* f return POS_NULL; } + if (count) + *count = extent->count - (file_pos - extent->file_pos); + ret = extent->parity_pos + (file_pos - extent->file_pos); fs_unlock(disk); return ret; } +block_off_t fs_file2par_find(struct snapraid_disk* disk, struct snapraid_file* file, block_off_t file_pos) +{ + return fs_file2par_find_run(disk, file, file_pos, 0); +} + /** * Allocate a run of file blocks mapping it to a run of parity positions. * diff --git a/cmdline/elem.h b/cmdline/elem.h index 79e9839a..d93d212f 100644 --- a/cmdline/elem.h +++ b/cmdline/elem.h @@ -1209,13 +1209,21 @@ static inline struct snapraid_file* fs_par2file_get(struct snapraid_disk* disk, block_off_t fs_file2par_find(struct snapraid_disk* disk, struct snapraid_file* file, block_off_t file_pos); /** - * Get the parity position from the file position. + * Get the parity position from the file position. If count is specified, return + * the number of blocks remaining in the same extent, including the current one. + * Return POS_NULL if no parity is allocated. */ -static inline block_off_t fs_file2par_get(struct snapraid_disk* disk, struct snapraid_file* file, block_off_t file_pos) +block_off_t fs_file2par_find_run(struct snapraid_disk* disk, struct snapraid_file* file, block_off_t file_pos, block_off_t* count); + +/** + * Get the parity position and the number of blocks remaining in the same extent, + * including the current one, from the file position. + */ +static inline block_off_t fs_file2par_get_run(struct snapraid_disk* disk, struct snapraid_file* file, block_off_t file_pos, block_off_t* count) { block_off_t ret; - ret = fs_file2par_find(disk, file, file_pos); + ret = fs_file2par_find_run(disk, file, file_pos, count); if (ret == POS_NULL) { /* LCOV_EXCL_START */ log_fatal(EINTERNAL, "Internal inconsistency: Resolving file '%s' at position '%" PRIu64 "/%" PRIu64 "' in disk '%s'\n", file->sub, file_pos, file->blockmax, disk->name); @@ -1226,6 +1234,14 @@ static inline block_off_t fs_file2par_get(struct snapraid_disk* disk, struct sna return ret; } +/** + * Get the parity position from the file position. + */ +static inline block_off_t fs_file2par_get(struct snapraid_disk* disk, struct snapraid_file* file, block_off_t file_pos) +{ + return fs_file2par_get_run(disk, file, file_pos, 0); +} + /** * Get the block from the parity position. * Return BLOCK_NULL==0 if the block is over the end of the disk or not used. diff --git a/cmdline/selftest.c b/cmdline/selftest.c index 89fc2f21..6eb426d9 100644 --- a/cmdline/selftest.c +++ b/cmdline/selftest.c @@ -1852,6 +1852,42 @@ static void test_stream(void) } } +static void test_extent_run(void) +{ + struct snapraid_disk* disk; + struct snapraid_file* file; + block_off_t parity_pos; + block_off_t count; + + disk = disk_alloc("test", "", 0, "", 0); + file = file_alloc(4096, "file", 6 * 4096, 0, 0, 0); + + fs_allocate(disk, 10, file, 0, 3); + fs_allocate(disk, 20, file, 3, 2); + fs_allocate(disk, 22, file, 5, 1); + + parity_pos = fs_file2par_get_run(disk, file, 0, &count); + if (parity_pos != 10 || count != 3) { + log_fatal(EINTERNAL, "test_extent_run: first extent failed\n"); + exit(EXIT_FAILURE); + } + + parity_pos = fs_file2par_get_run(disk, file, 1, &count); + if (parity_pos != 11 || count != 2) { + log_fatal(EINTERNAL, "test_extent_run: extent remainder failed\n"); + exit(EXIT_FAILURE); + } + + parity_pos = fs_file2par_get_run(disk, file, 3, &count); + if (parity_pos != 20 || count != 3) { + log_fatal(EINTERNAL, "test_extent_run: extended extent failed\n"); + exit(EXIT_FAILURE); + } + + disk_free(disk); + file_free(file); +} + static void test_raid(void) { /* vandermonde raid parity generation with 32 data disks */ @@ -2551,6 +2587,9 @@ void test(int argc, char* argv[]) /* buffered streaming i/o and crc verification */ test_stream(); + /* extent-aware file-to-parity run lookup */ + test_extent_run(); + /* wildcard and path pattern matching */ test_wnmatch(); diff --git a/cmdline/state.c b/cmdline/state.c index 6197a0a5..f7932de5 100644 --- a/cmdline/state.c +++ b/cmdline/state.c @@ -4161,7 +4161,9 @@ static void* state_write_thread(void* arg) struct snapraid_block* block = file_block(file, begin); unsigned char* end_block_ptr = (unsigned char*)block + block_stride; unsigned v_state = block_state_get(block); - block_off_t v_pos = fs_file2par_get(disk, file, begin); + block_off_t extent_count; + block_off_t v_pos = fs_file2par_get_run(disk, file, begin, &extent_count); + block_off_t extent_end = begin + extent_count; block_off_t v_count; block_off_t end; @@ -4172,8 +4174,18 @@ static void* state_write_thread(void* arg) block = (struct snapraid_block*)end_block_ptr; if (v_state != block_state_get(block)) break; - if (v_pos + (end - begin) != fs_file2par_get(disk, file, end)) - break; + + /* Resolve the mapping only when crossing an extent boundary. */ + if (end == extent_end) { + block_off_t next_count; + block_off_t next_pos = fs_file2par_get_run(disk, file, end, &next_count); + + if (v_pos + (end - begin) != next_pos) + break; + + extent_end += next_count; + } + end_block_ptr += block_stride; ++end; }