The following changes since commit 65f21d61d5d0796335ceb3320b8846e4d6d30ac7: xxhash: dos2unix'ize (2014-02-20 13:29:42 -0800) are available in the git repository at: git://git.kernel.dk/fio.git master for you to fetch changes up to 0de5b26f6e177aacac0683306c47e0cbaf58b0b6: Unify the time handling (2014-02-21 15:26:01 -0800) ---------------------------------------------------------------- Jens Axboe (3): Fixup time multipliers Improve latency_target runs Unify the time handling HOWTO | 3 ++- backend.c | 6 +++--- eta.c | 20 ++++++++++++-------- fio.1 | 4 ++-- fio.h | 2 ++ init.c | 4 ++-- io_u.c | 37 +++++++++++++++++++++++++++++++------ libfio.c | 2 +- options.c | 7 +++++-- parse.c | 40 ++++++++++++++++++++++++---------------- parse.h | 5 +++-- stat.c | 35 +++++++++++++++++++++++++++++++++++ stat.h | 1 + time.c | 7 ++++++- time.h | 1 + 15 files changed, 130 insertions(+), 44 deletions(-) --- Diff of recent changes: diff --git a/HOWTO b/HOWTO index bafad93..4dacd98 100644 --- a/HOWTO +++ b/HOWTO @@ -223,7 +223,8 @@ a string. The following types are used: str String. This is a sequence of alpha characters. time Integer with possible time suffix. In seconds unless otherwise specified, use eg 10m for 10 minutes. Accepts s/m/h for seconds, - minutes, and hours. + minutes, and hours, and accepts 'ms' (or 'msec') for milliseconds, + and 'us' (or 'usec') for microseconds. int SI integer. A whole number value, which may contain a suffix describing the base of the number. Accepted suffixes are k/m/g/t/p, meaning kilo, mega, giga, tera, and peta. The suffix is not case diff --git a/backend.c b/backend.c index 87aec87..ee395bd 100644 --- a/backend.c +++ b/backend.c @@ -346,7 +346,7 @@ static inline int runtime_exceeded(struct thread_data *td, struct timeval *t) return 0; if (!td->o.timeout) return 0; - if (mtime_since(&td->epoch, t) >= td->o.timeout ) + if (utime_since(&td->epoch, t) >= td->o.timeout) return 1; return 0; @@ -1683,8 +1683,8 @@ static void do_usleep(unsigned int usecs) static void run_threads(void) { struct thread_data *td; - unsigned long spent; unsigned int i, todo, nr_running, m_rate, t_rate, nr_started; + uint64_t spent; if (fio_gtod_offload && fio_start_gtod_thread()) return; @@ -1782,7 +1782,7 @@ static void run_threads(void) } if (td->o.start_delay) { - spent = mtime_since_genesis(); + spent = utime_since_genesis(); if (td->o.start_delay > spent) continue; diff --git a/eta.c b/eta.c index e12e4fc..b050102 100644 --- a/eta.c +++ b/eta.c @@ -127,8 +127,10 @@ static int thread_eta(struct thread_data *td) unsigned long long bytes_total, bytes_done; unsigned long eta_sec = 0; unsigned long elapsed; + uint64_t timeout; elapsed = (mtime_since_now(&td->epoch) + 999) / 1000; + timeout = td->o.timeout / 1000000UL; bytes_total = td->total_io_size; @@ -174,8 +176,7 @@ static int thread_eta(struct thread_data *td) perc = 1.0; if (td->o.time_based) { - perc_t = (double) elapsed / - (double) (td->o.timeout / 1000); + perc_t = (double) elapsed / (double) timeout; if (perc_t < perc) perc = perc_t; } @@ -183,9 +184,8 @@ static int thread_eta(struct thread_data *td) eta_sec = (unsigned long) (elapsed * (1.0 / perc)) - elapsed; if (td->o.timeout && - eta_sec > ( (td->o.timeout / 1000) + done_secs - elapsed)) - eta_sec = (td->o.timeout / 1000) + done_secs - - elapsed; + eta_sec > (timeout + done_secs - elapsed)) + eta_sec = timeout + done_secs - elapsed; } else if (td->runstate == TD_NOT_CREATED || td->runstate == TD_CREATED || td->runstate == TD_INITIALIZED || td->runstate == TD_SETTING_UP @@ -199,8 +199,12 @@ static int thread_eta(struct thread_data *td) * if given, otherwise assume it'll run at the specified rate. */ if (td->o.timeout) { - t_eta = (td->o.timeout + td->o.start_delay + - td->o.ramp_time ) / 1000; + uint64_t timeout = td->o.timeout; + uint64_t start_delay = td->o.start_delay; + uint64_t ramp_time = td->o.ramp_time; + + t_eta = timeout + start_delay + ramp_time; + t_eta /= 1000000ULL; if (in_ramp_time(td)) { unsigned long ramp_left; @@ -214,7 +218,7 @@ static int thread_eta(struct thread_data *td) rate_bytes = ddir_rw_sum(td->o.rate); if (rate_bytes) { r_eta = (bytes_total / 1024) / rate_bytes; - r_eta += td->o.start_delay / 1000; + r_eta += (td->o.start_delay / 1000000ULL); } if (r_eta && t_eta) diff --git a/fio.1 b/fio.1 index 81b03f7..c530d84 100644 --- a/fio.1 +++ b/fio.1 @@ -131,8 +131,8 @@ etc. This is useful for disk drives where values are often given in base 10 values. Specifying '30GiB' will get you 30*1000^3 bytes. When specifying times the default suffix meaning changes, still denoting the base unit of the value, but accepted suffixes are 'D' (days), 'H' (hours), 'M' -(minutes), 'S' Seconds, 'ms' milli seconds. Time values without a unit specify -seconds. +(minutes), 'S' Seconds, 'ms' (or msec) milli seconds, 'us' (or 'usec') micro +seconds. Time values without a unit specify seconds. The suffixes are not case sensitive. .TP .I bool diff --git a/fio.h b/fio.h index d1180cd..9159b0c 100644 --- a/fio.h +++ b/fio.h @@ -262,6 +262,7 @@ struct thread_data { unsigned int latency_qd_low; unsigned int latency_failed; uint64_t latency_ios; + int latency_end_run; /* * read/write mixed workload state @@ -504,6 +505,7 @@ extern int load_blktrace(struct thread_data *, const char *, int); */ extern void lat_target_check(struct thread_data *); extern void lat_target_init(struct thread_data *); +extern void lat_target_reset(struct thread_data *); #define for_each_td(td, i) \ for ((i) = 0, (td) = &threads[0]; (i) < (int) thread_number; (i)++, (td)++) diff --git a/init.c b/init.c index 5fc3b22..6a65e55 100644 --- a/init.c +++ b/init.c @@ -1773,7 +1773,7 @@ int parse_cmd_line(int argc, char *argv[], int client_type) case 'E': { long long t = 0; - if (str_to_decimal(optarg, &t, 0, NULL)) { + if (str_to_decimal(optarg, &t, 0, NULL, 1)) { log_err("fio: failed parsing eta time %s\n", optarg); exit_val = 1; do_exit++; @@ -1933,7 +1933,7 @@ int parse_cmd_line(int argc, char *argv[], int client_type) case 'L': { long long val; - if (check_str_time(optarg, &val)) { + if (check_str_time(optarg, &val, 1)) { log_err("fio: failed parsing time %s\n", optarg); do_exit++; exit_val = 1; diff --git a/io_u.c b/io_u.c index b84b3e2..619fa25 100644 --- a/io_u.c +++ b/io_u.c @@ -1162,6 +1162,10 @@ static int __lat_target_failed(struct thread_data *td) return 1; td->latency_qd_high = td->latency_qd; + + if (td->latency_qd == td->latency_qd_low) + td->latency_qd_low--; + td->latency_qd = (td->latency_qd + td->latency_qd_low) / 2; dprint(FD_RATE, "Ramped down: %d %d %d\n", td->latency_qd_low, td->latency_qd, td->latency_qd_high); @@ -1186,6 +1190,8 @@ static int lat_target_failed(struct thread_data *td) void lat_target_init(struct thread_data *td) { + td->latency_end_run = 0; + if (td->o.latency_target) { dprint(FD_RATE, "Latency target=%llu\n", td->o.latency_target); fio_gettime(&td->latency_ts, NULL); @@ -1197,9 +1203,16 @@ void lat_target_init(struct thread_data *td) td->latency_qd = td->o.iodepth; } +void lat_target_reset(struct thread_data *td) +{ + if (!td->latency_end_run) + lat_target_init(td); +} + static void lat_target_success(struct thread_data *td) { const unsigned int qd = td->latency_qd; + struct thread_options *o = &td->o; td->latency_qd_low = td->latency_qd; @@ -1208,20 +1221,32 @@ static void lat_target_success(struct thread_data *td) * of bisecting from highest possible queue depth. If we have set * a limit other than td->o.iodepth, bisect between that. */ - if (td->latency_qd_high != td->o.iodepth) + if (td->latency_qd_high != o->iodepth) td->latency_qd = (td->latency_qd + td->latency_qd_high) / 2; else td->latency_qd *= 2; - if (td->latency_qd > td->o.iodepth) - td->latency_qd = td->o.iodepth; + if (td->latency_qd > o->iodepth) + td->latency_qd = o->iodepth; dprint(FD_RATE, "Ramped up: %d %d %d\n", td->latency_qd_low, td->latency_qd, td->latency_qd_high); + /* - * Same as last one, we are done + * Same as last one, we are done. Let it run a latency cycle, so + * we get only the results from the targeted depth. */ - if (td->latency_qd == qd) - td->done = 1; + if (td->latency_qd == qd) { + if (td->latency_end_run) { + dprint(FD_RATE, "We are done\n"); + td->done = 1; + } else { + dprint(FD_RATE, "Quiesce and final run\n"); + io_u_quiesce(td); + td->latency_end_run = 1; + reset_all_stats(td); + reset_io_stats(td); + } + } lat_new_cycle(td); } diff --git a/libfio.c b/libfio.c index 222cd16..f4aac2e 100644 --- a/libfio.c +++ b/libfio.c @@ -135,7 +135,7 @@ void reset_all_stats(struct thread_data *td) memcpy(&td->epoch, &tv, sizeof(tv)); memcpy(&td->start, &tv, sizeof(tv)); - lat_target_init(td); + lat_target_reset(td); } void reset_fio_state(void) diff --git a/options.c b/options.c index 87a4432..5355982 100644 --- a/options.c +++ b/options.c @@ -102,7 +102,7 @@ static int bssplit_ddir(struct thread_options *o, int ddir, char *str) } else perc = -1; - if (str_to_decimal(fname, &val, 1, o)) { + if (str_to_decimal(fname, &val, 1, o, 0)) { log_err("fio: bssplit conversion failed\n"); free(bssplit); return 1; @@ -336,7 +336,7 @@ static int str_rw_cb(void *data, const char *str) else { long long val; - if (str_to_decimal(nr, &val, 1, o)) { + if (str_to_decimal(nr, &val, 1, o, 0)) { log_err("fio: rw postfix parsing failed\n"); free(nr); return 1; @@ -2068,6 +2068,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .off2 = td_var_offset(start_delay_high), .help = "Only start job when this period has passed", .def = "0", + .is_seconds = 1, .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_RUNTIME, }, @@ -2079,6 +2080,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .off1 = td_var_offset(timeout), .help = "Stop workload when this amount of time has passed", .def = "0", + .is_seconds = 1, .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_RUNTIME, }, @@ -2106,6 +2108,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .type = FIO_OPT_STR_VAL_TIME, .off1 = td_var_offset(ramp_time), .help = "Ramp up time before measuring performance", + .is_seconds = 1, .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_RUNTIME, }, diff --git a/parse.c b/parse.c index c8bae03..079f19e 100644 --- a/parse.c +++ b/parse.c @@ -122,11 +122,12 @@ static void show_option_help(struct fio_option *o, int is_err) show_option_values(o); } -static unsigned long long get_mult_time(const char *str, int len) +static unsigned long long get_mult_time(const char *str, int len, + int is_seconds) { const char *p = str; char *c; - unsigned long long mult = 1000; + unsigned long long mult = 1; /* * Go forward until we hit a non-digit, or +/- sign @@ -137,23 +138,29 @@ static unsigned long long get_mult_time(const char *str, int len) p++; } - if (!isalpha((int) *p)) - return 1000; + if (!isalpha((int) *p)) { + if (is_seconds) + return 1000000UL; + else + return 1; + } c = strdup(p); for (int i = 0; i < strlen(c); i++) c[i] = tolower(c[i]); - if (!strncmp("ms", c, 2)) + if (!strncmp("us", c, 2) || !strncmp("usec", c, 4)) mult = 1; - else if (!strcmp("s", c)) + else if (!strncmp("ms", c, 2) || !strncmp("msec", c, 4)) mult = 1000; + else if (!strcmp("s", c)) + mult = 1000000; else if (!strcmp("m", c)) - mult = 60 * 1000; + mult = 60 * 1000000UL; else if (!strcmp("h", c)) - mult = 60 * 60 * 1000; + mult = 60 * 60 * 1000000UL; else if (!strcmp("d", c)) - mult = 24 * 60 * 60 * 1000; + mult = 24 * 60 * 60 * 1000000UL; free(c); return mult; @@ -268,7 +275,8 @@ int str_to_float(const char *str, double *val) /* * convert string into decimal value, noting any size suffix */ -int str_to_decimal(const char *str, long long *val, int kilo, void *data) +int str_to_decimal(const char *str, long long *val, int kilo, void *data, + int is_seconds) { int len, base; @@ -295,19 +303,19 @@ int str_to_decimal(const char *str, long long *val, int kilo, void *data) else *val *= mult; } else - *val *= get_mult_time(str, len); + *val *= get_mult_time(str, len, is_seconds); return 0; } int check_str_bytes(const char *p, long long *val, void *data) { - return str_to_decimal(p, val, 1, data); + return str_to_decimal(p, val, 1, data, 0); } -int check_str_time(const char *p, long long *val) +int check_str_time(const char *p, long long *val, int is_seconds) { - return str_to_decimal(p, val, 0, NULL); + return str_to_decimal(p, val, 0, NULL, is_seconds); } void strip_blank_front(char **p) @@ -349,7 +357,7 @@ static int check_range_bytes(const char *str, long *val, void *data) { long long __val; - if (!str_to_decimal(str, &__val, 1, data)) { + if (!str_to_decimal(str, &__val, 1, data, 0)) { *val = __val; return 0; } @@ -459,7 +467,7 @@ static int __handle_option(struct fio_option *o, const char *ptr, void *data, *p = '\0'; if (is_time) - ret = check_str_time(tmp, &ull); + ret = check_str_time(tmp, &ull, o->is_seconds); else ret = check_str_bytes(tmp, &ull, data); diff --git a/parse.h b/parse.h index 1009252..c797b92 100644 --- a/parse.h +++ b/parse.h @@ -72,6 +72,7 @@ struct fio_option { unsigned int category; /* what type of option */ unsigned int group; /* who to group with */ void *gui_data; + int is_seconds; /* time value with seconds base */ }; typedef int (str_cb_fn)(void *, char *); @@ -87,9 +88,9 @@ extern void options_free(struct fio_option *, void *); extern void strip_blank_front(char **); extern void strip_blank_end(char *); -extern int str_to_decimal(const char *, long long *, int, void *); +extern int str_to_decimal(const char *, long long *, int, void *, int); extern int check_str_bytes(const char *p, long long *val, void *data); -extern int check_str_time(const char *p, long long *val); +extern int check_str_time(const char *p, long long *val, int); extern int str_to_float(const char *str, double *val); /* diff --git a/stat.c b/stat.c index bc01b51..e43db8f 100644 --- a/stat.c +++ b/stat.c @@ -1579,6 +1579,41 @@ static inline void reset_io_stat(struct io_stat *ios) ios->mean.u.f = ios->S.u.f = 0; } +void reset_io_stats(struct thread_data *td) +{ + struct thread_stat *ts = &td->ts; + int i, j; + + for (i = 0; i < DDIR_RWDIR_CNT; i++) { + reset_io_stat(&ts->clat_stat[i]); + reset_io_stat(&ts->slat_stat[i]); + reset_io_stat(&ts->lat_stat[i]); + reset_io_stat(&ts->bw_stat[i]); + reset_io_stat(&ts->iops_stat[i]); + + ts->io_bytes[i] = 0; + ts->runtime[i] = 0; + + for (j = 0; j < FIO_IO_U_PLAT_NR; j++) + ts->io_u_plat[i][j] = 0; + } + + for (i = 0; i < FIO_IO_U_MAP_NR; i++) { + ts->io_u_map[i] = 0; + ts->io_u_submit[i] = 0; + ts->io_u_complete[i] = 0; + ts->io_u_lat_u[i] = 0; + ts->io_u_lat_m[i] = 0; + ts->total_submit = 0; + ts->total_complete = 0; + } + + for (i = 0; i < 3; i++) { + ts->total_io_u[i] = 0; + ts->short_io_u[i] = 0; + } +} + static void _add_stat_to_log(struct io_log *iolog, unsigned long elapsed) { /* diff --git a/stat.h b/stat.h index 7ad0c9d..bc4f6da 100644 --- a/stat.h +++ b/stat.h @@ -224,6 +224,7 @@ extern unsigned int calc_clat_percentiles(unsigned int *io_u_plat, unsigned long extern void stat_calc_lat_m(struct thread_stat *ts, double *io_u_lat); extern void stat_calc_lat_u(struct thread_stat *ts, double *io_u_lat); extern void stat_calc_dist(unsigned int *map, unsigned long total, double *io_u_dist); +extern void reset_io_stats(struct thread_data *); static inline int usec_to_msec(unsigned long *min, unsigned long *max, double *mean, double *dev) diff --git a/time.c b/time.c index b374d3e..f3de3e7 100644 --- a/time.c +++ b/time.c @@ -58,6 +58,11 @@ uint64_t mtime_since_genesis(void) return mtime_since_now(&genesis); } +uint64_t utime_since_genesis(void) +{ + return utime_since_now(&genesis); +} + int in_ramp_time(struct thread_data *td) { return td->o.ramp_time && !td->ramp_time_over; @@ -71,7 +76,7 @@ int ramp_time_over(struct thread_data *td) return 1; fio_gettime(&tv, NULL); - if (mtime_since(&td->epoch, &tv) >= td->o.ramp_time ) { + if (utime_since(&td->epoch, &tv) >= td->o.ramp_time) { td->ramp_time_over = 1; reset_all_stats(td); td_set_runstate(td, TD_RAMP); diff --git a/time.h b/time.h index d835191..c550a55 100644 --- a/time.h +++ b/time.h @@ -7,6 +7,7 @@ extern uint64_t mtime_since(struct timeval *, struct timeval *); extern uint64_t mtime_since_now(struct timeval *); extern uint64_t time_since_now(struct timeval *); extern uint64_t mtime_since_genesis(void); +extern uint64_t utime_since_genesis(void); extern void usec_spin(unsigned int); extern void usec_sleep(struct thread_data *, unsigned long); extern void fill_start_time(struct timeval *); -- To unsubscribe from this list: send the line "unsubscribe fio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html