Allocate content extents by run instead of by block

This commit is contained in:
Andrea Mazzoleni 2026-09-09 18:06:09 +02:00
parent b14f703d2b
commit 17b24c8d21
4 changed files with 34 additions and 34 deletions

View file

@ -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);

View file

@ -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.

View file

@ -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;

View file

@ -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 */