X-Git-Url: https://git.kernel.dk/?a=blobdiff_plain;f=eta.c;h=e8c727809e39977dc2886a4e7f2730142db38eb5;hb=refs%2Fheads%2Fmaster;hp=8b77dafaf9700b30996c1650788ec6c554598cdf;hpb=db37d89074ed204c9c2bd010e72f63dcf4725715;p=fio.git diff --git a/eta.c b/eta.c index 8b77dafa..7d07708f 100644 --- a/eta.c +++ b/eta.c @@ -2,14 +2,19 @@ * Status and ETA code */ #include -#include #include +#include +#ifdef CONFIG_VALGRIND_DEV +#include +#else +#define DRD_IGNORE_VAR(x) do { } while (0) +#endif #include "fio.h" #include "lib/pow2.h" static char __run_str[REAL_MAX_JOBS + 1]; -static char run_str[__THREAD_RUNSTR_SZ(REAL_MAX_JOBS)]; +static char run_str[__THREAD_RUNSTR_SZ(REAL_MAX_JOBS) + 1]; static void update_condensed_str(char *rstr, char *run_str_condensed) { @@ -145,7 +150,7 @@ void eta_to_str(char *str, unsigned long eta_sec) str += sprintf(str, "%02uh:", h); str += sprintf(str, "%02um:", m); - str += sprintf(str, "%02us", s); + sprintf(str, "%02us", s); } /* @@ -173,12 +178,27 @@ static unsigned long thread_eta(struct thread_data *td) bytes_total = td->fill_device_size; } - if (td->o.zone_size && td->o.zone_skip && bytes_total) { + /* + * If io_size is set, bytes_total is an exact value that does not need + * adjustment. + */ + if (td->o.zone_size && td->o.zone_skip && bytes_total && + !fio_option_is_set(&td->o, io_size)) { unsigned int nr_zones; uint64_t zone_bytes; - zone_bytes = bytes_total + td->o.zone_size + td->o.zone_skip; - nr_zones = (zone_bytes - 1) / (td->o.zone_size + td->o.zone_skip); + /* + * Calculate the upper bound of the number of zones that will + * be processed, including skipped bytes between zones. If this + * is larger than total_io_size (e.g. when --io_size or --size + * specify a small value), use the lower bound to avoid + * adjustments to a negative value that would result in a very + * large bytes_total and an incorrect eta. + */ + zone_bytes = td->o.zone_size + td->o.zone_skip; + nr_zones = (bytes_total + zone_bytes - 1) / zone_bytes; + if (bytes_total < nr_zones * td->o.zone_skip) + nr_zones = bytes_total / zone_bytes; bytes_total -= nr_zones * td->o.zone_skip; } @@ -195,8 +215,9 @@ static unsigned long thread_eta(struct thread_data *td) perc = td->o.rwmix[DDIR_WRITE]; bytes_total += (bytes_total * perc) / 100; - } else + } else { bytes_total <<= 1; + } } if (td->runstate == TD_RUNNING || td->runstate == TD_VERIFYING) { @@ -208,8 +229,9 @@ static unsigned long thread_eta(struct thread_data *td) perc = (double) bytes_done / (double) bytes_total; if (perc > 1.0) perc = 1.0; - } else + } else { perc = 0.0; + } if (td->o.time_based) { if (timeout) { @@ -312,7 +334,7 @@ static void calc_rate(int unified_rw_rep, unsigned long mtime, else this_rate = 0; - if (unified_rw_rep) { + if (unified_rw_rep == UNIFIED_MIXED) { rate[i] = 0; rate[0] += this_rate; } else @@ -337,7 +359,7 @@ static void calc_iops(int unified_rw_rep, unsigned long mtime, else this_iops = 0; - if (unified_rw_rep) { + if (unified_rw_rep == UNIFIED_MIXED) { iops[i] = 0; iops[0] += this_iops; } else @@ -355,17 +377,33 @@ bool eta_time_within_slack(unsigned int time) return time > ((eta_interval_msec * 95) / 100); } +/* + * These are the conditions under which we might be able to skip the eta + * calculation. + */ +static bool skip_eta() +{ + if (!(output_format & FIO_OUTPUT_NORMAL) && f_out == stdout) + return true; + if (temp_stall_ts || eta_print == FIO_ETA_NEVER) + return true; + if (!isatty(STDOUT_FILENO) && eta_print != FIO_ETA_ALWAYS) + return true; + + return false; +} + /* * Print status of the jobs we know about. This includes rate estimates, * ETA, thread state, etc. */ -bool calc_thread_status(struct jobs_eta *je, int force) +static bool calc_thread_status(struct jobs_eta *je, int force) { - struct thread_data *td; - int i, unified_rw_rep; + int unified_rw_rep; + bool any_td_in_ramp; uint64_t rate_time, disp_time, bw_avg_time, *eta_secs; - unsigned long long io_bytes[DDIR_RWDIR_CNT]; - unsigned long long io_iops[DDIR_RWDIR_CNT]; + unsigned long long io_bytes[DDIR_RWDIR_CNT] = {}; + unsigned long long io_iops[DDIR_RWDIR_CNT] = {}; struct timespec now; static unsigned long long rate_io_bytes[DDIR_RWDIR_CNT]; @@ -373,14 +411,12 @@ bool calc_thread_status(struct jobs_eta *je, int force) static unsigned long long disp_io_iops[DDIR_RWDIR_CNT]; static struct timespec rate_prev_time, disp_prev_time; - if (!force) { - if (!(output_format & FIO_OUTPUT_NORMAL) && - f_out == stdout) - return false; - if (temp_stall_ts || eta_print == FIO_ETA_NEVER) - return false; + bool ret = true; - if (!isatty(STDOUT_FILENO) && (eta_print != FIO_ETA_ALWAYS)) + if (!force && skip_eta()) { + if (write_bw_log) + ret = false; + else return false; } @@ -389,20 +425,18 @@ bool calc_thread_status(struct jobs_eta *je, int force) if (!ddir_rw_sum(disp_io_bytes)) fill_start_time(&disp_prev_time); - eta_secs = malloc(thread_number * sizeof(uint64_t)); - memset(eta_secs, 0, thread_number * sizeof(uint64_t)); + eta_secs = calloc(thread_number, sizeof(uint64_t)); je->elapsed_sec = (mtime_since_genesis() + 999) / 1000; - io_bytes[DDIR_READ] = io_bytes[DDIR_WRITE] = io_bytes[DDIR_TRIM] = 0; - io_iops[DDIR_READ] = io_iops[DDIR_WRITE] = io_iops[DDIR_TRIM] = 0; bw_avg_time = ULONG_MAX; unified_rw_rep = 0; - for_each_td(td, i) { + for_each_td(td) { unified_rw_rep += td->o.unified_rw_rep; if (is_power_of_2(td->o.kb_base)) je->is_pow2 = 1; je->unit_base = td->o.unit_base; + je->sig_figs = td->o.sig_figs; if (td->o.bw_avg_time < bw_avg_time) bw_avg_time = td->o.bw_avg_time; if (td->runstate == TD_RUNNING || td->runstate == TD_VERIFYING @@ -439,9 +473,9 @@ bool calc_thread_status(struct jobs_eta *je, int force) je->nr_pending++; if (je->elapsed_sec >= 3) - eta_secs[i] = thread_eta(td); + eta_secs[__td_index] = thread_eta(td); else - eta_secs[i] = INT_MAX; + eta_secs[__td_index] = INT_MAX; check_str_update(td); @@ -458,26 +492,26 @@ bool calc_thread_status(struct jobs_eta *je, int force) } } } - } + } end_for_each(); if (exitall_on_terminate) { je->eta_sec = INT_MAX; - for_each_td(td, i) { - if (eta_secs[i] < je->eta_sec) - je->eta_sec = eta_secs[i]; - } + for_each_td_index() { + if (eta_secs[__td_index] < je->eta_sec) + je->eta_sec = eta_secs[__td_index]; + } end_for_each(); } else { unsigned long eta_stone = 0; je->eta_sec = 0; - for_each_td(td, i) { + for_each_td(td) { if ((td->runstate == TD_NOT_CREATED) && td->o.stonewall) - eta_stone += eta_secs[i]; + eta_stone += eta_secs[__td_index]; else { - if (eta_secs[i] > je->eta_sec) - je->eta_sec = eta_secs[i]; + if (eta_secs[__td_index] > je->eta_sec) + je->eta_sec = eta_secs[__td_index]; } - } + } end_for_each(); je->eta_sec += eta_stone; } @@ -486,13 +520,18 @@ bool calc_thread_status(struct jobs_eta *je, int force) fio_gettime(&now, NULL); rate_time = mtime_since(&rate_prev_time, &now); - if (write_bw_log && rate_time > bw_avg_time && !in_ramp_time(td)) { + any_td_in_ramp = false; + for_each_td(td) { + any_td_in_ramp |= in_ramp_time(td); + } end_for_each(); + if (write_bw_log && rate_time > bw_avg_time && !any_td_in_ramp) { calc_rate(unified_rw_rep, rate_time, io_bytes, rate_io_bytes, je->rate); memcpy(&rate_prev_time, &now, sizeof(now)); - add_agg_sample(sample_val(je->rate[DDIR_READ]), DDIR_READ, 0); - add_agg_sample(sample_val(je->rate[DDIR_WRITE]), DDIR_WRITE, 0); - add_agg_sample(sample_val(je->rate[DDIR_TRIM]), DDIR_TRIM, 0); + regrow_agg_logs(); + for_each_rw_ddir(ddir) { + add_agg_sample(sample_val(je->rate[ddir]), ddir, 0); + } } disp_time = mtime_since(&disp_prev_time, &now); @@ -511,7 +550,46 @@ bool calc_thread_status(struct jobs_eta *je, int force) je->nr_threads = thread_number; update_condensed_str(__run_str, run_str); memcpy(je->run_str, run_str, strlen(run_str)); - return true; + return ret; +} + +static int gen_eta_str(struct jobs_eta *je, char *p, size_t left, + char **rate_str, char **iops_str) +{ + static const char c[DDIR_RWDIR_CNT] = {'r', 'w', 't'}; + bool has[DDIR_RWDIR_CNT]; + bool has_any = false; + const char *sep; + int l = 0; + + for_each_rw_ddir(ddir) { + has[ddir] = (je->rate[ddir] || je->iops[ddir]); + has_any |= has[ddir]; + } + if (!has_any) + return 0; + + l += snprintf(p + l, left - l, "["); + sep = ""; + for_each_rw_ddir(ddir) { + if (has[ddir]) { + l += snprintf(p + l, left - l, "%s%c=%s", + sep, c[ddir], rate_str[ddir]); + sep = ","; + } + } + l += snprintf(p + l, left - l, "]["); + sep = ""; + for_each_rw_ddir(ddir) { + if (has[ddir]) { + l += snprintf(p + l, left - l, "%s%c=%s", + sep, c[ddir], iops_str[ddir]); + sep = ","; + } + } + l += snprintf(p + l, left - l, " IOPS]"); + + return l; } void display_thread_status(struct jobs_eta *je) @@ -520,7 +598,7 @@ void display_thread_status(struct jobs_eta *je) static int eta_new_line_init, eta_new_line_pending; static int linelen_last; static int eta_good; - char output[REAL_MAX_JOBS + 512], *p = output; + char output[__THREAD_RUNSTR_SZ(REAL_MAX_JOBS) + 512], *p = output; char eta_str[128]; double perc = 0.0; @@ -531,6 +609,7 @@ void display_thread_status(struct jobs_eta *je) if (eta_new_line_pending) { eta_new_line_pending = 0; + linelen_last = 0; p += sprintf(p, "\n"); } @@ -542,9 +621,9 @@ void display_thread_status(struct jobs_eta *je) char *tr, *mr; mr = num2str(je->m_rate[0] + je->m_rate[1] + je->m_rate[2], - je->sig_figs, 0, je->is_pow2, N2S_BYTEPERSEC); + je->sig_figs, 1, je->is_pow2, N2S_BYTEPERSEC); tr = num2str(je->t_rate[0] + je->t_rate[1] + je->t_rate[2], - je->sig_figs, 0, je->is_pow2, N2S_BYTEPERSEC); + je->sig_figs, 1, je->is_pow2, N2S_BYTEPERSEC); p += sprintf(p, ", %s-%s", mr, tr); free(tr); @@ -564,6 +643,7 @@ void display_thread_status(struct jobs_eta *je) size_t left; int l; int ddir; + int linelen; if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running || je->eta_sec == -1) @@ -586,32 +666,25 @@ void display_thread_status(struct jobs_eta *je) } left = sizeof(output) - (p - output) - 1; + l = snprintf(p, left, ": [%s][%s]", je->run_str, perc_str); + l += gen_eta_str(je, p + l, left - l, rate_str, iops_str); + l += snprintf(p + l, left - l, "[eta %s]", eta_str); - if (je->rate[DDIR_TRIM] || je->iops[DDIR_TRIM]) - l = snprintf(p, left, - ": [%s][%s][r=%s,w=%s,t=%s][r=%s,w=%s,t=%s IOPS][eta %s]", - je->run_str, perc_str, rate_str[DDIR_READ], - rate_str[DDIR_WRITE], rate_str[DDIR_TRIM], - iops_str[DDIR_READ], iops_str[DDIR_WRITE], - iops_str[DDIR_TRIM], eta_str); - else - l = snprintf(p, left, - ": [%s][%s][r=%s,w=%s][r=%s,w=%s IOPS][eta %s]", - je->run_str, perc_str, - rate_str[DDIR_READ], rate_str[DDIR_WRITE], - iops_str[DDIR_READ], iops_str[DDIR_WRITE], - eta_str); + /* If truncation occurred adjust l so p is on the null */ + if (l >= left) + l = left - 1; p += l; - if (l >= 0 && l < linelen_last) - p += sprintf(p, "%*s", linelen_last - l, ""); - linelen_last = l; + linelen = p - output; + if (l >= 0 && linelen < linelen_last) + p += sprintf(p, "%*s", linelen_last - linelen, ""); + linelen_last = linelen; for (ddir = 0; ddir < DDIR_RWDIR_CNT; ddir++) { free(rate_str[ddir]); free(iops_str[ddir]); } } - p += sprintf(p, "\r"); + sprintf(p, "\r"); printf("%s", output); @@ -634,10 +707,9 @@ struct jobs_eta *get_jobs_eta(bool force, size_t *size) return NULL; *size = sizeof(*je) + THREAD_RUNSTR_SZ + 8; - je = malloc(*size); + je = calloc(1, *size); if (!je) return NULL; - memset(je, 0, *size); if (!calc_thread_status(je, force)) { free(je); @@ -654,14 +726,19 @@ void print_thread_status(void) size_t size; je = get_jobs_eta(false, &size); - if (je) + if (je) { display_thread_status(je); - - free(je); + free(je); + } } void print_status_init(int thr_number) { + struct jobs_eta_packed jep; + + compiletime_assert(sizeof(struct jobs_eta) == sizeof(jep), "jobs_eta"); + + DRD_IGNORE_VAR(__run_str); __run_str[thr_number] = 'P'; update_condensed_str(__run_str, run_str); }