mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-12 02:10:54 +00:00
268 lines
8.3 KiB
C
268 lines
8.3 KiB
C
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
// Copyright (C) 2026 Andrea Mazzoleni
|
|
|
|
#include "os/portable.h"
|
|
|
|
#include "elem.h"
|
|
#include "parity.h"
|
|
#include "state.h"
|
|
#include "stream.h"
|
|
#include "locate.h"
|
|
|
|
struct snapraid_parity_entry {
|
|
uint32_t block_size;
|
|
block_off_t low; /**< Lower position in the parity */
|
|
block_off_t high; /**< Higher position in the parity */
|
|
block_off_t fragments; /**< Number of fragments in the parity */
|
|
struct snapraid_file* file;
|
|
struct snapraid_disk* disk;
|
|
tommy_node node;
|
|
};
|
|
|
|
struct snapraid_locate_info {
|
|
block_off_t block_max; /**< Number of blocks in the parity */
|
|
data_off_t parity_size; /**< Size in bytes of the parity */
|
|
block_off_t tail_block; /**< Number of blocks of the tail of the parity */
|
|
block_off_t low_to_free_tail_block; /**< First block that has to be freed */
|
|
};
|
|
|
|
static int parity_entry_compare(const void* void_a, const void* void_b)
|
|
{
|
|
const struct snapraid_parity_entry* entry_a = void_a;
|
|
const struct snapraid_parity_entry* entry_b = void_b;
|
|
|
|
if (entry_a->high < entry_b->high)
|
|
return -1;
|
|
if (entry_a->high > entry_b->high)
|
|
return 1;
|
|
|
|
if (entry_a->low < entry_b->low)
|
|
return -1;
|
|
if (entry_a->low > entry_b->low)
|
|
return 1;
|
|
|
|
return strcmp(entry_a->file->sub, entry_b->file->sub);
|
|
}
|
|
|
|
static void dump_entry(void* void_entry)
|
|
{
|
|
struct snapraid_parity_entry* entry = void_entry;
|
|
printf("%12" PRIu64 " ", entry->low * (uint64_t)entry->block_size);
|
|
printf("%12" PRIu64 " ", (entry->high - entry->low + 1) * (uint64_t)entry->block_size);
|
|
printf("%8" PRIu64 " ", entry->fragments);
|
|
printf("%s\n", fmt_term(entry->disk, entry->file->sub));
|
|
}
|
|
|
|
static void add_size_to_sum(void* arg, void* void_entry)
|
|
{
|
|
struct snapraid_parity_entry* entry = void_entry;
|
|
data_off_t* size_sum = arg;
|
|
|
|
*size_sum += entry->file->size;
|
|
}
|
|
|
|
static void collect_parity_block_file(uint32_t block_size, struct snapraid_disk* disk, struct snapraid_file* file, tommy_list* file_list, block_off_t low_to_free_tail_block)
|
|
{
|
|
block_off_t parity_low = 0; /* lower block */
|
|
block_off_t parity_high = 0; /* higher block */
|
|
block_off_t parity_fragments = 0; /* number of fragments */
|
|
|
|
for (block_off_t i = 0; i < file->blockmax; ++i) {
|
|
block_off_t parity_pos = fs_file2par_find(disk, file, i);
|
|
|
|
/* check if a valid position is found */
|
|
if (parity_pos == POS_NULL) {
|
|
/* block not yet allocated */
|
|
continue;
|
|
}
|
|
|
|
if (parity_fragments == 0) {
|
|
parity_low = parity_pos;
|
|
parity_high = parity_pos;
|
|
parity_fragments = 1;
|
|
} else {
|
|
if (parity_pos != parity_high + 1)
|
|
++parity_fragments;
|
|
parity_high = parity_pos;
|
|
}
|
|
}
|
|
|
|
if (parity_fragments == 0)
|
|
return; /* not allocated */
|
|
|
|
if (low_to_free_tail_block > 0 && parity_high < low_to_free_tail_block)
|
|
return; /* entry not in the range to free */
|
|
|
|
/* found a relevant block so add the corresponding file */
|
|
struct snapraid_parity_entry* entry = malloc_nofail(sizeof(struct snapraid_parity_entry));
|
|
entry->block_size = block_size;
|
|
entry->file = file;
|
|
entry->disk = disk;
|
|
entry->low = parity_low;
|
|
entry->high = parity_high;
|
|
entry->fragments = parity_fragments;
|
|
tommy_list_insert_tail(file_list, &entry->node, entry);
|
|
}
|
|
|
|
static void state_locate_info(struct snapraid_state* state, uint64_t parity_tail, struct snapraid_locate_info* info)
|
|
{
|
|
uint32_t block_size = state->block_size;
|
|
|
|
info->block_max = parity_allocated_size(state);
|
|
info->parity_size = info->block_max * (uint64_t)block_size;
|
|
|
|
info->tail_block = (parity_tail / block_size) + (parity_tail % block_size != 0);
|
|
|
|
if (info->tail_block > info->block_max)
|
|
info->low_to_free_tail_block = 0;
|
|
else
|
|
info->low_to_free_tail_block = info->block_max - info->tail_block;
|
|
}
|
|
|
|
void state_locate(struct snapraid_state* state, uint64_t parity_tail)
|
|
{
|
|
char buf[64];
|
|
uint32_t block_size = state->block_size;
|
|
block_off_t low_to_free_tail_block;
|
|
|
|
printf("SnapRAID locate report:\n");
|
|
printf("\n");
|
|
|
|
if (parity_tail == 0) {
|
|
printf("Locate all files\n\n");
|
|
low_to_free_tail_block = 0;
|
|
} else {
|
|
printf("Locate files within the tail of %sB of the parity\n\n", fmt_size(parity_tail, buf, sizeof(buf)));
|
|
|
|
struct snapraid_locate_info info;
|
|
state_locate_info(state, parity_tail, &info);
|
|
|
|
printf("Current parity size is %sB\n", fmt_size(info.parity_size, buf, sizeof(buf)));
|
|
if (info.tail_block > info.block_max) {
|
|
printf("Specified tail greater than the parity size! Operate on the full parity\n");
|
|
}
|
|
|
|
low_to_free_tail_block = info.low_to_free_tail_block;
|
|
}
|
|
|
|
msg_progress("Collecting files with offset greater or equal to %" PRIu64 "\n", low_to_free_tail_block * (uint64_t)block_size);
|
|
|
|
tommy_list files;
|
|
tommy_list_init(&files);
|
|
for (tommy_node* i = tommy_list_head(&state->disklist); i != 0; i = i->next) {
|
|
struct snapraid_disk* disk = i->data;
|
|
for (tommy_node* j = tommy_list_head(&disk->filelist); j != 0; j = j->next) {
|
|
struct snapraid_file* file = j->data;
|
|
collect_parity_block_file(block_size, disk, file, &files, low_to_free_tail_block);
|
|
}
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
if (tommy_list_count(&files) == 0) {
|
|
printf("No files located in the specified parity tail.\n");
|
|
} else {
|
|
tommy_list_sort(&files, parity_entry_compare);
|
|
|
|
data_off_t total_size_located = 0;
|
|
|
|
tommy_list_foreach_arg(&files, add_size_to_sum, &total_size_located);
|
|
|
|
printf("Located data in this range: %sB\n", fmt_size(total_size_located, buf, sizeof(buf)));
|
|
|
|
printf("\n");
|
|
|
|
/* |<##################################################################72>|####80>| */
|
|
printf(" Offset Span Frags\n");
|
|
|
|
tommy_list_foreach(&files, dump_entry);
|
|
}
|
|
|
|
tommy_list_foreach(&files, free);
|
|
}
|
|
|
|
void state_locate_mark_tail_blocks_for_resync(struct snapraid_state* state, uint64_t parity_tail)
|
|
{
|
|
char buf[64];
|
|
uint32_t block_size = state->block_size;
|
|
block_off_t low_to_free_tail_block;
|
|
|
|
if (parity_tail == 0) {
|
|
printf("Forcing reallocation of all files\n\n");
|
|
low_to_free_tail_block = 0;
|
|
} else {
|
|
msg_progress("Forcing reallocation of all files within the tail of %sB of the parity\n\n", fmt_size(parity_tail, buf, sizeof(buf)));
|
|
|
|
struct snapraid_locate_info info;
|
|
state_locate_info(state, parity_tail, &info);
|
|
|
|
printf("Current parity size is %sB\n", fmt_size(info.parity_size, buf, sizeof(buf)));
|
|
if (info.tail_block > info.block_max) {
|
|
printf("Specified tail greater than the parity size! Operate on the full parity\n");
|
|
}
|
|
|
|
low_to_free_tail_block = info.low_to_free_tail_block;
|
|
}
|
|
|
|
msg_progress("Collecting files with offset greater or equal to %" PRIu64 "\n", low_to_free_tail_block * (uint64_t)block_size);
|
|
|
|
tommy_list files;
|
|
tommy_list_init(&files);
|
|
for (tommy_node* i = tommy_list_head(&state->disklist); i != 0; i = i->next) {
|
|
struct snapraid_disk* disk = i->data;
|
|
for (tommy_node* j = tommy_list_head(&disk->filelist); j != 0; j = j->next) {
|
|
struct snapraid_file* file = j->data;
|
|
collect_parity_block_file(block_size, disk, file, &files, low_to_free_tail_block);
|
|
}
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
if (tommy_list_count(&files) == 0) {
|
|
printf("No files located in the specified parity tail.\n");
|
|
} else {
|
|
/* process all the files partiall or fully overlapping the free zone */
|
|
for (tommy_node* j = tommy_list_head(&files); j != 0; j = j->next) {
|
|
struct snapraid_parity_entry* entry = j->data;
|
|
struct snapraid_file* file = entry->file;
|
|
struct snapraid_disk* disk = entry->disk;
|
|
|
|
/*
|
|
* Reallocate the full file, not only the part of in the free zone
|
|
* this is required because the files blocks have to be in order in
|
|
* in the parity file.
|
|
* not reallocating the file head will prevent the reallocation of the file tail
|
|
*/
|
|
for (block_off_t f = 0; f < file->blockmax; ++f) {
|
|
block_off_t parity_pos = fs_file2par_find(disk, file, f);
|
|
|
|
if (parity_pos == POS_NULL) {
|
|
/* block not yet allocated */
|
|
continue;
|
|
}
|
|
|
|
struct snapraid_block* block = fs_file2block_get(file, f);
|
|
unsigned block_state = block_state_get(block);
|
|
|
|
if (block_state == BLOCK_STATE_BLK
|
|
|| block_state == BLOCK_STATE_REBUILD
|
|
) {
|
|
/*
|
|
* Explicit force-realloc overrides an in-place REBUILD request.
|
|
* REP makes the block eligible for the normal reallocation path.
|
|
*
|
|
* This transition changes persistent block-state semantics. Mark the
|
|
* state dirty explicitly rather than relying on the following scan to
|
|
* incidentally do so.
|
|
*/
|
|
block_state_set(block, BLOCK_STATE_REP);
|
|
state->need_write = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
tommy_list_foreach(&files, free);
|
|
}
|
|
|