diff --git a/cmdline/device.c b/cmdline/device.c index 7f58fbbd..d02fd2fd 100644 --- a/cmdline/device.c +++ b/cmdline/device.c @@ -608,7 +608,7 @@ static void printl(const char* str, size_t pad) len = strlen(str); while (len < pad) { - printf(" "); + putchar(' '); ++len; } } diff --git a/cmdline/dry.c b/cmdline/dry.c index f83212ee..9986c47b 100644 --- a/cmdline/dry.c +++ b/cmdline/dry.c @@ -225,8 +225,8 @@ static int state_dry_process(struct snapraid_state* state, struct snapraid_parit struct snapraid_disk* disk; unsigned diskcur; - /* until now is CPU */ - state_usage_cpu(state); + /* until now is misc */ + state_usage_misc(state); /* get the next task */ task = io_data_read(&io, &diskcur, waiting_map, &waiting_mac); @@ -284,8 +284,8 @@ static int state_dry_process(struct snapraid_state* state, struct snapraid_parit countsize += read_size; } - /* until now is CPU */ - state_usage_cpu(state); + /* until now is misc */ + state_usage_misc(state); /* read the parity */ for (l = 0; l < state->level; ++l) { diff --git a/cmdline/elem.h b/cmdline/elem.h index cb5c77b2..87a649e6 100644 --- a/cmdline/elem.h +++ b/cmdline/elem.h @@ -31,6 +31,11 @@ /****************************************************************************/ /* snapraid */ +/** + * Number of measures of the operation progress. + */ +#define PROGRESS_MAX 100 + /** * Max UUID length. */ @@ -328,10 +333,12 @@ struct snapraid_disk { char uuid[UUID_MAX]; /**< UUID of the disk. */ uint64_t device; /**< Device identifier. */ - uint64_t tick; /**< Usage time of the disk. */ block_off_t total_blocks; /**< Number of total blocks. */ block_off_t free_blocks; /**< Number of free blocks at the last sync. */ + uint64_t tick; /**< Usage time. */ + uint64_t progress_tick[PROGRESS_MAX]; /**< Last ticks of progress. */ + /** * First free searching block. * Note that it doesn't necessarily point at the first free block, @@ -423,10 +430,8 @@ struct snapraid_parity { int skip_access; /**< If the disk is inaccessible and it should be skipped. */ int is_excluded; /**< If the parity is excluded by disk filter. */ - /** - * Cumulative time used for parity disks. - */ - uint64_t tick; + uint64_t tick; /**< Usage time. */ + uint64_t progress_tick[PROGRESS_MAX]; /**< Last cpu ticks of progress. */ }; /** diff --git a/cmdline/scrub.c b/cmdline/scrub.c index bd582199..e01eebb4 100644 --- a/cmdline/scrub.c +++ b/cmdline/scrub.c @@ -364,8 +364,8 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par /* if not, silent errors are assumed as expected error */ file_is_unsynced = 0; - /* until now is CPU */ - state_usage_cpu(state); + /* until now is misc */ + state_usage_misc(state); /* get the next task */ task = io_data_read(&io, &diskcur, waiting_map, &waiting_mac); @@ -456,6 +456,9 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par memhash(state->hash, state->hashseed, hash, buffer[diskcur], read_size); } + /* until now is hash */ + state_usage_hash(state); + if (block_has_updated_hash(block)) { /* compare the hash */ if (memcmp(hash, block->hash, HASH_SIZE) != 0) { @@ -483,8 +486,8 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par for (; l < LEV_MAX; ++l) buffer_recov[l] = 0; - /* until now is CPU */ - state_usage_cpu(state); + /* until now is misc */ + state_usage_misc(state); /* read the parity */ for (l = 0; l < state->level; ++l) { @@ -563,6 +566,9 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par } } } + + /* until now is raid */ + state_usage_raid(state); } if (silent_error_on_this_block || io_error_on_this_block) { @@ -606,8 +612,8 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par ) { autosavedone = 0; /* restart the counter */ - /* until now is CPU */ - state_usage_cpu(state); + /* until now is misc */ + state_usage_misc(state); state_progress_stop(state); diff --git a/cmdline/state.c b/cmdline/state.c index 1de43e49..dbf7e94f 100644 --- a/cmdline/state.c +++ b/cmdline/state.c @@ -159,7 +159,9 @@ void state_init(struct snapraid_state* state) state->parity[l].is_excluded = 0; } state->tick_io = 0; - state->tick_cpu = 0; + state->tick_misc = 0; + state->tick_raid = 0; + state->tick_hash = 0; state->tick_last = tick(); state->share[0] = 0; state->pool[0] = 0; @@ -3908,6 +3910,8 @@ int state_progress(struct snapraid_state* state, block_off_t blockpos, block_off { time_t now; int pred; + tommy_node* i; + unsigned l; now = time(0); @@ -3920,16 +3924,16 @@ int state_progress(struct snapraid_state* state, block_off_t blockpos, block_off if (state->progress_tick == 0 || state->progress_time[pred] != now ) { - uint64_t tick_total; uint64_t tick_cpu; + uint64_t tick_total; time_t elapsed; unsigned out_perc = 0; unsigned out_speed = 0; unsigned out_cpu = 0; unsigned out_eta = 0; - tick_total = state->tick_cpu + state->tick_io; - tick_cpu = state->tick_cpu; + tick_cpu = state->tick_misc + state->tick_raid + state->tick_hash; + tick_total = tick_cpu + state->tick_io; elapsed = now - state->progress_whole_start - state->progress_wasted; /* completion percentage */ @@ -3946,8 +3950,10 @@ int state_progress(struct snapraid_state* state, block_off_t blockpos, block_off time_t delta_time; block_off_t delta_pos; data_off_t delta_size; - uint64_t delta_tick_total; uint64_t delta_tick_cpu; + uint64_t delta_tick_total; + uint64_t oldest_tick_cpu; + uint64_t oldest_tick_total; /* number of past measures */ past = state->progress_tick; @@ -3968,11 +3974,16 @@ int state_progress(struct snapraid_state* state, block_off_t blockpos, block_off oldest -= PROGRESS_MAX; } + oldest_tick_cpu = state->progress_tick_misc[oldest] + + state->progress_tick_raid[oldest] + + state->progress_tick_hash[oldest]; + oldest_tick_total = oldest_tick_cpu + state->progress_tick_io[oldest]; + delta_time = now - state->progress_time[oldest]; delta_pos = countpos - state->progress_pos[oldest]; delta_size = countsize - state->progress_size[oldest]; - delta_tick_total = tick_total - state->progress_tick_total[oldest]; - delta_tick_cpu = tick_cpu - state->progress_tick_cpu[oldest]; + delta_tick_cpu = tick_cpu - oldest_tick_cpu; + delta_tick_total = tick_total - oldest_tick_total; /* estimate the speed in MB/s */ if (delta_time != 0) @@ -3991,6 +4002,11 @@ int state_progress(struct snapraid_state* state, block_off_t blockpos, block_off log_tag("run:pos:%u:%u:%" PRIu64 ":%u:%u:%u:%u:%" PRIu64 "\n", blockpos, countpos, countsize, out_perc, out_eta, out_speed, out_cpu, (uint64_t)elapsed); log_flush(); } else { + if (msg_level >= MSG_VERBOSE) { + printf("\n\n"); + state_usage_graph(state); + } + msg_bar("%u%%, %u MB", out_perc, (unsigned)(countsize / MEGA)); if (out_speed) msg_bar(", %u MB/s", out_speed); @@ -4005,8 +4021,16 @@ int state_progress(struct snapraid_state* state, block_off_t blockpos, block_off state->progress_time[state->progress_ptr] = now; state->progress_pos[state->progress_ptr] = countpos; state->progress_size[state->progress_ptr] = countsize; - state->progress_tick_cpu[state->progress_ptr] = tick_cpu; - state->progress_tick_total[state->progress_ptr] = tick_total; + state->progress_tick_misc[state->progress_ptr] = state->tick_misc; + state->progress_tick_raid[state->progress_ptr] = state->tick_raid; + state->progress_tick_hash[state->progress_ptr] = state->tick_hash; + state->progress_tick_io[state->progress_ptr] = state->tick_io; + for (i = state->disklist; i != 0; i = i->next) { + struct snapraid_disk* disk = i->data; + disk->progress_tick[state->progress_ptr] = disk->tick; + } + for (l = 0; l < state->level; ++l) + state->parity[l].progress_tick[state->progress_ptr] = state->parity[l].tick; /* next position */ ++state->progress_ptr; @@ -4040,13 +4064,35 @@ void state_usage_waste(struct snapraid_state* state) state->tick_last = now; } -void state_usage_cpu(struct snapraid_state* state) +void state_usage_misc(struct snapraid_state* state) { uint64_t now = tick(); uint64_t delta = now - state->tick_last; /* increment the time spent in computations */ - state->tick_cpu += delta; + state->tick_misc += delta; + + state->tick_last = now; +} + +void state_usage_raid(struct snapraid_state* state) +{ + uint64_t now = tick(); + uint64_t delta = now - state->tick_last; + + /* increment the time spent in computations */ + state->tick_raid += delta; + + state->tick_last = now; +} + +void state_usage_hash(struct snapraid_state* state) +{ + uint64_t now = tick(); + uint64_t delta = now - state->tick_last; + + /* increment the time spent in computations */ + state->tick_hash += delta; state->tick_last = now; } @@ -4085,6 +4131,100 @@ void state_usage_parity(struct snapraid_state* state, unsigned* waiting_map, uns state->tick_last = now; } +/** + * Print a repeated char. + */ +static void printc(char c, size_t pad) +{ + while (pad) { + putchar(c); + --pad; + } +} + +/** + * Print a string with space padding. + */ +static void printr(const char* str, size_t pad) +{ + size_t len; + + len = strlen(str); + + while (len < pad) { + putchar(' '); + ++len; + } + + printf("%s", str); +} + +#define USAGE_COLUMN 60 + +void state_usage_graph(struct snapraid_state* state) +{ + uint64_t tick_total; + tommy_node* i; + unsigned l; + size_t pad; + + tick_total = state->tick_misc + state->tick_raid + state->tick_hash + state->tick_io; + if (!tick_total) + return; + + pad = 4; + for (i = state->disklist; i != 0; i = i->next) { + struct snapraid_disk* disk = i->data; + size_t len = strlen(disk->name); + if (pad < len) + pad = len; + } + for (l = 0; l < state->level; ++l) { + size_t len = strlen(lev_config_name(l)); + if (pad < len) + pad = len; + } + + /* extra space */ + pad += 1; + + /* here we don't use msg_progress() because it doesn't allow partial output */ + for (i = state->disklist; i != 0; i = i->next) { + struct snapraid_disk* disk = i->data; + printr(disk->name, pad); + printf("%3" PRIu64 "%% | ", disk->tick * 100 / tick_total); + printc('*', disk->tick * USAGE_COLUMN / tick_total); + printf("\n"); + } + + for (l = 0; l < state->level; ++l) { + printr(lev_config_name(l), pad); + printf("%3" PRIu64 "%% | ", state->parity[l].tick * 100 / tick_total); + printc('*', state->parity[l].tick * USAGE_COLUMN / tick_total); + printf("\n"); + } + + printr("raid", pad); + printf("%3" PRIu64 "%% | ", state->tick_raid * 100 / tick_total); + printc('*', state->tick_raid * USAGE_COLUMN / tick_total); + printf("\n"); + + printr("hash", pad); + printf("%3" PRIu64 "%% | ", state->tick_hash * 100 / tick_total); + printc('*', state->tick_hash * USAGE_COLUMN / tick_total); + printf("\n"); + + printr("misc", pad); + printf("%3" PRIu64 "%% | ", state->tick_misc * 100 / tick_total); + printc('*', state->tick_misc * USAGE_COLUMN / tick_total); + printf("\n"); + + printr("", pad); + printf(" +-"); + printc('-', USAGE_COLUMN); + printf("\n"); +} + void state_usage_print(struct snapraid_state* state) { uint64_t tick_total; diff --git a/cmdline/state.h b/cmdline/state.h index d415f68c..f311dd99 100644 --- a/cmdline/state.h +++ b/cmdline/state.h @@ -119,11 +119,6 @@ struct snapraid_option { int auto_conf; /**< Allow to run without configuration file. */ }; -/** - * Number of measures of the operation progress. - */ -#define PROGRESS_MAX 100 - struct snapraid_state { struct snapraid_option opt; /**< Setup options. */ int filter_hidden; /**< Filter out hidden files. */ @@ -158,7 +153,9 @@ struct snapraid_state { /** * Cumulative time used for computations. */ - uint64_t tick_cpu; + uint64_t tick_misc; + uint64_t tick_raid; + uint64_t tick_hash; /** * Cumulative time used for all io operations of disks. @@ -179,8 +176,10 @@ struct snapraid_state { time_t progress_time[PROGRESS_MAX]; /**< Last times of progress. */ block_off_t progress_pos[PROGRESS_MAX]; /**< Last positions of progress. */ data_off_t progress_size[PROGRESS_MAX]; /**< Last sizes of progress. */ - uint64_t progress_tick_cpu[PROGRESS_MAX]; /**< Last cpu ticks of progress. */ - uint64_t progress_tick_total[PROGRESS_MAX]; /**< Last total ticks of progress. */ + uint64_t progress_tick_misc[PROGRESS_MAX]; /**< Last cpu ticks of progress. */ + uint64_t progress_tick_raid[PROGRESS_MAX]; /**< Last raid ticks of progress. */ + uint64_t progress_tick_hash[PROGRESS_MAX]; /**< Last hash ticks of progress. */ + uint64_t progress_tick_io[PROGRESS_MAX]; /**< Last io ticks of progress. */ int progress_ptr; /**< Pointer to the next position to fill. Rolling over. */ int progress_tick; /**< Number of measures done. */ @@ -340,7 +339,9 @@ void state_usage_waste(struct snapraid_state* state); /** * Set the usage time for CPU. */ -void state_usage_cpu(struct snapraid_state* state); +void state_usage_misc(struct snapraid_state* state); +void state_usage_raid(struct snapraid_state* state); +void state_usage_hash(struct snapraid_state* state); /** * Set the usage time for a set of data disks. @@ -357,6 +358,11 @@ void state_usage_parity(struct snapraid_state* state, unsigned* waiting_map, uns */ void state_usage_print(struct snapraid_state* state); +/** + * Print a graph of usage time. + */ +void state_usage_graph(struct snapraid_state* state); + /** * Check the file-system on all disks. * On error it aborts. diff --git a/cmdline/sync.c b/cmdline/sync.c index c33a5a3e..6af65d1a 100644 --- a/cmdline/sync.c +++ b/cmdline/sync.c @@ -124,8 +124,8 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta /* if we have to use the old hash */ rehash = info_get_rehash(info); - /* until now is CPU */ - state_usage_cpu(state); + /* until now is misc */ + state_usage_misc(state); /* if the file is different than the current one, close it */ if (handle[j].file != 0 && handle[j].file != file) { @@ -255,6 +255,9 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta memhash(state->hash, state->hashseed, hash, buffer, read_size); } + /* until now is hash */ + state_usage_hash(state); + if (block_state == BLOCK_STATE_REP) { /* compare the hash */ if (memcmp(hash, block->hash, HASH_SIZE) != 0) { @@ -817,8 +820,8 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari block_off_t file_pos; unsigned diskcur; - /* until now is CPU */ - state_usage_cpu(state); + /* until now is misc */ + state_usage_misc(state); task = io_data_read(&io, &diskcur, waiting_map, &waiting_mac); @@ -917,6 +920,9 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari memhash(state->hash, state->hashseed, hash, buffer[diskcur], read_size); } + /* until now is hash */ + state_usage_hash(state); + if (block_has_updated_hash(block)) { /* compare the hash */ if (memcmp(hash, block->hash, HASH_SIZE) != 0) { @@ -1035,8 +1041,8 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari /* if we have something to recover and enough parity */ if (something_to_recover && j == failed_count) { - /* until now is CPU */ - state_usage_cpu(state); + /* until now is misc */ + state_usage_misc(state); /* read the parity */ /* we are sure that parity exists because */ @@ -1082,6 +1088,9 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari /* only 'fix' supports the most advanced fixing */ raid_rec(failed_mac, failed_map, diskmax, state->level, state->block_size, buffer); + /* until now is raid */ + state_usage_raid(state); + /* check the result and prepare the data */ for (j = 0; j < failed_count; ++j) { unsigned char hash[HASH_SIZE]; @@ -1099,6 +1108,9 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari memhash(state->hash, state->hashseed, hash, block_buffer, size); } + /* until now is hash */ + state_usage_hash(state); + /* if the hash doesn't match */ if (memcmp(hash, failed[j].block->hash, HASH_SIZE) != 0) { /* we have not recovered */ @@ -1132,6 +1144,9 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari /* compute the parity */ raid_gen(diskmax, state->level, state->block_size, buffer); + /* until now is raid */ + state_usage_raid(state); + /* mark that the parity is going to be written */ parity_going_to_be_updated = 1; } @@ -1196,7 +1211,7 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari /* Note that the calls to io_parity_write() are mandatory */ /* even if the parity doesn't need to be updated */ /* This because we want to keep track of the time usage */ - state_usage_cpu(state); + state_usage_misc(state); /* write the parity */ for (l = 0; l < state->level; ++l) { @@ -1258,8 +1273,8 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari ) { autosavedone = 0; /* restart the counter */ - /* until now is CPU */ - state_usage_cpu(state); + /* until now is misc */ + state_usage_misc(state); state_progress_stop(state);