diff --git a/cmdline/scan.c b/cmdline/scan.c index 54e9cde9..fd55ae0c 100644 --- a/cmdline/scan.c +++ b/cmdline/scan.c @@ -2009,19 +2009,18 @@ static int state_diffscan(struct snapraid_state* state, int is_diff) no_difference = !total.count_move && !total.count_copy && !total.count_restore && !total.count_change && !total.count_remove && !total.count_insert; - int parity_invalid = state->parity_is_invalid; if (is_diff) { if (!no_difference) { msg_status("There are differences!\n"); } else { msg_status("No differences\n"); } - if (parity_invalid) + if (state->unsynced_blocks != 0) log_error(EUSER, "The last sync was interrupted. Run it again!\n"); } - if (parity_invalid) { - log_tag("summary:exit:incomplete_sync\n"); + if (state->unsynced_blocks != 0) { + log_tag("summary:exit:unsynced\n"); } else if (!no_difference) { log_tag("summary:exit:diff\n"); } else { @@ -2040,7 +2039,7 @@ static int state_diffscan(struct snapraid_state* state, int is_diff) return 1; /* check also for incomplete "sync" */ - if (parity_invalid) + if (state->unsynced_blocks != 0) return 1; } diff --git a/cmdline/state.c b/cmdline/state.c index 7391e292..7952631a 100644 --- a/cmdline/state.c +++ b/cmdline/state.c @@ -181,7 +181,7 @@ void state_init(struct snapraid_state* state) state->no_conf = 0; for (i = 0; i < SMART_IGNORE_MAX; ++i) state->smartignore[i] = 0; - state->parity_is_invalid = 0; + state->unsynced_blocks = 0; state->thermal_stop_gathering = 0; state->thermal_ambient_temperature = 0; state->thermal_highest_temperature = 0; @@ -3129,8 +3129,8 @@ static void state_read_content(struct snapraid_state* state, const char* path, S log_tag("content_info:block_unsynced:%u\n", count_unsynced); log_tag("content_info:block_unscrubbed:%u\n", count_unscrubbed); - /* set the parity validity */ - state->parity_is_invalid = count_unsynced != 0; + /* store the unsynced_blocks */ + state->unsynced_blocks = count_unsynced; } struct state_write_thread_context { @@ -4020,8 +4020,8 @@ static void state_write_content(struct snapraid_state* state, uint32_t* out_crc) log_tag("content_info:block_unsynced:%u\n", count_unsynced); log_tag("content_info:block_unscrubbed:%u\n", count_unscrubbed); - /* set the parity validity */ - state->parity_is_invalid = count_unsynced != 0; + /* store the unsynced_blocks */ + state->unsynced_blocks = count_unsynced; *out_crc = crc; } diff --git a/cmdline/state.h b/cmdline/state.h index a2448af3..c0cc8c4d 100644 --- a/cmdline/state.h +++ b/cmdline/state.h @@ -142,7 +142,7 @@ struct snapraid_state { unsigned besthash; /**< Best hash suggested. */ const char* command; /**< Command running. */ int smartignore[SMART_IGNORE_MAX]; /**< Smart attributes to ignore for all devices. */ - int parity_is_invalid; /**< If the parity is invalid, and a sync is needed. */ + uint32_t unsynced_blocks; /**< If the parity is invalid, and a sync is needed. */ tommy_list contentlist; /**< List of content files. */ tommy_list disklist; /**< List of all the disks. */ diff --git a/cmdline/status.c b/cmdline/status.c index bf475771..7938b93f 100644 --- a/cmdline/status.c +++ b/cmdline/status.c @@ -72,7 +72,6 @@ int state_status(struct snapraid_state* state) uint64_t file_block_count; uint64_t file_block_free; block_off_t parity_block_free; - unsigned unsynced_blocks; unsigned unscrubbed_blocks; uint64_t all_wasted; int free_not_zero; @@ -297,7 +296,6 @@ int state_status(struct snapraid_state* state) bad_last = 0; count = 0; rehash = 0; - unsynced_blocks = 0; unscrubbed_blocks = 0; log_tag("block_count:%u\n", blockmax); for (i = 0; i < blockmax; ++i) { @@ -309,19 +307,17 @@ int state_status(struct snapraid_state* state) /* for each disk */ one_invalid = 0; one_valid = 0; - for (node_disk = state->disklist; node_disk != 0; node_disk = node_disk->next) { - struct snapraid_disk* disk = node_disk->data; - struct snapraid_block* block = fs_par2block_find(disk, i); - if (block_has_file(block)) - one_valid = 1; - if (block_has_invalid_parity(block)) - one_invalid = 1; - } + if (state->opt.gui_verbose) { /* avoid this time consuming operation if not needed */ + for (node_disk = state->disklist; node_disk != 0; node_disk = node_disk->next) { + struct snapraid_disk* disk = node_disk->data; + struct snapraid_block* block = fs_par2block_find(disk, i); - /* if both valid and invalid, we need to update */ - if (one_invalid && one_valid) { - ++unsynced_blocks; + if (block_has_file(block)) + one_valid = 1; + if (block_has_invalid_parity(block)) + one_invalid = 1; + } } /* skip unused blocks */ @@ -367,7 +363,7 @@ int state_status(struct snapraid_state* state) } /* sort the info to get the time info */ - qsort(timemap, count, sizeof(time_t), time_compare); + sort_time(timemap, count); /* output the info map */ i = 0; @@ -477,9 +473,9 @@ int state_status(struct snapraid_state* state) printf("WARNING! You have scrub dates in the future! The next sync/scrub will truncate them!\n"); } - if (unsynced_blocks) { + if (state->unsynced_blocks) { printf("WARNING! The array is NOT fully synced.\n"); - printf("You have a sync in progress at %u%%.\n", muldiv(blockmax - unsynced_blocks, 100, blockmax)); + printf("You have a sync in progress at %u%%.\n", muldiv(blockmax - state->unsynced_blocks, 100, blockmax)); } else { printf("No sync is in progress.\n"); } @@ -573,7 +569,7 @@ int state_status(struct snapraid_state* state) if (bad) log_tag("summary:exit:bad\n"); - else if (unsynced_blocks) + else if (state->unsynced_blocks != 0) log_tag("summary:exit:unsynced\n"); else log_tag("summary:exit:ok\n"); diff --git a/cmdline/util.c b/cmdline/util.c index 8f5e4779..56801f5f 100644 --- a/cmdline/util.c +++ b/cmdline/util.c @@ -854,3 +854,99 @@ unsigned muldiv_upper(uint64_t v, uint64_t mul, uint64_t div) return (uint32_t)((v * mul + div - 1) / div); } +/****************************************************************************/ +/* sort */ + +/** + * Three-way partition for quicksort + * Partitions array into three parts: < pivot, == pivot, > pivot + * Returns the boundaries of the equal section via lt and gt pointers + */ +static void partition3way(time_t *arr, size_t left, size_t right, size_t *lt, size_t *gt) +{ + /* choose median-of-three as pivot to avoid worst case on sorted data */ + size_t mid = left + (right - left) / 2; + time_t pivot; + + /* sort left, mid, right and use median as pivot */ + if (arr[left] > arr[mid]) { + uint64_t tmp = arr[left]; arr[left] = arr[mid]; arr[mid] = tmp; + } + if (arr[mid] > arr[right]) { + uint64_t tmp = arr[mid]; arr[mid] = arr[right]; arr[right] = tmp; + if (arr[left] > arr[mid]) { + tmp = arr[left]; arr[left] = arr[mid]; arr[mid] = tmp; + } + } + pivot = arr[mid]; + + // Dutch National Flag partition + size_t i = left; + size_t lt_idx = left; + size_t gt_idx = right; + + while (i <= gt_idx) { + if (arr[i] < pivot) { + uint64_t tmp = arr[lt_idx]; + arr[lt_idx] = arr[i]; + arr[i] = tmp; + lt_idx++; + i++; + } else if (arr[i] > pivot) { + uint64_t tmp = arr[i]; + arr[i] = arr[gt_idx]; + arr[gt_idx] = tmp; + gt_idx--; + } else { + i++; + } + } + + *lt = lt_idx; + *gt = gt_idx; +} + +/** + * Recursive three-way quicksort + */ +static void quicksort3way_recursive(time_t *arr, size_t left, size_t right) +{ + if (left >= right) + return; + + /* use insertion sort for small subarrays (optimization) */ + if (right - left < 16) { + for (size_t i = left + 1; i <= right; i++) { + time_t key = arr[i]; + size_t j = i; + while (j > left && arr[j - 1] > key) { + arr[j] = arr[j - 1]; + j--; + } + arr[j] = key; + } + return; + } + + size_t lt, gt; + partition3way(arr, left, right, <, >); + + /* + * Recursively sort elements less than and greater than pivot + * Elements equal to pivot are already in correct position + */ + if (lt > left) { + quicksort3way_recursive(arr, left, lt - 1); + } + if (gt < right) { + quicksort3way_recursive(arr, gt + 1, right); + } +} + +void sort_time(time_t *arr, size_t n) +{ + if (n <= 1) + return; + quicksort3way_recursive(arr, 0, n - 1); +} + diff --git a/cmdline/util.h b/cmdline/util.h index 8705f904..97cc0307 100644 --- a/cmdline/util.h +++ b/cmdline/util.h @@ -273,5 +273,17 @@ static inline int bit_vect_test(bit_vect_t* bit_vect, size_t off) unsigned muldiv(uint64_t v, uint64_t mul, uint64_t div); unsigned muldiv_upper(uint64_t v, uint64_t mul, uint64_t div); +/****************************************************************************/ +/* sort */ + +/** + * Sort an array of uint64_t integers in-place + * Optimized for arrays with many duplicate values + * + * @param arr Pointer to array of uint64_t + * @param n Number of elements in the array + */ +void sort_time(time_t* arr, size_t n); + #endif diff --git a/doc/snapraid_log.1 b/doc/snapraid_log.1 index aee43c53..336aaacc 100644 --- a/doc/snapraid_log.1 +++ b/doc/snapraid_log.1 @@ -484,6 +484,10 @@ The array is already synchronized. .I diff Indicates that differences were found. The array needs a \`sync\` operation. +.HP 4 +.I unsynced +The array has an interrupted sync. +The array needs another \`sync\` operation. .PD .RE .SH GENERAL PROGRESS AND EXECUTION TAGS diff --git a/doc/snapraid_log.d b/doc/snapraid_log.d index e53e680f..2045c1de 100644 --- a/doc/snapraid_log.d +++ b/doc/snapraid_log.d @@ -330,6 +330,8 @@ Scan Tags The array is already synchronized. diff - Indicates that differences were found. The array needs a `sync` operation. + unsynced - The array has an interrupted sync. + The array needs another `sync` operation. General Progress and Execution Tags These tags are used to report the runtime processing of the commands diff --git a/doc/snapraid_log.txt b/doc/snapraid_log.txt index e6566afd..da7a5b67 100644 --- a/doc/snapraid_log.txt +++ b/doc/snapraid_log.txt @@ -346,6 +346,8 @@ These tags provide a final count of all detected changes. The array is already synchronized. diff - Indicates that differences were found. The array needs a `sync` operation. + unsynced - The array has an interrupted sync. + The array needs another `sync` operation. 6 GENERAL PROGRESS AND EXECUTION TAGS