mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-25 18:40:19 -04:00
Avoid redundant unsynced block scans when writing content
This commit is contained in:
parent
1073c97e0a
commit
31f44958a3
1 changed files with 62 additions and 48 deletions
110
cmdline/state.c
110
cmdline/state.c
|
|
@ -2058,25 +2058,38 @@ static void state_content_check(struct snapraid_state* state, const char* path)
|
|||
}
|
||||
|
||||
/**
|
||||
* Check if the position is REQUIRED, or we can completely clear it from the state.
|
||||
* Analyze if the position is REQUIRED, if parity is unsynced, and if it has
|
||||
* DELETED blocks that can be discharged when the position is unused.
|
||||
*
|
||||
* Note that position with only DELETED blocks are discharged.
|
||||
* A DELETED block makes parity invalid, but it counts as unsynced only when at
|
||||
* least one disk still has a file at the same position. This is the same logic
|
||||
* used by "status" to detect an incomplete sync.
|
||||
*/
|
||||
static int fs_position_is_required(struct snapraid_state* state, block_off_t pos)
|
||||
static int fs_position_analyze(struct snapraid_state* state, block_off_t pos, int* is_unsynced, int* has_deleted)
|
||||
{
|
||||
tommy_node* i;
|
||||
int one_file = 0;
|
||||
int one_invalid = 0;
|
||||
int one_deleted = 0;
|
||||
|
||||
/* check for each disk */
|
||||
for (i = state->disklist; i != 0; i = i->next) {
|
||||
struct snapraid_disk* disk = i->data;
|
||||
struct snapraid_block* block = fs_par2block_find(disk, pos);
|
||||
unsigned block_state = block_state_get(block);
|
||||
|
||||
/* if we have at least one file, the position is needed */
|
||||
if (block_has_file(block))
|
||||
return 1;
|
||||
one_file = 1;
|
||||
if (block_has_invalid_parity(block))
|
||||
one_invalid = 1;
|
||||
if (block_state == BLOCK_STATE_DELETED)
|
||||
one_deleted = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
*is_unsynced = one_file && one_invalid;
|
||||
*has_deleted = one_deleted;
|
||||
|
||||
return one_file;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -2307,6 +2320,24 @@ static int state_read_block_run(STREAM* f, struct snapraid_file* file, block_off
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable lock-free access to the extent trees of all disks.
|
||||
* Callers must use this helper in matched, local pairs around operations that
|
||||
* are known to be the only extent-tree users.
|
||||
*/
|
||||
static void fs_single_thread(struct snapraid_state* state, int single_thread)
|
||||
{
|
||||
#if HAVE_THREAD
|
||||
for (tommy_node* i = state->disklist; i != 0; i = i->next) {
|
||||
struct snapraid_disk* disk = i->data;
|
||||
disk->single_thread = single_thread;
|
||||
}
|
||||
#else
|
||||
(void)state;
|
||||
(void)single_thread;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void state_read_content(struct snapraid_state* state, const char* path, STREAM* f)
|
||||
{
|
||||
block_off_t blockmax;
|
||||
|
|
@ -2351,12 +2382,6 @@ static void state_read_content(struct snapraid_state* state, const char* path, S
|
|||
tommy_array_init(&disk_mapping);
|
||||
tommy_hashdyn_init(&bucket_hash);
|
||||
|
||||
/* mark all disks as single threads */
|
||||
for (tommy_node* i = state->disklist; i != 0; i = i->next) {
|
||||
struct snapraid_disk* disk = i->data;
|
||||
disk->single_thread = 1;
|
||||
}
|
||||
|
||||
ret = sread(f, buffer, 12);
|
||||
if (ret < 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
|
|
@ -3717,12 +3742,6 @@ static void state_read_content(struct snapraid_state* state, const char* path, S
|
|||
}
|
||||
}
|
||||
|
||||
/* mark all disks as multi threads */
|
||||
for (tommy_node* i = state->disklist; i != 0; i = i->next) {
|
||||
struct snapraid_disk* disk = i->data;
|
||||
disk->single_thread = 0;
|
||||
}
|
||||
|
||||
tommy_array_done(&disk_mapping);
|
||||
|
||||
if (serror(f)) {
|
||||
|
|
@ -3828,6 +3847,7 @@ struct state_write_thread_context {
|
|||
time_t info_oldest;
|
||||
time_t info_now;
|
||||
int info_has_rehash;
|
||||
uint64_t count_unsynced;
|
||||
STREAM* f;
|
||||
int first;
|
||||
#if HAVE_MT_WRITE
|
||||
|
|
@ -3842,7 +3862,6 @@ struct state_write_thread_context {
|
|||
uint64_t count_dir;
|
||||
uint64_t count_bad;
|
||||
uint64_t count_rehash;
|
||||
uint64_t count_unsynced;
|
||||
uint64_t count_unscrubbed;
|
||||
};
|
||||
|
||||
|
|
@ -3919,7 +3938,6 @@ static void* state_write_thread(void* arg)
|
|||
uint64_t count_dir;
|
||||
uint64_t count_bad;
|
||||
uint64_t count_rehash;
|
||||
uint64_t count_unsynced;
|
||||
uint64_t count_unscrubbed;
|
||||
tommy_node* i;
|
||||
block_off_t begin;
|
||||
|
|
@ -3937,7 +3955,6 @@ static void* state_write_thread(void* arg)
|
|||
count_dir = 0;
|
||||
count_bad = 0;
|
||||
count_rehash = 0;
|
||||
count_unsynced = 0;
|
||||
count_unscrubbed = 0;
|
||||
tommy_hashdyn_init(&bucket_hash);
|
||||
|
||||
|
|
@ -4395,17 +4412,11 @@ static void* state_write_thread(void* arg)
|
|||
|
||||
info = info_get(&state->infoarr, begin);
|
||||
|
||||
/* avoid this slow operation if not needed */
|
||||
if (context->first && fs_is_block_unsynced(state, begin))
|
||||
++count_unsynced;
|
||||
|
||||
/* find the end of run of blocks */
|
||||
end = begin + 1;
|
||||
while (end < blockmax
|
||||
&& info == info_get(&state->infoarr, end)
|
||||
) {
|
||||
if (context->first && fs_is_block_unsynced(state, end))
|
||||
++count_unsynced;
|
||||
++end;
|
||||
}
|
||||
|
||||
|
|
@ -4509,7 +4520,6 @@ static void* state_write_thread(void* arg)
|
|||
context->count_dir = count_dir;
|
||||
context->count_bad = count_bad;
|
||||
context->count_rehash = count_rehash;
|
||||
context->count_unsynced = count_unsynced;
|
||||
context->count_unscrubbed = count_unscrubbed;
|
||||
|
||||
/* store the bucket info list */
|
||||
|
|
@ -4567,6 +4577,9 @@ static void state_write_content(struct snapraid_state* state, uint32_t* out_crc)
|
|||
/* blocks of all array */
|
||||
blockmax = parity_allocated_size(state);
|
||||
|
||||
/* preparation is the only extent-tree user until serialization starts */
|
||||
fs_single_thread(state, 1);
|
||||
|
||||
/* check the file-system on all disks */
|
||||
state_fscheck(state, "before write");
|
||||
|
||||
|
|
@ -4577,11 +4590,18 @@ static void state_write_content(struct snapraid_state* state, uint32_t* out_crc)
|
|||
info_oldest = 0; /* oldest time in info */
|
||||
info_now = time(0); /* get the present time */
|
||||
info_has_rehash = 0; /* if there is a rehash info */
|
||||
count_unsynced = 0;
|
||||
for (idx = 0; idx < blockmax; ++idx) {
|
||||
int is_unsynced;
|
||||
int has_deleted;
|
||||
|
||||
/* if the position is used */
|
||||
if (fs_position_is_required(state, idx)) {
|
||||
if (fs_position_analyze(state, idx, &is_unsynced, &has_deleted)) {
|
||||
snapraid_info info = info_get(&state->infoarr, idx);
|
||||
|
||||
if (is_unsynced)
|
||||
++count_unsynced;
|
||||
|
||||
/* only if there is some info to store */
|
||||
if (info) {
|
||||
time_t info_time = info_get_time(info);
|
||||
|
|
@ -4596,8 +4616,9 @@ static void state_write_content(struct snapraid_state* state, uint32_t* out_crc)
|
|||
/* clear any previous info */
|
||||
info_set(&state->infoarr, idx, 0);
|
||||
|
||||
/* and clear any deleted blocks */
|
||||
fs_position_clear_deleted(state, idx);
|
||||
/* and clear any deleted blocks, avoiding another scan in the common case */
|
||||
if (has_deleted)
|
||||
fs_position_clear_deleted(state, idx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4652,6 +4673,8 @@ static void state_write_content(struct snapraid_state* state, uint32_t* out_crc)
|
|||
}
|
||||
}
|
||||
|
||||
fs_single_thread(state, 0);
|
||||
|
||||
#if HAVE_MT_WRITE
|
||||
/* start all writing threads */
|
||||
first = 1;
|
||||
|
|
@ -4696,6 +4719,7 @@ static void state_write_content(struct snapraid_state* state, uint32_t* out_crc)
|
|||
context->info_oldest = info_oldest;
|
||||
context->info_now = info_now;
|
||||
context->info_has_rehash = info_has_rehash;
|
||||
context->count_unsynced = count_unsynced;
|
||||
context->f = f;
|
||||
context->first = first;
|
||||
context->content = content->content;
|
||||
|
|
@ -4747,7 +4771,6 @@ static void state_write_content(struct snapraid_state* state, uint32_t* out_crc)
|
|||
count_dir = 0;
|
||||
count_bad = 0;
|
||||
count_rehash = 0;
|
||||
count_unsynced = 0;
|
||||
count_unscrubbed = 0;
|
||||
|
||||
/* make all temporary content files durable only after all writers have terminated */
|
||||
|
|
@ -4875,26 +4898,14 @@ static void state_write_content(struct snapraid_state* state, uint32_t* out_crc)
|
|||
context->info_oldest = info_oldest;
|
||||
context->info_now = info_now;
|
||||
context->info_has_rehash = info_has_rehash;
|
||||
context->count_unsynced = count_unsynced;
|
||||
context->f = f;
|
||||
context->first = 1;
|
||||
|
||||
#if HAVE_THREAD
|
||||
/* this non-MT path is the only user of the extent trees until serialization completes. */
|
||||
for (i = state->disklist; i != 0; i = i->next) {
|
||||
struct snapraid_disk* disk = i->data;
|
||||
disk->single_thread = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* this serializer is the only extent-tree user until it completes */
|
||||
fs_single_thread(state, 1);
|
||||
retval = state_write_thread(context);
|
||||
|
||||
#if HAVE_THREAD
|
||||
/* restore locking before any later phase can start worker threads. */
|
||||
for (i = state->disklist; i != 0; i = i->next) {
|
||||
struct snapraid_disk* disk = i->data;
|
||||
disk->single_thread = 0;
|
||||
}
|
||||
#endif
|
||||
fs_single_thread(state, 0);
|
||||
|
||||
/* abort on failure */
|
||||
if (retval) {
|
||||
|
|
@ -5145,7 +5156,10 @@ void state_read(struct snapraid_state* state)
|
|||
|
||||
/* guess the file type from the first char */
|
||||
if (c == 'S') {
|
||||
/* parsing is the only user of the extent trees until the content is loaded */
|
||||
fs_single_thread(state, 1);
|
||||
state_read_content(state, path, f);
|
||||
fs_single_thread(state, 0);
|
||||
} else {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal(EUSER, "From SnapRAID v9.0 the text content file is not supported anymore.\n");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue