The following changes since commit b2fcbe01bdac01bc5d7f8ddea94f264b9f8c2003: Ensure that thread_stat alignment is correct (2017-06-19 16:41:51 -0600) are available in the git repository at: git://git.kernel.dk/fio.git master for you to fetch changes up to cf6b7fb4f1883af9cbc443ed2536e7454ed51215: t/time-test: cleanups (2017-06-22 19:29:35 -0600) ---------------------------------------------------------------- Jens Axboe (10): Merge branch 'nanosecond-2stage' of https://github.com/vincentkfu/fio into nsec Fixup some style issues Merge branch 'nsec' crc32c: use bool arch: tsc_reliable can be a bool client/server: bool conversion iolog: get work items out of shared memory pool iolog: punt freeing of data back to original thread iolog: ensure proper flushing of compressed logs t/time-test: cleanups Vincent Fu (12): nanosecond: initial commit changing timeval to timespec nanosecond: update completion latency recording and normal, json output to use nanoseconds nanosecond: reconcile terse output with nanosecond timing for latencies nanosecond: alter gfio to accommodate nanosecond timing nanosecond: fiologparser_hist set default --group_nr to 29 to match stat.h FIO_IO_U_PLAT_GROUP_NR nanosecond: fix up conversion of ticks to nsec by doing the conversion in 2 stages nanosecond: add test program t/time-test for experimenting with cpu clock ticks to nsec conversion lib/seqlock: #include "types.h" for bool type nanosecond: update t/time-test.c to include experiments using seqlock for conversion gettime: for better accuracy calculate cycles_per_msec instead of cycles_per_usec gettime: drop tv_valid->last_cycles and tv_valid->last_tv_valid server: bump server version for the change to FIO_IO_U_PLAT_GROUP_NR HOWTO | 2 +- Makefile | 7 + arch/arch-ia64.h | 4 +- arch/arch-ppc.h | 4 +- arch/arch-s390.h | 4 +- arch/arch-x86-common.h | 2 +- arch/arch.h | 2 + backend.c | 42 ++-- client.c | 36 +-- client.h | 11 +- crc/crc32c-arm64.c | 9 +- crc/crc32c-intel.c | 6 +- crc/crc32c.h | 5 +- crc/test.c | 6 +- diskutil.c | 2 +- diskutil.h | 2 +- engines/guasi.c | 2 +- engines/libaio.c | 8 +- engines/rdma.c | 2 +- eta.c | 6 +- fio.h | 24 +- fio_time.h | 16 +- gclient.c | 56 +++-- gettime-thread.c | 14 +- gettime.c | 193 ++++++++++---- gettime.h | 14 +- helper_thread.c | 32 +-- idletime.c | 14 +- idletime.h | 4 +- io_u.c | 91 +++++-- io_u.h | 4 +- ioengines.c | 4 +- iolog.c | 52 +++- iolog.h | 7 +- lib/seqlock.h | 1 + libfio.c | 8 +- mutex.c | 12 +- options.c | 2 +- os/windows/posix.c | 6 +- profiles/act.c | 2 +- server.c | 13 +- server.h | 6 +- stat.c | 217 ++++++++++------ stat.h | 38 ++- steadystate.c | 2 +- steadystate.h | 2 +- t/arch.c | 2 +- t/debug.c | 2 +- t/dedupe.c | 2 +- t/lfsr-test.c | 2 +- t/time-test.c | 544 ++++++++++++++++++++++++++++++++++++++++ time.c | 36 ++- tools/hist/fiologparser_hist.py | 2 +- verify.c | 2 +- 54 files changed, 1203 insertions(+), 385 deletions(-) create mode 100644 t/time-test.c --- Diff of recent changes: diff --git a/HOWTO b/HOWTO index d3a5783..22c5a5b 100644 --- a/HOWTO +++ b/HOWTO @@ -3189,7 +3189,7 @@ Split up, the format is as follows: Total IO (KiB), bandwidth (KiB/sec), IOPS, runtime (msec) Submission latency: min, max, mean, stdev (usec) - Completion latency: min, max, mean, stdev(usec) + Completion latency: min, max, mean, stdev (usec) Completion latency percentiles: 20 fields (see below) Total latency: min, max, mean, stdev (usec) Bw (KiB/s): min, max, aggregate percentage of total, mean, stdev diff --git a/Makefile b/Makefile index d7786d2..64fa97a 100644 --- a/Makefile +++ b/Makefile @@ -250,6 +250,9 @@ T_PIPE_ASYNC_PROGS = t/read-to-pipe-async T_MEMLOCK_OBJS = t/memlock.o T_MEMLOCK_PROGS = t/memlock +T_TT_OBJS = t/time-test.o +T_TT_PROGS = t/time-test + T_OBJS = $(T_SMALLOC_OBJS) T_OBJS += $(T_IEEE_OBJS) T_OBJS += $(T_ZIPF_OBJS) @@ -261,6 +264,7 @@ T_OBJS += $(T_DEDUPE_OBJS) T_OBJS += $(T_VS_OBJS) T_OBJS += $(T_PIPE_ASYNC_OBJS) T_OBJS += $(T_MEMLOCK_OBJS) +T_OBJS += $(T_TT_OBJS) ifneq (,$(findstring CYGWIN,$(CONFIG_TARGET_OS))) T_DEDUPE_OBJS += os/windows/posix.o lib/hweight.o @@ -434,6 +438,9 @@ t/fio-dedupe: $(T_DEDUPE_OBJS) t/fio-verify-state: $(T_VS_OBJS) $(QUIET_LINK)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(T_VS_OBJS) $(LIBS) +t/time-test: $(T_TT_OBJS) + $(QUIET_LINK)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(T_TT_OBJS) $(LIBS) + clean: FORCE @rm -f .depend $(FIO_OBJS) $(GFIO_OBJS) $(OBJS) $(T_OBJS) $(PROGS) $(T_PROGS) $(T_TEST_PROGS) core.* core gfio FIO-VERSION-FILE *.d lib/*.d oslib/*.d crc/*.d engines/*.d profiles/*.d t/*.d config-host.mak config-host.h y.tab.[ch] lex.yy.c exp/*.[do] lexer.h @rm -rf doc/output diff --git a/arch/arch-ia64.h b/arch/arch-ia64.h index 53c049f..ece3f7e 100644 --- a/arch/arch-ia64.h +++ b/arch/arch-ia64.h @@ -28,10 +28,10 @@ static inline unsigned long long get_cpu_clock(void) } #define ARCH_HAVE_INIT -extern int tsc_reliable; +extern bool tsc_reliable; static inline int arch_init(char *envp[]) { - tsc_reliable = 1; + tsc_reliable = true; return 0; } diff --git a/arch/arch-ppc.h b/arch/arch-ppc.h index 4a8aa97..ba452b1 100644 --- a/arch/arch-ppc.h +++ b/arch/arch-ppc.h @@ -117,12 +117,12 @@ static void atb_clocktest(void) #endif #define ARCH_HAVE_INIT -extern int tsc_reliable; +extern bool tsc_reliable; static inline int arch_init(char *envp[]) { #if 0 - tsc_reliable = 1; + tsc_reliable = true; atb_clocktest(); #endif return 0; diff --git a/arch/arch-s390.h b/arch/arch-s390.h index 2e84bf8..6bf033b 100644 --- a/arch/arch-s390.h +++ b/arch/arch-s390.h @@ -28,10 +28,10 @@ static inline unsigned long long get_cpu_clock(void) #undef ARCH_CPU_CLOCK_WRAPS #define ARCH_HAVE_INIT -extern int tsc_reliable; +extern bool tsc_reliable; static inline int arch_init(char *envp[]) { - tsc_reliable = 1; + tsc_reliable = true; return 0; } diff --git a/arch/arch-x86-common.h b/arch/arch-x86-common.h index cbf66b8..c51c04c 100644 --- a/arch/arch-x86-common.h +++ b/arch/arch-x86-common.h @@ -14,7 +14,7 @@ static inline void cpuid(unsigned int op, #define ARCH_HAVE_INIT -extern int tsc_reliable; +extern bool tsc_reliable; extern int arch_random; static inline void arch_init_intel(unsigned int level) diff --git a/arch/arch.h b/arch/arch.h index 00d247c..4fb9b51 100644 --- a/arch/arch.h +++ b/arch/arch.h @@ -1,6 +1,8 @@ #ifndef ARCH_H #define ARCH_H +#include "../lib/types.h" + enum { arch_x86_64 = 1, arch_x86, diff --git a/backend.c b/backend.c index 9a684ed..fe15997 100644 --- a/backend.c +++ b/backend.c @@ -136,7 +136,7 @@ static void set_sig_handlers(void) /* * Check if we are above the minimum rate given. */ -static bool __check_min_rate(struct thread_data *td, struct timeval *now, +static bool __check_min_rate(struct thread_data *td, struct timespec *now, enum fio_ddir ddir) { unsigned long long bytes = 0; @@ -223,7 +223,7 @@ static bool __check_min_rate(struct thread_data *td, struct timeval *now, return false; } -static bool check_min_rate(struct thread_data *td, struct timeval *now) +static bool check_min_rate(struct thread_data *td, struct timespec *now) { bool ret = false; @@ -335,18 +335,18 @@ static int fio_file_fsync(struct thread_data *td, struct fio_file *f) return ret; } -static inline void __update_tv_cache(struct thread_data *td) +static inline void __update_ts_cache(struct thread_data *td) { - fio_gettime(&td->tv_cache, NULL); + fio_gettime(&td->ts_cache, NULL); } -static inline void update_tv_cache(struct thread_data *td) +static inline void update_ts_cache(struct thread_data *td) { - if ((++td->tv_cache_nr & td->tv_cache_mask) == td->tv_cache_mask) - __update_tv_cache(td); + if ((++td->ts_cache_nr & td->ts_cache_mask) == td->ts_cache_mask) + __update_ts_cache(td); } -static inline bool runtime_exceeded(struct thread_data *td, struct timeval *t) +static inline bool runtime_exceeded(struct thread_data *td, struct timespec *t) { if (in_ramp_time(td)) return false; @@ -430,7 +430,7 @@ static void check_update_rusage(struct thread_data *td) } } -static int wait_for_completions(struct thread_data *td, struct timeval *time) +static int wait_for_completions(struct thread_data *td, struct timespec *time) { const int full = queue_full(td); int min_evts = 0; @@ -462,7 +462,7 @@ static int wait_for_completions(struct thread_data *td, struct timeval *time) int io_queue_event(struct thread_data *td, struct io_u *io_u, int *ret, enum fio_ddir ddir, uint64_t *bytes_issued, int from_verify, - struct timeval *comp_time) + struct timespec *comp_time) { int ret2; @@ -633,12 +633,12 @@ static void do_verify(struct thread_data *td, uint64_t verify_bytes) enum fio_ddir ddir; int full; - update_tv_cache(td); + update_ts_cache(td); check_update_rusage(td); - if (runtime_exceeded(td, &td->tv_cache)) { - __update_tv_cache(td); - if (runtime_exceeded(td, &td->tv_cache)) { + if (runtime_exceeded(td, &td->ts_cache)) { + __update_ts_cache(td); + if (runtime_exceeded(td, &td->ts_cache)) { fio_mark_td_terminate(td); break; } @@ -874,7 +874,7 @@ static void do_io(struct thread_data *td, uint64_t *bytes_done) while ((td->o.read_iolog_file && !flist_empty(&td->io_log_list)) || (!flist_empty(&td->trim_list)) || !io_issue_bytes_exceeded(td) || td->o.time_based) { - struct timeval comp_time; + struct timespec comp_time; struct io_u *io_u; int full; enum fio_ddir ddir; @@ -884,11 +884,11 @@ static void do_io(struct thread_data *td, uint64_t *bytes_done) if (td->terminate || td->done) break; - update_tv_cache(td); + update_ts_cache(td); - if (runtime_exceeded(td, &td->tv_cache)) { - __update_tv_cache(td); - if (runtime_exceeded(td, &td->tv_cache)) { + if (runtime_exceeded(td, &td->ts_cache)) { + __update_ts_cache(td); + if (runtime_exceeded(td, &td->ts_cache)) { fio_mark_td_terminate(td); break; } @@ -1686,7 +1686,7 @@ static void *thread_main(void *data) uint64_t verify_bytes; fio_gettime(&td->start, NULL); - memcpy(&td->tv_cache, &td->start, sizeof(td->start)); + memcpy(&td->ts_cache, &td->start, sizeof(td->start)); if (clear_state) { clear_io_state(td, 0); @@ -2202,7 +2202,7 @@ reap: while (todo) { struct thread_data *map[REAL_MAX_JOBS]; - struct timeval this_start; + struct timespec this_start; int this_jobs = 0, left; struct fork_data *fd; diff --git a/client.c b/client.c index 80096bf..7a986aa 100644 --- a/client.c +++ b/client.c @@ -48,7 +48,7 @@ struct client_ops fio_client_ops = { .client_type = FIO_CLIENT_TYPE_CLI, }; -static struct timeval eta_tv; +static struct timespec eta_ts; static FLIST_HEAD(client_list); static FLIST_HEAD(eta_list); @@ -318,7 +318,7 @@ struct fio_client *fio_client_add_explicit(struct client_ops *ops, client->hostname = strdup(hostname); if (type == Fio_client_socket) - client->is_sock = 1; + client->is_sock = true; else { int ipv6; @@ -728,7 +728,7 @@ static int __fio_client_send_remote_ini(struct fio_client *client, strcpy((char *) pdu->file, filename); pdu->client_type = cpu_to_le16((uint16_t) client->type); - client->sent_job = 1; + client->sent_job = true; ret = fio_net_send_cmd(client->fd, FIO_NET_CMD_LOAD_FILE, pdu, p_size,NULL, NULL); free(pdu); return ret; @@ -781,7 +781,7 @@ static int __fio_client_send_local_ini(struct fio_client *client, pdu->buf_len = __cpu_to_le32(sb.st_size); pdu->client_type = cpu_to_le32(client->type); - client->sent_job = 1; + client->sent_job = true; ret = fio_net_send_cmd(client->fd, FIO_NET_CMD_JOB, pdu, p_size, NULL, NULL); free(pdu); close(fd); @@ -799,7 +799,7 @@ int fio_client_send_ini(struct fio_client *client, const char *filename, ret = __fio_client_send_remote_ini(client, filename); if (!ret) - client->sent_job = 1; + client->sent_job = true; return ret; } @@ -908,6 +908,8 @@ static void convert_ts(struct thread_stat *dst, struct thread_stat *src) dst->io_u_complete[i] = le32_to_cpu(src->io_u_complete[i]); } + for (i = 0; i < FIO_IO_U_LAT_N_NR; i++) + dst->io_u_lat_n[i] = le32_to_cpu(src->io_u_lat_n[i]); for (i = 0; i < FIO_IO_U_LAT_U_NR; i++) dst->io_u_lat_u[i] = le32_to_cpu(src->io_u_lat_u[i]); for (i = 0; i < FIO_IO_U_LAT_M_NR; i++) @@ -1001,7 +1003,7 @@ static void handle_ts(struct fio_client *client, struct fio_net_cmd *cmd) opt_list = &client->opt_lists[p->ts.thread_number - 1]; tsobj = show_thread_status(&p->ts, &p->rs, opt_list, NULL); - client->did_stat = 1; + client->did_stat = true; if (tsobj) { json_object_add_client_info(tsobj, client); json_array_add_value_object(clients_array, tsobj); @@ -1123,7 +1125,7 @@ static void handle_du(struct fio_client *client, struct fio_net_cmd *cmd) struct cmd_du_pdu *du = (struct cmd_du_pdu *) cmd->payload; if (!client->disk_stats_shown) { - client->disk_stats_shown = 1; + client->disk_stats_shown = true; log_info("\nDisk stats (read/write):\n"); } @@ -1869,7 +1871,7 @@ static int handle_cmd_timeout(struct fio_client *client, } static int client_check_cmd_timeout(struct fio_client *client, - struct timeval *now) + struct timespec *now) { struct fio_net_cmd_reply *reply; struct flist_head *entry, *tmp; @@ -1878,7 +1880,7 @@ static int client_check_cmd_timeout(struct fio_client *client, flist_for_each_safe(entry, tmp, &client->cmd_list) { reply = flist_entry(entry, struct fio_net_cmd_reply, list); - if (mtime_since(&reply->tv, now) < FIO_NET_CLIENT_TIMEOUT) + if (mtime_since(&reply->ts, now) < FIO_NET_CLIENT_TIMEOUT) continue; if (!handle_cmd_timeout(client, reply)) @@ -1896,10 +1898,10 @@ static int fio_check_clients_timed_out(void) { struct fio_client *client; struct flist_head *entry, *tmp; - struct timeval tv; + struct timespec ts; int ret = 0; - fio_gettime(&tv, NULL); + fio_gettime(&ts, NULL); flist_for_each_safe(entry, tmp, &client_list) { client = flist_entry(entry, struct fio_client, list); @@ -1907,7 +1909,7 @@ static int fio_check_clients_timed_out(void) if (flist_empty(&client->cmd_list)) continue; - if (!client_check_cmd_timeout(client, &tv)) + if (!client_check_cmd_timeout(client, &ts)) continue; if (client->ops->timed_out) @@ -1928,7 +1930,7 @@ int fio_handle_clients(struct client_ops *ops) struct pollfd *pfds; int i, ret = 0, retval = 0; - fio_gettime(&eta_tv, NULL); + fio_gettime(&eta_ts, NULL); pfds = malloc(nr_clients * sizeof(struct pollfd)); @@ -1960,13 +1962,13 @@ int fio_handle_clients(struct client_ops *ops) assert(i == nr_clients); do { - struct timeval tv; + struct timespec ts; int timeout; - fio_gettime(&tv, NULL); - if (mtime_since(&eta_tv, &tv) >= 900) { + fio_gettime(&ts, NULL); + if (mtime_since(&eta_ts, &ts) >= 900) { request_client_etas(ops); - memcpy(&eta_tv, &tv, sizeof(tv)); + memcpy(&eta_ts, &ts, sizeof(ts)); if (fio_check_clients_timed_out()) break; diff --git a/client.h b/client.h index fc9c196..394b685 100644 --- a/client.h +++ b/client.h @@ -6,6 +6,7 @@ #include <netinet/in.h> #include <arpa/inet.h> +#include "lib/types.h" #include "stat.h" struct fio_net_cmd; @@ -45,16 +46,16 @@ struct fio_client { int state; - int skip_newline; - int is_sock; - int disk_stats_shown; + bool skip_newline; + bool is_sock; + bool disk_stats_shown; unsigned int jobs; unsigned int nr_stat; int error; int signal; int ipv6; - int sent_job; - int did_stat; + bool sent_job; + bool did_stat; uint32_t type; uint32_t thread_number; diff --git a/crc/crc32c-arm64.c b/crc/crc32c-arm64.c index c3f42c7..08177ba 100644 --- a/crc/crc32c-arm64.c +++ b/crc/crc32c-arm64.c @@ -19,7 +19,7 @@ #define HWCAP_CRC32 (1 << 7) #endif /* HWCAP_CRC32 */ -int crc32c_arm64_available = 0; +bool crc32c_arm64_available = false; #ifdef ARCH_HAVE_ARM64_CRC_CRYPTO @@ -27,7 +27,7 @@ int crc32c_arm64_available = 0; #include <arm_acle.h> #include <arm_neon.h> -static int crc32c_probed; +static bool crc32c_probed; /* * Function to calculate reflected crc with PMULL Instruction @@ -106,9 +106,8 @@ void crc32c_arm64_probe(void) if (!crc32c_probed) { hwcap = getauxval(AT_HWCAP); - if (hwcap & HWCAP_CRC32) - crc32c_arm64_available = 1; - crc32c_probed = 1; + crc32c_arm64_available = (hwcap & HWCAP_CRC32) != 0; + crc32c_probed = true; } } diff --git a/crc/crc32c-intel.c b/crc/crc32c-intel.c index 0b0f193..05a087d 100644 --- a/crc/crc32c-intel.c +++ b/crc/crc32c-intel.c @@ -18,7 +18,7 @@ * Volume 2A: Instruction Set Reference, A-M */ -int crc32c_intel_available = 0; +bool crc32c_intel_available = false; #ifdef ARCH_HAVE_SSE4_2 @@ -30,7 +30,7 @@ int crc32c_intel_available = 0; #define SCALE_F 4 #endif -static int crc32c_probed; +static bool crc32c_probed; static uint32_t crc32c_intel_le_hw_byte(uint32_t crc, unsigned char const *data, unsigned long length) @@ -87,7 +87,7 @@ void crc32c_intel_probe(void) do_cpuid(&eax, &ebx, &ecx, &edx); crc32c_intel_available = (ecx & (1 << 20)) != 0; - crc32c_probed = 1; + crc32c_probed = true; } } diff --git a/crc/crc32c.h b/crc/crc32c.h index 5d66407..d513f3a 100644 --- a/crc/crc32c.h +++ b/crc/crc32c.h @@ -19,10 +19,11 @@ #define CRC32C_H #include "../arch/arch.h" +#include "../lib/types.h" extern uint32_t crc32c_sw(unsigned char const *, unsigned long); -extern int crc32c_arm64_available; -extern int crc32c_intel_available; +extern bool crc32c_arm64_available; +extern bool crc32c_intel_available; #ifdef ARCH_HAVE_ARM64_CRC_CRYPTO extern uint32_t crc32c_arm64(unsigned char const *, unsigned long); diff --git a/crc/test.c b/crc/test.c index 368229e..b119872 100644 --- a/crc/test.c +++ b/crc/test.c @@ -392,7 +392,7 @@ int fio_crctest(const char *type) fill_random_buf(&state, buf, CHUNK); for (i = 0; t[i].name; i++) { - struct timeval tv; + struct timespec ts; double mb_sec; uint64_t usec; char pre[3]; @@ -409,9 +409,9 @@ int fio_crctest(const char *type) t[i].fn(&t[i], buf, CHUNK); } - fio_gettime(&tv, NULL); + fio_gettime(&ts, NULL); t[i].fn(&t[i], buf, CHUNK); - usec = utime_since_now(&tv); + usec = utime_since_now(&ts); if (usec) { mb_sec = (double) mb / (double) usec; diff --git a/diskutil.c b/diskutil.c index 9767ea2..4fe554f 100644 --- a/diskutil.c +++ b/diskutil.c @@ -84,7 +84,7 @@ static int get_io_ticks(struct disk_util *du, struct disk_util_stat *dus) static void update_io_tick_disk(struct disk_util *du) { struct disk_util_stat __dus, *dus, *ldus; - struct timeval t; + struct timespec t; if (!du->users) return; diff --git a/diskutil.h b/diskutil.h index f773066..91b4202 100644 --- a/diskutil.h +++ b/diskutil.h @@ -64,7 +64,7 @@ struct disk_util { */ struct flist_head slaves; - struct timeval time; + struct timespec time; struct fio_mutex *lock; unsigned long users; diff --git a/engines/guasi.c b/engines/guasi.c index eb12c89..9644ee5 100644 --- a/engines/guasi.c +++ b/engines/guasi.c @@ -132,7 +132,7 @@ static void fio_guasi_queued(struct thread_data *td, struct io_u **io_us, int nr { int i; struct io_u *io_u; - struct timeval now; + struct timespec now; if (!fio_fill_issue_time(td)) return; diff --git a/engines/libaio.c b/engines/libaio.c index e15c519..e0d7cbb 100644 --- a/engines/libaio.c +++ b/engines/libaio.c @@ -220,7 +220,7 @@ static int fio_libaio_queue(struct thread_data *td, struct io_u *io_u) static void fio_libaio_queued(struct thread_data *td, struct io_u **io_us, unsigned int nr) { - struct timeval now; + struct timespec now; unsigned int i; if (!fio_fill_issue_time(td)) @@ -241,7 +241,7 @@ static int fio_libaio_commit(struct thread_data *td) struct libaio_data *ld = td->io_ops_data; struct iocb **iocbs; struct io_u **io_us; - struct timeval tv; + struct timespec ts; int ret, wait_start = 0; if (!ld->queued) @@ -282,9 +282,9 @@ static int fio_libaio_commit(struct thread_data *td) break; } if (!wait_start) { - fio_gettime(&tv, NULL); + fio_gettime(&ts, NULL); wait_start = 1; - } else if (mtime_since_now(&tv) > 30000) { + } else if (mtime_since_now(&ts) > 30000) { log_err("fio: aio appears to be stalled, giving up\n"); break; } diff --git a/engines/rdma.c b/engines/rdma.c index 10e60dc..8d31ff3 100644 --- a/engines/rdma.c +++ b/engines/rdma.c @@ -802,7 +802,7 @@ static void fio_rdmaio_queued(struct thread_data *td, struct io_u **io_us, unsigned int nr) { struct rdmaio_data *rd = td->io_ops_data; - struct timeval now; + struct timespec now; unsigned int i; if (!fio_fill_issue_time(td)) diff --git a/eta.c b/eta.c index adf7f94..baaa681 100644 --- a/eta.c +++ b/eta.c @@ -358,12 +358,12 @@ bool calc_thread_status(struct jobs_eta *je, int force) 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]; - struct timeval now; + struct timespec now; static unsigned long long rate_io_bytes[DDIR_RWDIR_CNT]; static unsigned long long disp_io_bytes[DDIR_RWDIR_CNT]; static unsigned long long disp_io_iops[DDIR_RWDIR_CNT]; - static struct timeval rate_prev_time, disp_prev_time; + static struct timespec rate_prev_time, disp_prev_time; if (!force) { if (!(output_format & FIO_OUTPUT_NORMAL) && @@ -511,7 +511,7 @@ bool calc_thread_status(struct jobs_eta *je, int force) void display_thread_status(struct jobs_eta *je) { - static struct timeval disp_eta_new_line; + static struct timespec disp_eta_new_line; static int eta_new_line_init, eta_new_line_pending; static int linelen_last; static int eta_good; diff --git a/fio.h b/fio.h index 6c06a0c..d5d6bfe 100644 --- a/fio.h +++ b/fio.h @@ -165,10 +165,10 @@ struct thread_data { struct thread_data *parent; uint64_t stat_io_bytes[DDIR_RWDIR_CNT]; - struct timeval bw_sample_time; + struct timespec bw_sample_time; uint64_t stat_io_blocks[DDIR_RWDIR_CNT]; - struct timeval iops_sample_time; + struct timespec iops_sample_time; volatile int update_rusage; struct fio_mutex *rusage_sem; @@ -287,7 +287,7 @@ struct thread_data { unsigned long rate_bytes[DDIR_RWDIR_CNT]; unsigned long rate_blocks[DDIR_RWDIR_CNT]; unsigned long long rate_io_issue_bytes[DDIR_RWDIR_CNT]; - struct timeval lastrate[DDIR_RWDIR_CNT]; + struct timespec lastrate[DDIR_RWDIR_CNT]; int64_t last_usec[DDIR_RWDIR_CNT]; struct frand_state poisson_state[DDIR_RWDIR_CNT]; @@ -323,21 +323,21 @@ struct thread_data { */ struct frand_state random_state; - struct timeval start; /* start of this loop */ - struct timeval epoch; /* time job was started */ + struct timespec start; /* start of this loop */ + struct timespec epoch; /* time job was started */ unsigned long long unix_epoch; /* Time job was started, unix epoch based. */ - struct timeval last_issue; + struct timespec last_issue; long time_offset; - struct timeval tv_cache; - struct timeval terminate_time; - unsigned int tv_cache_nr; - unsigned int tv_cache_mask; + struct timespec ts_cache; + struct timespec terminate_time; + unsigned int ts_cache_nr; + unsigned int ts_cache_mask; unsigned int ramp_time_over; /* * Time since last latency_window was started */ - struct timeval latency_ts; + struct timespec latency_ts; unsigned int latency_qd; unsigned int latency_qd_high; unsigned int latency_qd_low; @@ -642,7 +642,7 @@ extern void reset_all_stats(struct thread_data *); extern int io_queue_event(struct thread_data *td, struct io_u *io_u, int *ret, enum fio_ddir ddir, uint64_t *bytes_issued, int from_verify, - struct timeval *comp_time); + struct timespec *comp_time); /* * Latency target helpers diff --git a/fio_time.h b/fio_time.h index b49cc82..f4eac79 100644 --- a/fio_time.h +++ b/fio_time.h @@ -4,22 +4,24 @@ #include "lib/types.h" struct thread_data; -extern uint64_t utime_since(const struct timeval *,const struct timeval *); -extern uint64_t utime_since_now(const struct timeval *); -extern uint64_t mtime_since(const struct timeval *, const struct timeval *); -extern uint64_t mtime_since_now(const struct timeval *); -extern uint64_t time_since_now(const struct timeval *); +extern uint64_t ntime_since(const struct timespec *, const struct timespec *); +extern uint64_t utime_since(const struct timespec *, const struct timespec *); +extern uint64_t utime_since_now(const struct timespec *); +extern uint64_t mtime_since(const struct timespec *, const struct timespec *); +extern uint64_t mtime_since_now(const struct timespec *); +extern uint64_t mtime_since_tv(const struct timeval *, const struct timeval *); +extern uint64_t time_since_now(const struct timespec *); extern uint64_t time_since_genesis(void); extern uint64_t mtime_since_genesis(void); extern uint64_t utime_since_genesis(void); extern uint64_t usec_spin(unsigned int); extern uint64_t usec_sleep(struct thread_data *, unsigned long); -extern void fill_start_time(struct timeval *); +extern void fill_start_time(struct timespec *); extern void set_genesis_time(void); extern bool ramp_time_over(struct thread_data *); extern bool in_ramp_time(struct thread_data *); extern void fio_time_init(void); -extern void timeval_add_msec(struct timeval *, unsigned int); +extern void timespec_add_msec(struct timespec *, unsigned int); extern void set_epoch_time(struct thread_data *, int); #endif diff --git a/gclient.c b/gclient.c index 928a1b7..4eb99a0 100644 --- a/gclient.c +++ b/gclient.c @@ -930,8 +930,10 @@ static gint on_config_lat_drawing_area(GtkWidget *w, GdkEventConfigure *event, static void gfio_show_latency_buckets(struct gfio_client *gc, GtkWidget *vbox, struct thread_stat *ts) { - double io_u_lat[FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR]; - const char *ranges[] = { "2us", "4us", "10us", "20us", "50us", "100us", + double io_u_lat[FIO_IO_U_LAT_N_NR + FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR]; + const char *ranges[] = { "2ns", "4ns", "10ns", "20ns", "50ns", "100ns", + "250ns", "500ns", "750ns", "1000ns", "2us", + "4us", "10us", "20us", "50us", "100us", "250us", "500us", "750us", "1ms", "2ms", "4ms", "10ms", "20ms", "50ms", "100ms", "250ms", "500ms", "750ms", "1s", "2s", ">= 2s" }; @@ -940,8 +942,9 @@ static void gfio_show_latency_buckets(struct gfio_client *gc, GtkWidget *vbox, GtkWidget *frame, *tree_view, *hbox, *completion_vbox, *drawing_area; struct gui_entry *ge = gc->ge; - stat_calc_lat_u(ts, io_u_lat); - stat_calc_lat_m(ts, &io_u_lat[FIO_IO_U_LAT_U_NR]); + stat_calc_lat_n(ts, io_u_lat); + stat_calc_lat_u(ts, &io_u_lat[FIO_IO_U_LAT_N_NR]); + stat_calc_lat_m(ts, &io_u_lat[FIO_IO_U_LAT_N_NR + FIO_IO_U_LAT_U_NR]); /* * Found out which first bucket has entries, and which last bucket @@ -983,16 +986,18 @@ static void gfio_show_latency_buckets(struct gfio_client *gc, GtkWidget *vbox, gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, TRUE, 3); } -static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min, - unsigned long max, double mean, double dev) +static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long long min, + unsigned long long max, double mean, double dev) { - const char *base = "(usec)"; + const char *base = "(nsec)"; GtkWidget *hbox, *label, *frame; char *minp, *maxp; char tmp[64]; - if (usec_to_msec(&min, &max, &mean, &dev)) + if (nsec_to_msec(&min, &max, &mean, &dev)) base = "(msec)"; + else if (nsec_to_usec(&min, &max, &mean, &dev)) + base = "(usec)"; minp = num2str(min, 6, 1, 0, N2S_NONE); maxp = num2str(max, 6, 1, 0, N2S_NONE); @@ -1019,7 +1024,7 @@ static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min, free(maxp); } -static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals, +static GtkWidget *gfio_output_clat_percentiles(unsigned long long *ovals, fio_fp64_t *plist, unsigned int len, const char *base, @@ -1030,10 +1035,10 @@ static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals, GtkTreeSelection *selection; GtkListStore *model; GtkTreeIter iter; - int i; + int i, j; for (i = 0; i < len; i++) - types[i] = G_TYPE_INT; + types[i] = G_TYPE_ULONG; model = gtk_list_store_newv(len, types); @@ -1056,15 +1061,15 @@ static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals, gtk_list_store_append(model, &iter); for (i = 0; i < len; i++) { - if (scale) + for (j = 0; j < scale; j++) ovals[i] = (ovals[i] + 999) / 1000; - gtk_list_store_set(model, &iter, i, ovals[i], -1); + gtk_list_store_set(model, &iter, i, (unsigned long) ovals[i], -1); } return tree_view; } -static struct graph *setup_clat_graph(char *title, unsigned int *ovals, +static struct graph *setup_clat_graph(char *title, unsigned long long *ovals, fio_fp64_t *plist, unsigned int len, double xdim, double ydim) @@ -1096,7 +1101,8 @@ static void gfio_show_clat_percentiles(struct gfio_client *gc, unsigned int *io_u_plat = ts->io_u_plat[ddir]; unsigned long nr = ts->clat_stat[ddir].samples; fio_fp64_t *plist = ts->percentile_list; - unsigned int *ovals, len, minv, maxv, scale_down; + unsigned int len, scale_down; + unsigned long long *ovals, minv, maxv; const char *base; GtkWidget *tree_view, *frame, *hbox, *drawing_area, *completion_vbox; struct gui_entry *ge = gc->ge; @@ -1107,16 +1113,19 @@ static void gfio_show_clat_percentiles(struct gfio_client *gc, goto out; /* - * We default to usecs, but if the value range is such that we - * should scale down to msecs, do that. + * We default to nsecs, but if the value range is such that we + * should scale down to usecs or msecs, do that. */ - if (minv > 2000 && maxv > 99999) { - scale_down = 1; + if (minv > 2000000 && maxv > 99999999ULL) { + scale_down = 2; base = "msec"; - } else { - scale_down = 0; + } else if (minv > 2000 && maxv > 99999) { + scale_down = 1; base = "usec"; - } + } else { + scale_down = 0; + base = "nsec"; + } sprintf(tmp, "Completion percentiles (%s)", base); tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down); @@ -1152,7 +1161,8 @@ static void gfio_show_ddir_status(struct gfio_client *gc, GtkWidget *mbox, { const char *ddir_label[3] = { "Read", "Write", "Trim" }; GtkWidget *frame, *label, *box, *vbox, *main_vbox; - unsigned long min[3], max[3], runt; + unsigned long long min[3], max[3]; + unsigned long runt; unsigned long long bw, iops; unsigned int flags = 0; double mean[3], dev[3]; diff --git a/gettime-thread.c b/gettime-thread.c index 19541b4..cbb81dc 100644 --- a/gettime-thread.c +++ b/gettime-thread.c @@ -6,30 +6,30 @@ #include "fio.h" #include "smalloc.h" -struct timeval *fio_tv = NULL; +struct timespec *fio_ts = NULL; int fio_gtod_offload = 0; static pthread_t gtod_thread; static os_cpu_mask_t fio_gtod_cpumask; void fio_gtod_init(void) { - if (fio_tv) + if (fio_ts) return; - fio_tv = smalloc(sizeof(struct timeval)); - if (!fio_tv) + fio_ts = smalloc(sizeof(*fio_ts)); + if (!fio_ts) log_err("fio: smalloc pool exhausted\n"); } static void fio_gtod_update(void) { - if (fio_tv) { + if (fio_ts) { struct timeval __tv; gettimeofday(&__tv, NULL); - fio_tv->tv_sec = __tv.tv_sec; + fio_ts->tv_sec = __tv.tv_sec; write_barrier(); - fio_tv->tv_usec = __tv.tv_usec; + fio_ts->tv_nsec = __tv.tv_usec * 1000; write_barrier(); } } diff --git a/gettime.c b/gettime.c index 628aad6..5741932 100644 --- a/gettime.c +++ b/gettime.c @@ -15,19 +15,22 @@ #if defined(ARCH_HAVE_CPU_CLOCK) #ifndef ARCH_CPU_CLOCK_CYCLES_PER_USEC -static unsigned long cycles_per_usec; -static unsigned long inv_cycles_per_usec; -static uint64_t max_cycles_for_mult; +static unsigned long cycles_per_msec; +static unsigned long long cycles_start; +static unsigned long long clock_mult; +static unsigned long long max_cycles_mask; +static unsigned long long nsecs_for_max_cycles; +static unsigned int clock_shift; +static unsigned int max_cycles_shift; +#define MAX_CLOCK_SEC 60*60 #endif #ifdef ARCH_CPU_CLOCK_WRAPS -static unsigned long long cycles_start, cycles_wrap; +static unsigned int cycles_wrap; #endif #endif -int tsc_reliable = 0; +bool tsc_reliable = false; struct tv_valid { - uint64_t last_cycles; - int last_tv_valid; int warned; }; #ifdef ARCH_HAVE_CPU_CLOCK @@ -143,31 +146,31 @@ static int fill_clock_gettime(struct timespec *ts) } #endif -static void __fio_gettime(struct timeval *tp) +static void __fio_gettime(struct timespec *tp) { switch (fio_clock_source) { #ifdef CONFIG_GETTIMEOFDAY - case CS_GTOD: - gettimeofday(tp, NULL); + case CS_GTOD: { + struct timeval tv; + gettimeofday(&tv, NULL); + + tp->tv_sec = tv.tv_sec; + tp->tv_nsec = tv.tv_usec * 1000; break; + } #endif #ifdef CONFIG_CLOCK_GETTIME case CS_CGETTIME: { - struct timespec ts; - - if (fill_clock_gettime(&ts) < 0) { + if (fill_clock_gettime(tp) < 0) { log_err("fio: clock_gettime fails\n"); assert(0); } - - tp->tv_sec = ts.tv_sec; - tp->tv_usec = ts.tv_nsec / 1000; break; } #endif #ifdef ARCH_HAVE_CPU_CLOCK case CS_CPUCLOCK: { - uint64_t usecs, t; + uint64_t nsecs, t, multiples; struct tv_valid *tv; #ifdef CONFIG_TLS_THREAD @@ -184,21 +187,17 @@ static void __fio_gettime(struct timeval *tp) log_err("fio: double CPU clock wrap\n"); tv->warned = 1; } - - t -= cycles_start; #endif - tv->last_cycles = t; - tv->last_tv_valid = 1; #ifdef ARCH_CPU_CLOCK_CYCLES_PER_USEC - usecs = t / ARCH_CPU_CLOCK_CYCLES_PER_USEC; + nsecs = t / ARCH_CPU_CLOCK_CYCLES_PER_USEC * 1000; #else - if (t < max_cycles_for_mult) - usecs = (t * inv_cycles_per_usec) / 16777216UL; - else - usecs = t / cycles_per_usec; + t -= cycles_start; + multiples = t >> max_cycles_shift; + nsecs = multiples * nsecs_for_max_cycles; + nsecs += ((t & max_cycles_mask) * clock_mult) >> clock_shift; #endif - tp->tv_sec = usecs / 1000000; - tp->tv_usec = usecs % 1000000; + tp->tv_sec = nsecs / 1000000000ULL; + tp->tv_nsec = nsecs % 1000000000ULL; break; } #endif @@ -209,9 +208,9 @@ static void __fio_gettime(struct timeval *tp) } #ifdef FIO_DEBUG_TIME -void fio_gettime(struct timeval *tp, void *caller) +void fio_gettime(struct timespec *tp, void *caller) #else -void fio_gettime(struct timeval *tp, void fio_unused *caller) +void fio_gettime(struct timespec *tp, void fio_unused *caller) #endif { #ifdef FIO_DEBUG_TIME @@ -227,9 +226,9 @@ void fio_gettime(struct timeval *tp, void fio_unused *caller) } #if defined(ARCH_HAVE_CPU_CLOCK) && !defined(ARCH_CPU_CLOCK_CYCLES_PER_USEC) -static unsigned long get_cycles_per_usec(void) +static unsigned long get_cycles_per_msec(void) { - struct timeval s, e; + struct timespec s, e; uint64_t c_s, c_e; enum fio_cs old_cs = fio_clock_source; uint64_t elapsed; @@ -253,7 +252,7 @@ static unsigned long get_cycles_per_usec(void) } while (1); fio_clock_source = old_cs; - return (c_e - c_s) / elapsed; + return (c_e - c_s) * 1000 / elapsed; } #define NR_TIME_ITERS 50 @@ -262,12 +261,13 @@ static int calibrate_cpu_clock(void) { double delta, mean, S; uint64_t minc, maxc, avg, cycles[NR_TIME_ITERS]; - int i, samples; + int i, samples, sft = 0; + unsigned long long tmp, max_ticks, max_mult; - cycles[0] = get_cycles_per_usec(); + cycles[0] = get_cycles_per_msec(); S = delta = mean = 0.0; for (i = 0; i < NR_TIME_ITERS; i++) { - cycles[i] = get_cycles_per_usec(); + cycles[i] = get_cycles_per_msec(); delta = cycles[i] - mean; if (delta) { mean += delta / (i + 1.0); @@ -304,19 +304,67 @@ static int calibrate_cpu_clock(void) dprint(FD_TIME, "cycles[%d]=%llu\n", i, (unsigned long long) cycles[i]); avg /= samples; + cycles_per_msec = avg; dprint(FD_TIME, "avg: %llu\n", (unsigned long long) avg); dprint(FD_TIME, "min=%llu, max=%llu, mean=%f, S=%f\n", (unsigned long long) minc, (unsigned long long) maxc, mean, S); - cycles_per_usec = avg; - inv_cycles_per_usec = 16777216UL / cycles_per_usec; - max_cycles_for_mult = ~0ULL / inv_cycles_per_usec; - dprint(FD_TIME, "inv_cycles_per_usec=%lu\n", inv_cycles_per_usec); -#ifdef ARCH_CPU_CLOCK_WRAPS + max_ticks = MAX_CLOCK_SEC * cycles_per_msec * 1000ULL; + max_mult = ULLONG_MAX / max_ticks; + dprint(FD_TIME, "\n\nmax_ticks=%llu, __builtin_clzll=%d, " + "max_mult=%llu\n", max_ticks, + __builtin_clzll(max_ticks), max_mult); + + /* + * Find the largest shift count that will produce + * a multiplier that does not exceed max_mult + */ + tmp = max_mult * cycles_per_msec / 1000000; + while (tmp > 1) { + tmp >>= 1; + sft++; + dprint(FD_TIME, "tmp=%llu, sft=%u\n", tmp, sft); + } + + clock_shift = sft; + clock_mult = (1ULL << sft) * 1000000 / cycles_per_msec; + dprint(FD_TIME, "clock_shift=%u, clock_mult=%llu\n", clock_shift, + clock_mult); + + /* + * Find the greatest power of 2 clock ticks that is less than the + * ticks in MAX_CLOCK_SEC_2STAGE + */ + max_cycles_shift = max_cycles_mask = 0; + tmp = MAX_CLOCK_SEC * 1000ULL * cycles_per_msec; + dprint(FD_TIME, "tmp=%llu, max_cycles_shift=%u\n", tmp, + max_cycles_shift); + while (tmp > 1) { + tmp >>= 1; + max_cycles_shift++; + dprint(FD_TIME, "tmp=%llu, max_cycles_shift=%u\n", tmp, max_cycles_shift); + } + /* + * if use use (1ULL << max_cycles_shift) * 1000 / cycles_per_msec + * here we will have a discontinuity every + * (1ULL << max_cycles_shift) cycles + */ + nsecs_for_max_cycles = ((1ULL << max_cycles_shift) * clock_mult) + >> clock_shift; + + /* Use a bitmask to calculate ticks % (1ULL << max_cycles_shift) */ + for (tmp = 0; tmp < max_cycles_shift; tmp++) + max_cycles_mask |= 1ULL << tmp; + + dprint(FD_TIME, "max_cycles_shift=%u, 2^max_cycles_shift=%llu, " + "nsecs_for_max_cycles=%llu, " + "max_cycles_mask=%016llx\n", + max_cycles_shift, (1ULL << max_cycles_shift), + nsecs_for_max_cycles, max_cycles_mask); + cycles_start = get_cpu_clock(); dprint(FD_TIME, "cycles_start=%llu\n", cycles_start); -#endif return 0; } #else @@ -365,7 +413,7 @@ void fio_clock_init(void) fio_clock_source_inited = fio_clock_source; if (calibrate_cpu_clock()) - tsc_reliable = 0; + tsc_reliable = false; /* * If the arch sets tsc_reliable != 0, then it must be good enough @@ -379,12 +427,32 @@ void fio_clock_init(void) log_info("fio: clocksource=cpu may not be reliable\n"); } -uint64_t utime_since(const struct timeval *s, const struct timeval *e) +uint64_t ntime_since(const struct timespec *s, const struct timespec *e) +{ + int64_t sec, nsec; + + sec = e->tv_sec - s->tv_sec; + nsec = e->tv_nsec - s->tv_nsec; + if (sec > 0 && nsec < 0) { + sec--; + nsec += 1000000000LL; + } + + /* + * time warp bug on some kernels? + */ + if (sec < 0 || (sec == 0 && nsec < 0)) + return 0; + + return nsec + (sec * 1000000000LL); +} + +uint64_t utime_since(const struct timespec *s, const struct timespec *e) { int64_t sec, usec; sec = e->tv_sec - s->tv_sec; - usec = e->tv_usec - s->tv_usec; + usec = (e->tv_nsec - s->tv_nsec) / 1000; if (sec > 0 && usec < 0) { sec--; usec += 1000000; @@ -399,9 +467,9 @@ uint64_t utime_since(const struct timeval *s, const struct timeval *e) return usec + (sec * 1000000); } -uint64_t utime_since_now(const struct timeval *s) +uint64_t utime_since_now(const struct timespec *s) { - struct timeval t; + struct timespec t; #ifdef FIO_DEBUG_TIME void *p = __builtin_return_address(0); @@ -413,12 +481,12 @@ uint64_t utime_since_now(const struct timeval *s) return utime_since(s, &t); } -uint64_t mtime_since(const struct timeval *s, const struct timeval *e) +uint64_t mtime_since_tv(const struct timeval *s, const struct timeval *e) { - long sec, usec; + int64_t sec, usec; sec = e->tv_sec - s->tv_sec; - usec = e->tv_usec - s->tv_usec; + usec = (e->tv_usec - s->tv_usec); if (sec > 0 && usec < 0) { sec--; usec += 1000000; @@ -432,9 +500,9 @@ uint64_t mtime_since(const struct timeval *s, const struct timeval *e) return sec + usec; } -uint64_t mtime_since_now(const struct timeval *s) +uint64_t mtime_since_now(const struct timespec *s) { - struct timeval t; + struct timespec t; #ifdef FIO_DEBUG_TIME void *p = __builtin_return_address(0); @@ -446,7 +514,26 @@ uint64_t mtime_since_now(const struct timeval *s) return mtime_since(s, &t); } -uint64_t time_since_now(const struct timeval *s) +uint64_t mtime_since(const struct timespec *s, const struct timespec *e) +{ + int64_t sec, usec; + + sec = e->tv_sec - s->tv_sec; + usec = (e->tv_nsec - s->tv_nsec) / 1000; + if (sec > 0 && usec < 0) { + sec--; + usec += 1000000; + } + + if (sec < 0 || (sec == 0 && usec < 0)) + return 0; + + sec *= 1000; + usec /= 1000; + return sec + usec; +} + +uint64_t time_since_now(const struct timespec *s) { return mtime_since_now(s) / 1000; } diff --git a/gettime.h b/gettime.h index 86d55bd..11e2a7b 100644 --- a/gettime.h +++ b/gettime.h @@ -13,27 +13,27 @@ enum fio_cs { CS_INVAL, }; -extern void fio_gettime(struct timeval *, void *); +extern void fio_gettime(struct timespec *, void *); extern void fio_gtod_init(void); extern void fio_clock_init(void); extern int fio_start_gtod_thread(void); extern int fio_monotonic_clocktest(int debug); extern void fio_local_clock_init(int); -extern struct timeval *fio_tv; +extern struct timespec *fio_ts; -static inline int fio_gettime_offload(struct timeval *tv) +static inline int fio_gettime_offload(struct timespec *ts) { time_t last_sec; - if (!fio_tv) + if (!fio_ts) return 0; do { read_barrier(); - last_sec = tv->tv_sec = fio_tv->tv_sec; - tv->tv_usec = fio_tv->tv_usec; - } while (fio_tv->tv_sec != last_sec); + last_sec = ts->tv_sec = fio_ts->tv_sec; + ts->tv_nsec = fio_ts->tv_nsec; + } while (fio_ts->tv_sec != last_sec); return 1; } diff --git a/helper_thread.c b/helper_thread.c index 47ec728..9c6e0a2 100644 --- a/helper_thread.c +++ b/helper_thread.c @@ -71,45 +71,45 @@ static void *helper_thread_main(void *data) { struct helper_data *hd = data; unsigned int msec_to_next_event, next_log, next_ss = STEADYSTATE_MSEC; - struct timeval tv, last_du, last_ss; + struct timeval tv; + struct timespec ts, last_du, last_ss; int ret = 0; sk_out_assign(hd->sk_out); gettimeofday(&tv, NULL); - memcpy(&last_du, &tv, sizeof(tv)); - memcpy(&last_ss, &tv, sizeof(tv)); + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = tv.tv_usec * 1000; + memcpy(&last_du, &ts, sizeof(ts)); + memcpy(&last_ss, &ts, sizeof(ts)); fio_mutex_up(hd->startup_mutex); msec_to_next_event = DISK_UTIL_MSEC; while (!ret && !hd->exit) { - struct timespec ts; - struct timeval now; uint64_t since_du, since_ss = 0; - timeval_add_msec(&tv, msec_to_next_event); - ts.tv_sec = tv.tv_sec; - ts.tv_nsec = tv.tv_usec * 1000; + timespec_add_msec(&ts, msec_to_next_event); pthread_mutex_lock(&hd->lock); pthread_cond_timedwait(&hd->cond, &hd->lock, &ts); - gettimeofday(&now, NULL); + gettimeofday(&tv, NULL); + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = tv.tv_usec * 1000; if (hd->reset) { - memcpy(&tv, &now, sizeof(tv)); - memcpy(&last_du, &now, sizeof(last_du)); - memcpy(&last_ss, &now, sizeof(last_ss)); + memcpy(&last_du, &ts, sizeof(ts)); + memcpy(&last_ss, &ts, sizeof(ts)); hd->reset = 0; } pthread_mutex_unlock(&hd->lock); - since_du = mtime_since(&last_du, &now); + since_du = mtime_since(&last_du, &ts); if (since_du >= DISK_UTIL_MSEC || DISK_UTIL_MSEC - since_du < 10) { ret = update_io_ticks(); - timeval_add_msec(&last_du, DISK_UTIL_MSEC); + timespec_add_msec(&last_du, DISK_UTIL_MSEC); msec_to_next_event = DISK_UTIL_MSEC; if (since_du >= DISK_UTIL_MSEC) msec_to_next_event -= (since_du - DISK_UTIL_MSEC); @@ -126,10 +126,10 @@ static void *helper_thread_main(void *data) next_log = DISK_UTIL_MSEC; if (steadystate_enabled) { - since_ss = mtime_since(&last_ss, &now); + since_ss = mtime_since(&last_ss, &ts); if (since_ss >= STEADYSTATE_MSEC || STEADYSTATE_MSEC - since_ss < 10) { steadystate_check(); - timeval_add_msec(&last_ss, since_ss); + timespec_add_msec(&last_ss, since_ss); if (since_ss > STEADYSTATE_MSEC) next_ss = STEADYSTATE_MSEC - (since_ss - STEADYSTATE_MSEC); else diff --git a/idletime.c b/idletime.c index 4c00d80..90bc1d9 100644 --- a/idletime.c +++ b/idletime.c @@ -11,7 +11,7 @@ static volatile struct idle_prof_common ipc; static double calibrate_unit(unsigned char *data) { unsigned long t, i, j, k; - struct timeval tps; + struct timespec tps; double tunit = 0.0; for (i = 0; i < CALIBRATE_RUNS; i++) { @@ -183,7 +183,6 @@ static void calibration_stats(void) void fio_idle_prof_init(void) { int i, ret; - struct timeval tp; struct timespec ts; pthread_attr_t tattr; struct idle_prof_thread *ipt; @@ -282,9 +281,8 @@ void fio_idle_prof_init(void) pthread_mutex_lock(&ipt->init_lock); while ((ipt->state != TD_EXITED) && (ipt->state!=TD_INITIALIZED)) { - fio_gettime(&tp, NULL); - ts.tv_sec = tp.tv_sec + 1; - ts.tv_nsec = tp.tv_usec * 1000; + fio_gettime(&ts, NULL); + ts.tv_sec += 1; pthread_cond_timedwait(&ipt->cond, &ipt->init_lock, &ts); } pthread_mutex_unlock(&ipt->init_lock); @@ -325,7 +323,6 @@ void fio_idle_prof_stop(void) { int i; uint64_t runt; - struct timeval tp; struct timespec ts; struct idle_prof_thread *ipt; @@ -343,9 +340,8 @@ void fio_idle_prof_stop(void) pthread_mutex_lock(&ipt->start_lock); while ((ipt->state != TD_EXITED) && (ipt->state!=TD_NOT_CREATED)) { - fio_gettime(&tp, NULL); - ts.tv_sec = tp.tv_sec + 1; - ts.tv_nsec = tp.tv_usec * 1000; + fio_gettime(&ts, NULL); + ts.tv_sec += 1; /* timed wait in case a signal is not received */ pthread_cond_timedwait(&ipt->cond, &ipt->start_lock, &ts); } diff --git a/idletime.h b/idletime.h index 84c1fbb..b8376c2 100644 --- a/idletime.h +++ b/idletime.h @@ -26,8 +26,8 @@ struct idle_prof_thread { pthread_t thread; int cpu; int state; - struct timeval tps; - struct timeval tpe; + struct timespec tps; + struct timespec tpe; double cali_time; /* microseconds to finish a unit work */ double loops; double idleness; diff --git a/io_u.c b/io_u.c index fd63119..375413f 100644 --- a/io_u.c +++ b/io_u.c @@ -20,7 +20,7 @@ struct io_completion_data { int error; /* output */ uint64_t bytes_done[DDIR_RWDIR_CNT]; /* output */ - struct timeval time; /* output */ + struct timespec time; /* output */ }; /* @@ -989,11 +989,52 @@ void io_u_mark_depth(struct thread_data *td, unsigned int nr) td->ts.io_u_map[idx] += nr; } -static void io_u_mark_lat_usec(struct thread_data *td, unsigned long usec) +static void io_u_mark_lat_nsec(struct thread_data *td, unsigned long long nsec) { int idx = 0; - assert(usec < 1000); + assert(nsec < 1000); + + switch (nsec) { + case 750 ... 999: + idx = 9; + break; + case 500 ... 749: + idx = 8; + break; + case 250 ... 499: + idx = 7; + break; + case 100 ... 249: + idx = 6; + break; + case 50 ... 99: + idx = 5; + break; + case 20 ... 49: + idx = 4; + break; + case 10 ... 19: + idx = 3; + break; + case 4 ... 9: + idx = 2; + break; + case 2 ... 3: + idx = 1; + case 0 ... 1: + break; + } + + assert(idx < FIO_IO_U_LAT_N_NR); + td->ts.io_u_lat_n[idx]++; +} + +static void io_u_mark_lat_usec(struct thread_data *td, unsigned long long usec) +{ + int idx = 0; + + assert(usec < 1000 && usec >= 1); switch (usec) { case 750 ... 999: @@ -1030,10 +1071,12 @@ static void io_u_mark_lat_usec(struct thread_data *td, unsigned long usec) td->ts.io_u_lat_u[idx]++; } -static void io_u_mark_lat_msec(struct thread_data *td, unsigned long msec) +static void io_u_mark_lat_msec(struct thread_data *td, unsigned long long msec) { int idx = 0; + assert(msec >= 1); + switch (msec) { default: idx = 11; @@ -1075,12 +1118,14 @@ static void io_u_mark_lat_msec(struct thread_data *td, unsigned long msec) td->ts.io_u_lat_m[idx]++; } -static void io_u_mark_latency(struct thread_data *td, unsigned long usec) +static void io_u_mark_latency(struct thread_data *td, unsigned long long nsec) { - if (usec < 1000) - io_u_mark_lat_usec(td, usec); + if (nsec < 1000) + io_u_mark_lat_nsec(td, nsec); + else if (nsec < 1000000) + io_u_mark_lat_usec(td, nsec / 1000); else - io_u_mark_lat_msec(td, usec / 1000); + io_u_mark_lat_msec(td, nsec / 1000000); } static unsigned int __get_next_fileno_rand(struct thread_data *td) @@ -1572,7 +1617,7 @@ static void small_content_scramble(struct io_u *io_u) * the buffer, given by the product of the usec time * and the actual offset. */ - offset = (io_u->start_time.tv_usec ^ boffset) & 511; + offset = ((io_u->start_time.tv_nsec/1000) ^ boffset) & 511; offset &= ~(sizeof(uint64_t) - 1); if (offset >= 512 - sizeof(uint64_t)) offset -= sizeof(uint64_t); @@ -1729,7 +1774,7 @@ static void account_io_completion(struct thread_data *td, struct io_u *io_u, const enum fio_ddir idx, unsigned int bytes) { const int no_reduce = !gtod_reduce(td); - unsigned long lusec = 0; + unsigned long long llnsec = 0; if (td->parent) td = td->parent; @@ -1738,37 +1783,37 @@ static void account_io_completion(struct thread_data *td, struct io_u *io_u, return; if (no_reduce) - lusec = utime_since(&io_u->issue_time, &icd->time); + llnsec = ntime_since(&io_u->issue_time, &icd->time); if (!td->o.disable_lat) { - unsigned long tusec; + unsigned long long tnsec; - tusec = utime_since(&io_u->start_time, &icd->time); - add_lat_sample(td, idx, tusec, bytes, io_u->offset); + tnsec = ntime_since(&io_u->start_time, &icd->time); + add_lat_sample(td, idx, tnsec, bytes, io_u->offset); if (td->flags & TD_F_PROFILE_OPS) { struct prof_io_ops *ops = &td->prof_io_ops; if (ops->io_u_lat) - icd->error = ops->io_u_lat(td, tusec); + icd->error = ops->io_u_lat(td, tnsec/1000); } - if (td->o.max_latency && tusec > td->o.max_latency) - lat_fatal(td, icd, tusec, td->o.max_latency); - if (td->o.latency_target && tusec > td->o.latency_target) { + if (td->o.max_latency && tnsec/1000 > td->o.max_latency) + lat_fatal(td, icd, tnsec/1000, td->o.max_latency); + if (td->o.latency_target && tnsec/1000 > td->o.latency_target) { if (lat_target_failed(td)) - lat_fatal(td, icd, tusec, td->o.latency_target); + lat_fatal(td, icd, tnsec/1000, td->o.latency_target); } } if (ddir_rw(idx)) { if (!td->o.disable_clat) { - add_clat_sample(td, idx, lusec, bytes, io_u->offset); - io_u_mark_latency(td, lusec); + add_clat_sample(td, idx, llnsec, bytes, io_u->offset); + io_u_mark_latency(td, llnsec); } if (!td->o.disable_bw && per_unit_log(td->bw_log)) - add_bw_sample(td, io_u, bytes, lusec); + add_bw_sample(td, io_u, bytes, llnsec); if (no_reduce && per_unit_log(td->iops_log)) add_iops_sample(td, io_u, bytes); @@ -2000,7 +2045,7 @@ void io_u_queued(struct thread_data *td, struct io_u *io_u) if (!td->o.disable_slat && ramp_time_over(td) && td->o.stats) { unsigned long slat_time; - slat_time = utime_since(&io_u->start_time, &io_u->issue_time); + slat_time = ntime_since(&io_u->start_time, &io_u->issue_time); if (td->parent) td = td->parent; diff --git a/io_u.h b/io_u.h index 155344d..b228e2e 100644 --- a/io_u.h +++ b/io_u.h @@ -31,8 +31,8 @@ enum { * The io unit */ struct io_u { - struct timeval start_time; - struct timeval issue_time; + struct timespec start_time; + struct timespec issue_time; struct fio_file *file; unsigned int flags; diff --git a/ioengines.c b/ioengines.c index 2d55065..abbaa9a 100644 --- a/ioengines.c +++ b/ioengines.c @@ -281,7 +281,7 @@ int td_io_queue(struct thread_data *td, struct io_u *io_u) */ if (td->o.read_iolog_file) memcpy(&td->last_issue, &io_u->issue_time, - sizeof(struct timeval)); + sizeof(io_u->issue_time)); } if (ddir_rw(ddir)) { @@ -356,7 +356,7 @@ int td_io_queue(struct thread_data *td, struct io_u *io_u) */ if (td->o.read_iolog_file) memcpy(&td->last_issue, &io_u->issue_time, - sizeof(struct timeval)); + sizeof(io_u->issue_time)); } return ret; diff --git a/iolog.c b/iolog.c index 01b82e8..27c14eb 100644 --- a/iolog.c +++ b/iolog.c @@ -65,7 +65,7 @@ static void iolog_delay(struct thread_data *td, unsigned long delay) { uint64_t usec = utime_since_now(&td->last_issue); uint64_t this_delay; - struct timeval tv; + struct timespec ts; if (delay < td->time_offset) { td->time_offset = 0; @@ -78,7 +78,7 @@ static void iolog_delay(struct thread_data *td, unsigned long delay) delay -= usec; - fio_gettime(&tv, NULL); + fio_gettime(&ts, NULL); while (delay && !td->terminate) { this_delay = delay; if (this_delay > 500000) @@ -88,7 +88,7 @@ static void iolog_delay(struct thread_data *td, unsigned long delay) delay -= this_delay; } - usec = utime_since_now(&tv); + usec = utime_since_now(&ts); if (usec > delay) td->time_offset = usec - delay; else @@ -643,6 +643,7 @@ void setup_log(struct io_log **log, struct log_params *p, l->log_gz = 0; else if (l->log_gz || l->log_gz_store) { mutex_init_pshared(&l->chunk_lock); + mutex_init_pshared(&l->deferred_free_lock); p->td->flags |= TD_F_COMPRESS_LOG; } @@ -1144,6 +1145,42 @@ size_t log_chunk_sizes(struct io_log *log) #ifdef CONFIG_ZLIB +static bool warned_on_drop; + +static void iolog_put_deferred(struct io_log *log, void *ptr) +{ + if (!ptr) + return; + + pthread_mutex_lock(&log->deferred_free_lock); + if (log->deferred < IOLOG_MAX_DEFER) { + log->deferred_items[log->deferred] = ptr; + log->deferred++; + } else if (!warned_on_drop) { + log_err("fio: had to drop log entry free\n"); + warned_on_drop = true; + } + pthread_mutex_unlock(&log->deferred_free_lock); +} + +static void iolog_free_deferred(struct io_log *log) +{ + int i; + + if (!log->deferred) + return; + + pthread_mutex_lock(&log->deferred_free_lock); + + for (i = 0; i < log->deferred; i++) { + free(log->deferred_items[i]); + log->deferred_items[i] = NULL; + } + + log->deferred = 0; + pthread_mutex_unlock(&log->deferred_free_lock); +} + static int gz_work(struct iolog_flush_data *data) { struct iolog_compress *c = NULL; @@ -1236,7 +1273,7 @@ static int gz_work(struct iolog_flush_data *data) if (ret != Z_OK) log_err("fio: deflateEnd %d\n", ret); - free(data->samples); + iolog_put_deferred(data->log, data->samples); if (!flist_empty(&list)) { pthread_mutex_lock(&data->log->chunk_lock); @@ -1247,7 +1284,7 @@ static int gz_work(struct iolog_flush_data *data) ret = 0; done: if (data->free) - free(data); + sfree(data); return ret; err: while (!flist_empty(&list)) { @@ -1348,7 +1385,7 @@ int iolog_cur_flush(struct io_log *log, struct io_logs *cur_log) { struct iolog_flush_data *data; - data = malloc(sizeof(*data)); + data = smalloc(sizeof(*data)); if (!data) return 1; @@ -1362,6 +1399,9 @@ int iolog_cur_flush(struct io_log *log, struct io_logs *cur_log) cur_log->log = NULL; workqueue_enqueue(&log->td->log_compress_wq, &data->work); + + iolog_free_deferred(log); + return 0; } #else diff --git a/iolog.h b/iolog.h index 0733ad3..d157fa2 100644 --- a/iolog.h +++ b/iolog.h @@ -131,6 +131,11 @@ struct io_log { pthread_mutex_t chunk_lock; unsigned int chunk_seq; struct flist_head chunk_list; + + pthread_mutex_t deferred_free_lock; +#define IOLOG_MAX_DEFER 8 + void *deferred_items[IOLOG_MAX_DEFER]; + unsigned int deferred; }; /* @@ -259,7 +264,7 @@ struct log_params { static inline bool per_unit_log(struct io_log *log) { - return log && !log->avg_msec; + return log && (!log->avg_msec || log->log_gz || log->log_gz_store); } static inline bool inline_log(struct io_log *log) diff --git a/lib/seqlock.h b/lib/seqlock.h index 1ac1eb6..762b6ec 100644 --- a/lib/seqlock.h +++ b/lib/seqlock.h @@ -1,6 +1,7 @@ #ifndef FIO_SEQLOCK_H #define FIO_SEQLOCK_H +#include "types.h" #include "../arch/arch.h" struct seqlock { diff --git a/libfio.c b/libfio.c index da22456..14ddc4d 100644 --- a/libfio.c +++ b/libfio.c @@ -144,10 +144,10 @@ void reset_all_stats(struct thread_data *td) } set_epoch_time(td, td->o.log_unix_epoch); - memcpy(&td->start, &td->epoch, sizeof(struct timeval)); - memcpy(&td->iops_sample_time, &td->epoch, sizeof(struct timeval)); - memcpy(&td->bw_sample_time, &td->epoch, sizeof(struct timeval)); - memcpy(&td->ss.prev_time, &td->epoch, sizeof(struct timeval)); + memcpy(&td->start, &td->epoch, sizeof(td->epoch)); + memcpy(&td->iops_sample_time, &td->epoch, sizeof(td->epoch)); + memcpy(&td->bw_sample_time, &td->epoch, sizeof(td->epoch)); + memcpy(&td->ss.prev_time, &td->epoch, sizeof(td->epoch)); lat_target_reset(td); clear_rusage_stat(td); diff --git a/mutex.c b/mutex.c index d8c4825..9fab715 100644 --- a/mutex.c +++ b/mutex.c @@ -141,11 +141,15 @@ struct fio_mutex *fio_mutex_init(int value) return NULL; } -static bool mutex_timed_out(struct timeval *t, unsigned int msecs) +static bool mutex_timed_out(struct timespec *t, unsigned int msecs) { - struct timeval now; + struct timeval tv; + struct timespec now; + + gettimeofday(&tv, NULL); + now.tv_sec = tv.tv_sec; + now.tv_nsec = tv.tv_usec * 1000; - gettimeofday(&now, NULL); return mtime_since(t, &now) >= msecs; } @@ -177,7 +181,7 @@ int fio_mutex_down_timeout(struct fio_mutex *mutex, unsigned int msecs) * way too early, double check. */ ret = pthread_cond_timedwait(&mutex->cond, &mutex->lock, &t); - if (ret == ETIMEDOUT && !mutex_timed_out(&tv_s, msecs)) + if (ret == ETIMEDOUT && !mutex_timed_out(&t, msecs)) ret = 0; } mutex->waiters--; diff --git a/options.c b/options.c index a8fdde4..7431ed8 100644 --- a/options.c +++ b/options.c @@ -1381,7 +1381,7 @@ static int str_gtod_reduce_cb(void *data, int *il) td->o.disable_bw = !!val; td->o.clat_percentiles = !val; if (val) - td->tv_cache_mask = 63; + td->ts_cache_mask = 63; return 0; } diff --git a/os/windows/posix.c b/os/windows/posix.c index eae8c86..488d0ed 100755 --- a/os/windows/posix.c +++ b/os/windows/posix.c @@ -25,8 +25,8 @@ #include "../os-windows.h" #include "../../lib/hweight.h" -extern unsigned long mtime_since_now(struct timeval *); -extern void fio_gettime(struct timeval *, void *); +extern unsigned long mtime_since_now(struct timespec *); +extern void fio_gettime(struct timespec *, void *); /* These aren't defined in the MinGW headers */ HRESULT WINAPI StringCchCopyA( @@ -852,7 +852,7 @@ int poll(struct pollfd fds[], nfds_t nfds, int timeout) int nanosleep(const struct timespec *rqtp, struct timespec *rmtp) { - struct timeval tv; + struct timespec tv; DWORD ms_remaining; DWORD ms_total = (rqtp->tv_sec * 1000) + (rqtp->tv_nsec / 1000000.0); diff --git a/profiles/act.c b/profiles/act.c index 643f8a8..59e5005 100644 --- a/profiles/act.c +++ b/profiles/act.c @@ -47,7 +47,7 @@ struct act_run_data { static struct act_run_data *act_run_data; struct act_prof_data { - struct timeval sample_tv; + struct timespec sample_tv; struct act_slice *slices; unsigned int cur_slice; unsigned int nr_slices; diff --git a/server.c b/server.c index 8a5e75d..8b36e38 100644 --- a/server.c +++ b/server.c @@ -438,7 +438,7 @@ static uint64_t alloc_reply(uint64_t tag, uint16_t opcode) reply = calloc(1, sizeof(*reply)); INIT_FLIST_HEAD(&reply->list); - fio_gettime(&reply->tv, NULL); + fio_gettime(&reply->ts, NULL); reply->saved_tag = tag; reply->opcode = opcode; @@ -1497,6 +1497,8 @@ void fio_server_send_ts(struct thread_stat *ts, struct group_run_stats *rs) p.ts.io_u_complete[i] = cpu_to_le32(ts->io_u_complete[i]); } + for (i = 0; i < FIO_IO_U_LAT_N_NR; i++) + p.ts.io_u_lat_n[i] = cpu_to_le32(ts->io_u_lat_n[i]); for (i = 0; i < FIO_IO_U_LAT_U_NR; i++) p.ts.io_u_lat_u[i] = cpu_to_le32(ts->io_u_lat_u[i]); for (i = 0; i < FIO_IO_U_LAT_M_NR; i++) @@ -2268,7 +2270,7 @@ int fio_server_parse_host(const char *host, int ipv6, struct in_addr *inp, * For local domain sockets: * *ptr is the filename, *is_sock is 1. */ -int fio_server_parse_string(const char *str, char **ptr, int *is_sock, +int fio_server_parse_string(const char *str, char **ptr, bool *is_sock, int *port, struct in_addr *inp, struct in6_addr *inp6, int *ipv6) { @@ -2277,13 +2279,13 @@ int fio_server_parse_string(const char *str, char **ptr, int *is_sock, int lport = 0; *ptr = NULL; - *is_sock = 0; + *is_sock = false; *port = fio_net_port; *ipv6 = 0; if (!strncmp(str, "sock:", 5)) { *ptr = strdup(str + 5); - *is_sock = 1; + *is_sock = true; return 0; } @@ -2362,7 +2364,8 @@ int fio_server_parse_string(const char *str, char **ptr, int *is_sock, static int fio_handle_server_arg(void) { int port = fio_net_port; - int is_sock, ret = 0; + bool is_sock; + int ret = 0; saddr_in.sin_addr.s_addr = htonl(INADDR_ANY); diff --git a/server.h b/server.h index f002f3b..7f235f3 100644 --- a/server.h +++ b/server.h @@ -43,13 +43,13 @@ struct fio_net_cmd { struct fio_net_cmd_reply { struct flist_head list; - struct timeval tv; + struct timespec ts; uint64_t saved_tag; uint16_t opcode; }; enum { - FIO_SERVER_VER = 63, + FIO_SERVER_VER = 64, FIO_SERVER_MAX_FRAGMENT_PDU = 1024, FIO_SERVER_MAX_CMD_MB = 2048, @@ -212,7 +212,7 @@ extern int fio_server_text_output(int, const char *, size_t); extern int fio_net_send_cmd(int, uint16_t, const void *, off_t, uint64_t *, struct flist_head *); extern int fio_net_send_simple_cmd(int, uint16_t, uint64_t, struct flist_head *); extern void fio_server_set_arg(const char *); -extern int fio_server_parse_string(const char *, char **, int *, int *, struct in_addr *, struct in6_addr *, int *); +extern int fio_server_parse_string(const char *, char **, bool *, int *, struct in_addr *, struct in6_addr *, int *); extern int fio_server_parse_host(const char *, int, struct in_addr *, struct in6_addr *); extern const char *fio_server_op(unsigned int); extern void fio_server_got_signal(int); diff --git a/stat.c b/stat.c index fd3ad5a..5042650 100644 --- a/stat.c +++ b/stat.c @@ -37,9 +37,9 @@ void update_rusage_stat(struct thread_data *td) struct thread_stat *ts = &td->ts; fio_getrusage(&td->ru_end); - ts->usr_time += mtime_since(&td->ru_start.ru_utime, + ts->usr_time += mtime_since_tv(&td->ru_start.ru_utime, &td->ru_end.ru_utime); - ts->sys_time += mtime_since(&td->ru_start.ru_stime, + ts->sys_time += mtime_since_tv(&td->ru_start.ru_stime, &td->ru_end.ru_stime); ts->ctx += td->ru_end.ru_nvcsw + td->ru_end.ru_nivcsw - (td->ru_start.ru_nvcsw + td->ru_start.ru_nivcsw); @@ -58,7 +58,7 @@ void update_rusage_stat(struct thread_data *td) * group by looking at the index bits. * */ -static unsigned int plat_val_to_idx(unsigned int val) +static unsigned int plat_val_to_idx(unsigned long long val) { unsigned int msb, error_bits, base, offset, idx; @@ -66,7 +66,7 @@ static unsigned int plat_val_to_idx(unsigned int val) if (val == 0) msb = 0; else - msb = (sizeof(val)*8) - __builtin_clz(val) - 1; + msb = (sizeof(val)*8) - __builtin_clzll(val) - 1; /* * MSB <= (FIO_IO_U_PLAT_BITS-1), cannot be rounded off. Use @@ -135,16 +135,16 @@ static int double_cmp(const void *a, const void *b) } unsigned int calc_clat_percentiles(unsigned int *io_u_plat, unsigned long nr, - fio_fp64_t *plist, unsigned int **output, - unsigned int *maxv, unsigned int *minv) + fio_fp64_t *plist, unsigned long long **output, + unsigned long long *maxv, unsigned long long *minv) { unsigned long sum = 0; unsigned int len, i, j = 0; unsigned int oval_len = 0; - unsigned int *ovals = NULL; + unsigned long long *ovals = NULL; int is_last; - *minv = -1U; + *minv = -1ULL; *maxv = 0; len = 0; @@ -173,7 +173,7 @@ unsigned int calc_clat_percentiles(unsigned int *io_u_plat, unsigned long nr, if (j == oval_len) { oval_len += 100; - ovals = realloc(ovals, oval_len * sizeof(unsigned int)); + ovals = realloc(ovals, oval_len * sizeof(*ovals)); } ovals[j] = plat_idx_to_val(i); @@ -201,9 +201,10 @@ static void show_clat_percentiles(unsigned int *io_u_plat, unsigned long nr, fio_fp64_t *plist, unsigned int precision, struct buf_output *out) { - unsigned int len, j = 0, minv, maxv; - unsigned int *ovals; - int is_last, per_line, scale_down; + unsigned int divisor, len, i, j = 0; + unsigned long long minv, maxv; + unsigned long long *ovals; + int is_last, per_line, scale_down, time_width; char fmt[32]; len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv); @@ -211,23 +212,31 @@ static void show_clat_percentiles(unsigned int *io_u_plat, unsigned long nr, goto out; /* - * We default to usecs, but if the value range is such that we - * should scale down to msecs, do that. + * We default to nsecs, but if the value range is such that we + * should scale down to usecs or msecs, do that. */ - if (minv > 2000 && maxv > 99999) { - scale_down = 1; + if (minv > 2000000 && maxv > 99999999ULL) { + scale_down = 2; + divisor = 1000000; log_buf(out, " clat percentiles (msec):\n |"); + } else if (minv > 2000 && maxv > 99999) { + scale_down = 1; + divisor = 1000; + log_buf(out, " clat percentiles (usec):\n |"); } else { scale_down = 0; - log_buf(out, " clat percentiles (usec):\n |"); + divisor = 1; + log_buf(out, " clat percentiles (nsec):\n |"); } - snprintf(fmt, sizeof(fmt), "%%1.%uf", precision); - per_line = (80 - 7) / (precision + 14); - for (j = 0; j < len; j++) { - char fbuf[16], *ptr = fbuf; + time_width = max(5, (int) (log10(maxv / divisor) + 1)); + snprintf(fmt, sizeof(fmt), " %%%u.%ufth=[%%%dllu]%%c", precision + 3, + precision, time_width); + /* fmt will be something like " %5.2fth=[%4llu]%c" */ + per_line = (80 - 7) / (precision + 10 + time_width); + for (j = 0; j < len; j++) { /* for formatting */ if (j != 0 && (j % per_line) == 0) log_buf(out, " |"); @@ -235,15 +244,10 @@ static void show_clat_percentiles(unsigned int *io_u_plat, unsigned long nr, /* end of the list */ is_last = (j == len - 1); - if (plist[j].u.f < 10.0) - ptr += sprintf(fbuf, " "); - - snprintf(ptr, sizeof(fbuf), fmt, plist[j].u.f); - - if (scale_down) + for (i = 0; i < scale_down; i++) ovals[j] = (ovals[j] + 999) / 1000; - log_buf(out, " %sth=[%5u]%c", fbuf, ovals[j], is_last ? '\n' : ','); + log_buf(out, fmt, plist[j].u.f, ovals[j], is_last ? '\n' : ','); if (is_last) break; @@ -257,8 +261,8 @@ out: free(ovals); } -bool calc_lat(struct io_stat *is, unsigned long *min, unsigned long *max, - double *mean, double *dev) +bool calc_lat(struct io_stat *is, unsigned long long *min, + unsigned long long *max, double *mean, double *dev) { double n = (double) is->samples; @@ -355,6 +359,28 @@ static void stat_calc_lat(struct thread_stat *ts, double *dst, } } +/* + * To keep the terse format unaltered, add all of the ns latency + * buckets to the first us latency bucket + */ +void stat_calc_lat_nu(struct thread_stat *ts, double *io_u_lat_u) +{ + unsigned long ntotal = 0, total = ddir_rw_sum(ts->total_io_u); + int i; + + stat_calc_lat(ts, io_u_lat_u, ts->io_u_lat_u, FIO_IO_U_LAT_U_NR); + + for (i = 0; i < FIO_IO_U_LAT_N_NR; i++) + ntotal += ts->io_u_lat_n[i]; + + io_u_lat_u[0] += 100.0 * (double) ntotal / (double) total; +} + +void stat_calc_lat_n(struct thread_stat *ts, double *io_u_lat) +{ + stat_calc_lat(ts, io_u_lat, ts->io_u_lat_n, FIO_IO_U_LAT_N_NR); +} + void stat_calc_lat_u(struct thread_stat *ts, double *io_u_lat) { stat_calc_lat(ts, io_u_lat, ts->io_u_lat_u, FIO_IO_U_LAT_U_NR); @@ -365,14 +391,17 @@ void stat_calc_lat_m(struct thread_stat *ts, double *io_u_lat) stat_calc_lat(ts, io_u_lat, ts->io_u_lat_m, FIO_IO_U_LAT_M_NR); } -static void display_lat(const char *name, unsigned long min, unsigned long max, - double mean, double dev, struct buf_output *out) +static void display_lat(const char *name, unsigned long long min, + unsigned long long max, double mean, double dev, + struct buf_output *out) { - const char *base = "(usec)"; + const char *base = "(nsec)"; char *minp, *maxp; - if (usec_to_msec(&min, &max, &mean, &dev)) + if (nsec_to_msec(&min, &max, &mean, &dev)) base = "(msec)"; + else if (nsec_to_usec(&min, &max, &mean, &dev)) + base = "(usec)"; minp = num2str(min, 6, 1, 0, N2S_NONE); maxp = num2str(max, 6, 1, 0, N2S_NONE); @@ -388,8 +417,8 @@ static void show_ddir_status(struct group_run_stats *rs, struct thread_stat *ts, int ddir, struct buf_output *out) { const char *str[] = { " read", "write", " trim" }; - unsigned long min, max, runt; - unsigned long long bw, iops; + unsigned long runt; + unsigned long long min, max, bw, iops; double mean, dev; char *io_p, *bw_p, *bw_p_alt, *iops_p; int i2p; @@ -467,7 +496,7 @@ static void show_ddir_status(struct group_run_stats *rs, struct thread_stat *ts, bw_str = (rs->unit_base == 1 ? "Mibit" : "MiB"); } - log_buf(out, " bw (%5s/s): min=%5lu, max=%5lu, per=%3.2f%%, avg=%5.02f, stdev=%5.02f\n", + log_buf(out, " bw (%5s/s): min=%5llu, max=%5llu, per=%3.2f%%, avg=%5.02f, stdev=%5.02f\n", bw_str, min, max, p_of_agg, mean, dev); } } @@ -502,6 +531,14 @@ static int show_lat(double *io_u_lat, int nr, const char **ranges, return shown; } +static void show_lat_n(double *io_u_lat_n, struct buf_output *out) +{ + const char *ranges[] = { "2=", "4=", "10=", "20=", "50=", "100=", + "250=", "500=", "750=", "1000=", }; + + show_lat(io_u_lat_n, FIO_IO_U_LAT_N_NR, ranges, "nsec", out); +} + static void show_lat_u(double *io_u_lat_u, struct buf_output *out) { const char *ranges[] = { "2=", "4=", "10=", "20=", "50=", "100=", @@ -521,12 +558,15 @@ static void show_lat_m(double *io_u_lat_m, struct buf_output *out) static void show_latencies(struct thread_stat *ts, struct buf_output *out) { + double io_u_lat_n[FIO_IO_U_LAT_N_NR]; double io_u_lat_u[FIO_IO_U_LAT_U_NR]; double io_u_lat_m[FIO_IO_U_LAT_M_NR]; + stat_calc_lat_n(ts, io_u_lat_n); stat_calc_lat_u(ts, io_u_lat_u); stat_calc_lat_m(ts, io_u_lat_m); + show_lat_n(io_u_lat_n, out); show_lat_u(io_u_lat_u, out); show_lat_m(io_u_lat_m, out); } @@ -818,11 +858,10 @@ static void show_ddir_status_terse(struct thread_stat *ts, struct group_run_stats *rs, int ddir, struct buf_output *out) { - unsigned long min, max; - unsigned long long bw, iops; - unsigned int *ovals = NULL; + unsigned long long min, max, minv, maxv, bw, iops; + unsigned long long *ovals = NULL; double mean, dev; - unsigned int len, minv, maxv; + unsigned int len; int i; assert(ddir_rw(ddir)); @@ -840,14 +879,14 @@ static void show_ddir_status_terse(struct thread_stat *ts, (unsigned long long) ts->runtime[ddir]); if (calc_lat(&ts->slat_stat[ddir], &min, &max, &mean, &dev)) - log_buf(out, ";%lu;%lu;%f;%f", min, max, mean, dev); + log_buf(out, ";%llu;%llu;%f;%f", min/1000, max/1000, mean/1000, dev/1000); else - log_buf(out, ";%lu;%lu;%f;%f", 0UL, 0UL, 0.0, 0.0); + log_buf(out, ";%llu;%llu;%f;%f", 0ULL, 0ULL, 0.0, 0.0); if (calc_lat(&ts->clat_stat[ddir], &min, &max, &mean, &dev)) - log_buf(out, ";%lu;%lu;%f;%f", min, max, mean, dev); + log_buf(out, ";%llu;%llu;%f;%f", min/1000, max/1000, mean/1000, dev/1000); else - log_buf(out, ";%lu;%lu;%f;%f", 0UL, 0UL, 0.0, 0.0); + log_buf(out, ";%llu;%llu;%f;%f", 0ULL, 0ULL, 0.0, 0.0); if (ts->clat_percentiles) { len = calc_clat_percentiles(ts->io_u_plat[ddir], @@ -862,13 +901,13 @@ static void show_ddir_status_terse(struct thread_stat *ts, log_buf(out, ";0%%=0"); continue; } - log_buf(out, ";%f%%=%u", ts->percentile_list[i].u.f, ovals[i]); + log_buf(out, ";%f%%=%llu", ts->percentile_list[i].u.f, ovals[i]/1000); } if (calc_lat(&ts->lat_stat[ddir], &min, &max, &mean, &dev)) - log_buf(out, ";%lu;%lu;%f;%f", min, max, mean, dev); + log_buf(out, ";%llu;%llu;%f;%f", min/1000, max/1000, mean/1000, dev/1000); else - log_buf(out, ";%lu;%lu;%f;%f", 0UL, 0UL, 0.0, 0.0); + log_buf(out, ";%llu;%llu;%f;%f", 0ULL, 0ULL, 0.0, 0.0); if (ovals) free(ovals); @@ -882,19 +921,19 @@ static void show_ddir_status_terse(struct thread_stat *ts, p_of_agg = 100.0; } - log_buf(out, ";%lu;%lu;%f%%;%f;%f", min, max, p_of_agg, mean, dev); + log_buf(out, ";%llu;%llu;%f%%;%f;%f", min, max, p_of_agg, mean, dev); } else - log_buf(out, ";%lu;%lu;%f%%;%f;%f", 0UL, 0UL, 0.0, 0.0, 0.0); + log_buf(out, ";%llu;%llu;%f%%;%f;%f", 0ULL, 0ULL, 0.0, 0.0, 0.0); } static void add_ddir_status_json(struct thread_stat *ts, struct group_run_stats *rs, int ddir, struct json_object *parent) { - unsigned long min, max; + unsigned long long min, max, minv, maxv; unsigned long long bw; - unsigned int *ovals = NULL; + unsigned long long *ovals = NULL; double mean, dev, iops; - unsigned int len, minv, maxv; + unsigned int len; int i; const char *ddirname[] = {"read", "write", "trim"}; struct json_object *dir_object, *tmp_object, *percentile_object, *clat_bins_object; @@ -933,7 +972,7 @@ static void add_ddir_status_json(struct thread_stat *ts, mean = dev = 0.0; } tmp_object = json_create_object(); - json_object_add_value_object(dir_object, "slat", tmp_object); + json_object_add_value_object(dir_object, "slat_ns", tmp_object); json_object_add_value_int(tmp_object, "min", min); json_object_add_value_int(tmp_object, "max", max); json_object_add_value_float(tmp_object, "mean", mean); @@ -944,7 +983,7 @@ static void add_ddir_status_json(struct thread_stat *ts, mean = dev = 0.0; } tmp_object = json_create_object(); - json_object_add_value_object(dir_object, "clat", tmp_object); + json_object_add_value_object(dir_object, "clat_ns", tmp_object); json_object_add_value_int(tmp_object, "min", min); json_object_add_value_int(tmp_object, "max", max); json_object_add_value_float(tmp_object, "mean", mean); @@ -985,7 +1024,7 @@ static void add_ddir_status_json(struct thread_stat *ts, mean = dev = 0.0; } tmp_object = json_create_object(); - json_object_add_value_object(dir_object, "lat", tmp_object); + json_object_add_value_object(dir_object, "lat_ns", tmp_object); json_object_add_value_int(tmp_object, "min", min); json_object_add_value_int(tmp_object, "max", max); json_object_add_value_float(tmp_object, "mean", mean); @@ -1047,7 +1086,7 @@ static void show_thread_status_terse_v2(struct thread_stat *ts, /* Calc % distribution of IO depths, usecond, msecond latency */ stat_calc_dist(ts->io_u_map, ddir_rw_sum(ts->total_io_u), io_u_dist); - stat_calc_lat_u(ts, io_u_lat_u); + stat_calc_lat_nu(ts, io_u_lat_u); stat_calc_lat_m(ts, io_u_lat_m); /* Only show fixed 7 I/O depth levels*/ @@ -1112,7 +1151,7 @@ static void show_thread_status_terse_v3_v4(struct thread_stat *ts, /* Calc % distribution of IO depths, usecond, msecond latency */ stat_calc_dist(ts->io_u_map, ddir_rw_sum(ts->total_io_u), io_u_dist); - stat_calc_lat_u(ts, io_u_lat_u); + stat_calc_lat_nu(ts, io_u_lat_u); stat_calc_lat_m(ts, io_u_lat_m); /* Only show fixed 7 I/O depth levels*/ @@ -1173,6 +1212,7 @@ static struct json_object *show_thread_status_json(struct thread_stat *ts, struct json_object *root, *tmp; struct jobs_eta *je; double io_u_dist[FIO_IO_U_MAP_NR]; + double io_u_lat_n[FIO_IO_U_LAT_N_NR]; double io_u_lat_u[FIO_IO_U_LAT_U_NR]; double io_u_lat_m[FIO_IO_U_LAT_M_NR]; double usr_cpu, sys_cpu; @@ -1217,6 +1257,7 @@ static struct json_object *show_thread_status_json(struct thread_stat *ts, /* Calc % distribution of IO depths, usecond, msecond latency */ stat_calc_dist(ts->io_u_map, ddir_rw_sum(ts->total_io_u), io_u_dist); + stat_calc_lat_n(ts, io_u_lat_n); stat_calc_lat_u(ts, io_u_lat_u); stat_calc_lat_m(ts, io_u_lat_m); @@ -1232,9 +1273,17 @@ static struct json_object *show_thread_status_json(struct thread_stat *ts, json_object_add_value_float(tmp, (const char *)name, io_u_dist[i]); } + /* Nanosecond latency */ tmp = json_create_object(); - json_object_add_value_object(root, "latency_us", tmp); + json_object_add_value_object(root, "latency_ns", tmp); + for (i = 0; i < FIO_IO_U_LAT_N_NR; i++) { + const char *ranges[] = { "2", "4", "10", "20", "50", "100", + "250", "500", "750", "1000", }; + json_object_add_value_float(tmp, ranges[i], io_u_lat_n[i]); + } /* Microsecond latency */ + tmp = json_create_object(); + json_object_add_value_object(root, "latency_us", tmp); for (i = 0; i < FIO_IO_U_LAT_U_NR; i++) { const char *ranges[] = { "2", "4", "10", "20", "50", "100", "250", "500", "750", "1000", }; @@ -1494,6 +1543,8 @@ void sum_thread_stats(struct thread_stat *dst, struct thread_stat *src, dst->io_u_submit[k] += src->io_u_submit[k]; for (k = 0; k < FIO_IO_U_MAP_NR; k++) dst->io_u_complete[k] += src->io_u_complete[k]; + for (k = 0; k < FIO_IO_U_LAT_N_NR; k++) + dst->io_u_lat_n[k] += src->io_u_lat_n[k]; for (k = 0; k < FIO_IO_U_LAT_U_NR; k++) dst->io_u_lat_u[k] += src->io_u_lat_u[k]; for (k = 0; k < FIO_IO_U_LAT_M_NR; k++) @@ -1849,22 +1900,22 @@ void __show_running_run_stats(void) { struct thread_data *td; unsigned long long *rt; - struct timeval tv; + struct timespec ts; int i; fio_mutex_down(stat_mutex); rt = malloc(thread_number * sizeof(unsigned long long)); - fio_gettime(&tv, NULL); + fio_gettime(&ts, NULL); for_each_td(td, i) { td->update_rusage = 1; td->ts.io_bytes[DDIR_READ] = td->io_bytes[DDIR_READ]; td->ts.io_bytes[DDIR_WRITE] = td->io_bytes[DDIR_WRITE]; td->ts.io_bytes[DDIR_TRIM] = td->io_bytes[DDIR_TRIM]; - td->ts.total_run_time = mtime_since(&td->epoch, &tv); + td->ts.total_run_time = mtime_since(&td->epoch, &ts); - rt[i] = mtime_since(&td->start, &tv); + rt[i] = mtime_since(&td->start, &ts); if (td_read(td) && td->ts.io_bytes[DDIR_READ]) td->ts.runtime[DDIR_READ] += rt[i]; if (td_write(td) && td->ts.io_bytes[DDIR_WRITE]) @@ -1899,7 +1950,7 @@ void __show_running_run_stats(void) } static int status_interval_init; -static struct timeval status_time; +static struct timespec status_time; static int status_file_disabled; #define FIO_STATUS_FILE "fio-dump-status" @@ -1955,7 +2006,7 @@ void check_for_running_stats(void) } } -static inline void add_stat_sample(struct io_stat *is, unsigned long data) +static inline void add_stat_sample(struct io_stat *is, unsigned long long data) { double val = data; double delta; @@ -2188,6 +2239,8 @@ void reset_io_stats(struct thread_data *td) ts->io_u_complete[i] = 0; } + for (i = 0; i < FIO_IO_U_LAT_N_NR; i++) + ts->io_u_lat_n[i] = 0; for (i = 0; i < FIO_IO_U_LAT_U_NR; i++) ts->io_u_lat_u[i] = 0; for (i = 0; i < FIO_IO_U_LAT_M_NR; i++) @@ -2303,16 +2356,16 @@ void add_agg_sample(union io_sample_data data, enum fio_ddir ddir, unsigned int } static void add_clat_percentile_sample(struct thread_stat *ts, - unsigned long usec, enum fio_ddir ddir) + unsigned long long nsec, enum fio_ddir ddir) { - unsigned int idx = plat_val_to_idx(usec); + unsigned int idx = plat_val_to_idx(nsec); assert(idx < FIO_IO_U_PLAT_NR); ts->io_u_plat[ddir][idx]++; } void add_clat_sample(struct thread_data *td, enum fio_ddir ddir, - unsigned long usec, unsigned int bs, uint64_t offset) + unsigned long long nsec, unsigned int bs, uint64_t offset) { unsigned long elapsed, this_window; struct thread_stat *ts = &td->ts; @@ -2320,14 +2373,14 @@ void add_clat_sample(struct thread_data *td, enum fio_ddir ddir, td_io_u_lock(td); - add_stat_sample(&ts->clat_stat[ddir], usec); + add_stat_sample(&ts->clat_stat[ddir], nsec); if (td->clat_log) - add_log_sample(td, td->clat_log, sample_val(usec), ddir, bs, + add_log_sample(td, td->clat_log, sample_val(nsec), ddir, bs, offset); if (ts->clat_percentiles) - add_clat_percentile_sample(ts, usec, ddir); + add_clat_percentile_sample(ts, nsec, ddir); if (iolog && iolog->hist_msec) { struct io_hist *hw = &iolog->hist_window[ddir]; @@ -2389,7 +2442,7 @@ void add_slat_sample(struct thread_data *td, enum fio_ddir ddir, } void add_lat_sample(struct thread_data *td, enum fio_ddir ddir, - unsigned long usec, unsigned int bs, uint64_t offset) + unsigned long long nsec, unsigned int bs, uint64_t offset) { struct thread_stat *ts = &td->ts; @@ -2398,23 +2451,23 @@ void add_lat_sample(struct thread_data *td, enum fio_ddir ddir, td_io_u_lock(td); - add_stat_sample(&ts->lat_stat[ddir], usec); + add_stat_sample(&ts->lat_stat[ddir], nsec); if (td->lat_log) - add_log_sample(td, td->lat_log, sample_val(usec), ddir, bs, + add_log_sample(td, td->lat_log, sample_val(nsec), ddir, bs, offset); td_io_u_unlock(td); } void add_bw_sample(struct thread_data *td, struct io_u *io_u, - unsigned int bytes, unsigned long spent) + unsigned int bytes, unsigned long long spent) { struct thread_stat *ts = &td->ts; unsigned long rate; if (spent) - rate = bytes * 1000 / spent; + rate = (unsigned long) (bytes * 1000000ULL / spent); else rate = 0; @@ -2430,8 +2483,8 @@ void add_bw_sample(struct thread_data *td, struct io_u *io_u, td_io_u_unlock(td); } -static int __add_samples(struct thread_data *td, struct timeval *parent_tv, - struct timeval *t, unsigned int avg_time, +static int __add_samples(struct thread_data *td, struct timespec *parent_tv, + struct timespec *t, unsigned int avg_time, uint64_t *this_io_bytes, uint64_t *stat_io_bytes, struct io_stat *stat, struct io_log *log, bool is_kb) @@ -2481,7 +2534,7 @@ static int __add_samples(struct thread_data *td, struct timeval *parent_tv, stat_io_bytes[ddir] = this_io_bytes[ddir]; } - timeval_add_msec(parent_tv, avg_time); + timespec_add_msec(parent_tv, avg_time); td_io_u_unlock(td); @@ -2493,7 +2546,7 @@ static int __add_samples(struct thread_data *td, struct timeval *parent_tv, return min(next, next_log); } -static int add_bw_samples(struct thread_data *td, struct timeval *t) +static int add_bw_samples(struct thread_data *td, struct timespec *t) { return __add_samples(td, &td->bw_sample_time, t, td->o.bw_avg_time, td->this_io_bytes, td->stat_io_bytes, @@ -2517,7 +2570,7 @@ void add_iops_sample(struct thread_data *td, struct io_u *io_u, td_io_u_unlock(td); } -static int add_iops_samples(struct thread_data *td, struct timeval *t) +static int add_iops_samples(struct thread_data *td, struct timespec *t) { return __add_samples(td, &td->iops_sample_time, t, td->o.iops_avg_time, td->this_io_blocks, td->stat_io_blocks, @@ -2531,7 +2584,7 @@ int calc_log_samples(void) { struct thread_data *td; unsigned int next = ~0U, tmp; - struct timeval now; + struct timespec now; int i; fio_gettime(&now, NULL); diff --git a/stat.h b/stat.h index d8a0803..132dee3 100644 --- a/stat.h +++ b/stat.h @@ -19,6 +19,7 @@ struct group_run_stats { * How many depth levels to log */ #define FIO_IO_U_MAP_NR 7 +#define FIO_IO_U_LAT_N_NR 10 #define FIO_IO_U_LAT_U_NR 10 #define FIO_IO_U_LAT_M_NR 12 @@ -108,7 +109,7 @@ struct group_run_stats { #define FIO_IO_U_PLAT_BITS 6 #define FIO_IO_U_PLAT_VAL (1 << FIO_IO_U_PLAT_BITS) -#define FIO_IO_U_PLAT_GROUP_NR 19 +#define FIO_IO_U_PLAT_GROUP_NR 29 #define FIO_IO_U_PLAT_NR (FIO_IO_U_PLAT_GROUP_NR * FIO_IO_U_PLAT_VAL) #define FIO_IO_U_LIST_MAX_LEN 20 /* The size of the default and user-specified list of percentiles */ @@ -178,6 +179,7 @@ struct thread_stat { uint32_t io_u_map[FIO_IO_U_MAP_NR]; uint32_t io_u_submit[FIO_IO_U_MAP_NR]; uint32_t io_u_complete[FIO_IO_U_MAP_NR]; + uint32_t io_u_lat_n[FIO_IO_U_LAT_N_NR]; uint32_t io_u_lat_u[FIO_IO_U_LAT_U_NR]; uint32_t io_u_lat_m[FIO_IO_U_LAT_M_NR]; uint32_t io_u_plat[DDIR_RWDIR_CNT][FIO_IO_U_PLAT_NR]; @@ -286,8 +288,9 @@ extern void sum_group_stats(struct group_run_stats *dst, struct group_run_stats extern void init_thread_stat(struct thread_stat *ts); extern void init_group_run_stat(struct group_run_stats *gs); extern void eta_to_str(char *str, unsigned long eta_sec); -extern bool calc_lat(struct io_stat *is, unsigned long *min, unsigned long *max, double *mean, double *dev); -extern unsigned int calc_clat_percentiles(unsigned int *io_u_plat, unsigned long nr, fio_fp64_t *plist, unsigned int **output, unsigned int *maxv, unsigned int *minv); +extern bool calc_lat(struct io_stat *is, unsigned long long *min, unsigned long long *max, double *mean, double *dev); +extern unsigned int calc_clat_percentiles(unsigned int *io_u_plat, unsigned long nr, fio_fp64_t *plist, unsigned long long **output, unsigned long long *maxv, unsigned long long *minv); +extern void stat_calc_lat_n(struct thread_stat *ts, double *io_u_lat); 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); @@ -295,9 +298,9 @@ extern void reset_io_stats(struct thread_data *); extern void update_rusage_stat(struct thread_data *); extern void clear_rusage_stat(struct thread_data *); -extern void add_lat_sample(struct thread_data *, enum fio_ddir, unsigned long, +extern void add_lat_sample(struct thread_data *, enum fio_ddir, unsigned long long, unsigned int, uint64_t); -extern void add_clat_sample(struct thread_data *, enum fio_ddir, unsigned long, +extern void add_clat_sample(struct thread_data *, enum fio_ddir, unsigned long long, unsigned int, uint64_t); extern void add_slat_sample(struct thread_data *, enum fio_ddir, unsigned long, unsigned int, uint64_t); @@ -305,16 +308,17 @@ extern void add_agg_sample(union io_sample_data, enum fio_ddir, unsigned int); extern void add_iops_sample(struct thread_data *, struct io_u *, unsigned int); extern void add_bw_sample(struct thread_data *, struct io_u *, - unsigned int, unsigned long); + unsigned int, unsigned long long); extern int calc_log_samples(void); extern struct io_log *agg_io_log[DDIR_RWDIR_CNT]; extern int write_bw_log; -static inline bool usec_to_msec(unsigned long *min, unsigned long *max, - double *mean, double *dev) +static inline bool nsec_to_usec(unsigned long long *min, + unsigned long long *max, double *mean, + double *dev) { - if (*min > 1000 && *max > 1000 && *mean > 1000.0 && *dev > 1000.0) { + if (*min > 2000 && *max > 99999 && *dev > 1000.0) { *min /= 1000; *max /= 1000; *mean /= 1000.0; @@ -324,6 +328,22 @@ static inline bool usec_to_msec(unsigned long *min, unsigned long *max, return false; } + +static inline bool nsec_to_msec(unsigned long long *min, + unsigned long long *max, double *mean, + double *dev) +{ + if (*min > 2000000 && *max > 99999999ULL && *dev > 1000000.0) { + *min /= 1000000; + *max /= 1000000; + *mean /= 1000000.0; + *dev /= 1000000.0; + return true; + } + + return false; +} + /* * Worst level condensing would be 1:5, so allow enough room for that */ diff --git a/steadystate.c b/steadystate.c index 98f027c..45d4f5d 100644 --- a/steadystate.c +++ b/steadystate.c @@ -196,7 +196,7 @@ void steadystate_check(void) int i, j, ddir, prev_groupid, group_ramp_time_over = 0; unsigned long rate_time; struct thread_data *td, *td2; - struct timeval now; + struct timespec now; uint64_t group_bw = 0, group_iops = 0; uint64_t td_iops, td_bytes; bool ret; diff --git a/steadystate.h b/steadystate.h index 20ccd30..bbc3945 100644 --- a/steadystate.h +++ b/steadystate.h @@ -35,7 +35,7 @@ struct steadystate_data { uint64_t sum_xy; uint64_t oldest_y; - struct timeval prev_time; + struct timespec prev_time; uint64_t prev_iops; uint64_t prev_bytes; }; diff --git a/t/arch.c b/t/arch.c index befb7c7..bd28a84 100644 --- a/t/arch.c +++ b/t/arch.c @@ -1,5 +1,5 @@ #include "../arch/arch.h" unsigned long arch_flags = 0; -int tsc_reliable; +bool tsc_reliable; int arch_random; diff --git a/t/debug.c b/t/debug.c index bf6f460..8965cfb 100644 --- a/t/debug.c +++ b/t/debug.c @@ -1,7 +1,7 @@ #include <stdio.h> FILE *f_err; -struct timeval *fio_tv = NULL; +struct timespec *fio_ts = NULL; unsigned long fio_debug = 0; void __dprint(int type, const char *str, ...) diff --git a/t/dedupe.c b/t/dedupe.c index 1f172a2..c3b837f 100644 --- a/t/dedupe.c +++ b/t/dedupe.c @@ -334,7 +334,7 @@ static void *thread_fn(void *data) static void show_progress(struct worker_thread *threads, unsigned long total) { unsigned long last_nitems = 0; - struct timeval last_tv; + struct timespec last_tv; fio_gettime(&last_tv, NULL); diff --git a/t/lfsr-test.c b/t/lfsr-test.c index 7016f26..4009b62 100644 --- a/t/lfsr-test.c +++ b/t/lfsr-test.c @@ -27,7 +27,7 @@ void usage() int main(int argc, char *argv[]) { int r; - struct timeval start, end; + struct timespec start, end; struct fio_lfsr *fl; int verify = 0; unsigned int spin = 0; diff --git a/t/time-test.c b/t/time-test.c new file mode 100644 index 0000000..a74d920 --- /dev/null +++ b/t/time-test.c @@ -0,0 +1,544 @@ +/* + * Carry out arithmetic to explore conversion of CPU clock ticks to nsec + * + * When we use the CPU clock for timing, we do the following: + * + * 1) Calibrate the CPU clock to relate the frequency of CPU clock ticks + * to actual time. + * + * Using gettimeofday() or clock_gettime(), count how many CPU clock + * ticks occur per usec + * + * 2) Calculate conversion factors so that we can ultimately convert + * from clocks ticks to nsec with + * nsec = (ticks * clock_mult) >> clock_shift + * + * This is equivalent to + * nsec = ticks * (MULTIPLIER / cycles_per_nsec) / MULTIPLIER + * where + * clock_mult = MULTIPLIER / cycles_per_nsec + * MULTIPLIER = 2^clock_shift + * + * It would be simpler to just calculate nsec = ticks / cycles_per_nsec, + * but all of this is necessary because of rounding when calculating + * cycles_per_nsec. With a 3.0GHz CPU, cycles_per_nsec would simply + * be 3. But with a 3.33GHz CPU or a 4.5GHz CPU, the fractional + * portion is lost with integer arithmetic. + * + * This multiply and shift calculation also has a performance benefit + * as multiplication and bit shift operations are faster than integer + * division. + * + * 3) Dynamically determine clock_shift and clock_mult at run time based + * on MAX_CLOCK_SEC and cycles_per_usec. MAX_CLOCK_SEC is the maximum + * duration for which the conversion will be valid. + * + * The primary constraint is that (ticks * clock_mult) must not overflow + * when ticks is at its maximum value. + * + * So we have + * max_ticks = MAX_CLOCK_SEC * 1000000000 * cycles_per_nsec + * max_ticks * clock_mult <= ULLONG_MAX + * max_ticks * MULTIPLIER / cycles_per_nsec <= ULLONG_MAX + * MULTIPLIER <= ULLONG_MAX * cycles_per_nsec / max_ticks + * + * Then choose the largest clock_shift that satisfies + * 2^clock_shift <= ULLONG_MAX * cycles_per_nsec / max_ticks + * + * Finally calculate the appropriate clock_mult associated with clock_shift + * clock_mult = 2^clock_shift / cycles_per_nsec + * + * 4) In the code below we have cycles_per_usec and use + * cycles_per_nsec = cycles_per_usec / 1000 + * + * + * The code below implements 4 clock tick to nsec conversion strategies + * + * i) 64-bit arithmetic for the (ticks * clock_mult) product with the + * conversion valid for at most MAX_CLOCK_SEC + * + * ii) NOT IMPLEMENTED Use 64-bit integers to emulate 128-bit multiplication + * for the (ticks * clock_mult) product + * + * iii) 64-bit arithmetic with clock ticks to nsec conversion occurring in + * two stages. The first stage counts the number of discrete, large chunks + * of time that have elapsed. To this is added the time represented by + * the remaining clock ticks. The advantage of this strategy is better + * accuracy because the (ticks * clock_mult) product used for final + * fractional chunk + * + * iv) 64-bit arithmetic with the clock ticks to nsec conversion occuring in + * two stages. This is carried out using locks to update the number of + * large time chunks (MAX_CLOCK_SEC_2STAGE) that have elapsed. + * + * v) 128-bit arithmetic used for the clock ticks to nsec conversion. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <assert.h> +#include <stdlib.h> +#include "lib/seqlock.h" + +#define DEBUG 0 +#define MAX_CLOCK_SEC 365*24*60*60ULL +#define MAX_CLOCK_SEC_2STAGE 60*60ULL +#define dprintf(...) if (DEBUG) { printf(__VA_ARGS__); } + +enum { + __CLOCK64_BIT = 1 << 0, + __CLOCK128_BIT = 1 << 1, + __CLOCK_MULT_SHIFT = 1 << 2, + __CLOCK_EMULATE_128 = 1 << 3, + __CLOCK_2STAGE = 1 << 4, + __CLOCK_LOCK = 1 << 5, + + CLOCK64_MULT_SHIFT = __CLOCK64_BIT | __CLOCK_MULT_SHIFT, + CLOCK64_EMULATE_128 = __CLOCK64_BIT | __CLOCK_EMULATE_128, + CLOCK64_2STAGE = __CLOCK64_BIT | __CLOCK_2STAGE, + CLOCK64_LOCK = __CLOCK64_BIT | __CLOCK_LOCK, + CLOCK128_MULT_SHIFT = __CLOCK128_BIT | __CLOCK_MULT_SHIFT, +}; + +static struct seqlock clock_seqlock; +static unsigned long long cycles_start; +static unsigned long long elapsed_nsec; + +static unsigned int max_cycles_shift; +static unsigned long long max_cycles_mask; +static unsigned long long nsecs_for_max_cycles; + +static unsigned int clock_shift; +static unsigned long long clock_mult; + +static unsigned long long *nsecs; +static unsigned long long clock_mult64_128[2]; +static __uint128_t clock_mult128; + +/* + * Functions for carrying out 128-bit + * arithmetic using 64-bit integers + * + * 128-bit integers are stored as + * arrays of two 64-bit integers + * + * Ordering is little endian + * + * a[0] has the less significant bits + * a[1] has the more significant bits + * + * NOT FULLY IMPLEMENTED + */ +static void do_mult(unsigned long long a[2], unsigned long long b, + unsigned long long product[2]) +{ + product[0] = product[1] = 0; + return; +} + +static void do_div(unsigned long long a[2], unsigned long long b, + unsigned long long c[2]) +{ + return; +} + +static void do_shift64(unsigned long long a[2], unsigned int count) +{ + a[0] = a[1] >> (count-64); + a[1] = 0; +} + +static void do_shift(unsigned long long a[2], unsigned int count) +{ + if (count > 64) + do_shift64(a, count); + else { + while (count--) { + a[0] >>= 1; + a[0] |= a[1] << 63; + a[1] >>= 1; + } + } +} + +static void update_clock(unsigned long long t) +{ + write_seqlock_begin(&clock_seqlock); + elapsed_nsec = (t >> max_cycles_shift) * nsecs_for_max_cycles; + cycles_start = t & ~max_cycles_mask; + write_seqlock_end(&clock_seqlock); +} + +static unsigned long long _get_nsec(int mode, unsigned long long t) +{ + switch(mode) { + case CLOCK64_MULT_SHIFT: + return (t * clock_mult) >> clock_shift; + case CLOCK64_EMULATE_128: { + unsigned long long product[2] = { }; + + do_mult(clock_mult64_128, t, product); + do_shift(product, clock_shift); + return product[0]; + } + case CLOCK64_2STAGE: { + unsigned long long multiples, nsec; + + multiples = t >> max_cycles_shift; + dprintf("multiples=%llu\n", multiples); + nsec = multiples * nsecs_for_max_cycles; + nsec += ((t & max_cycles_mask) * clock_mult) >> clock_shift; + return nsec; + } + case CLOCK64_LOCK: { + unsigned int seq; + unsigned long long nsec; + + do { + seq = read_seqlock_begin(&clock_seqlock); + nsec = elapsed_nsec; + nsec += ((t - cycles_start) * clock_mult) >> clock_shift; + } while (read_seqlock_retry(&clock_seqlock, seq)); + return nsec; + } + case CLOCK128_MULT_SHIFT: + return (unsigned long long)((t * clock_mult128) >> clock_shift); + default: + assert(0); + } +} + +static unsigned long long get_nsec(int mode, unsigned long long t) +{ + if (mode == CLOCK64_LOCK) { + update_clock(t); + } + + return _get_nsec(mode, t); +} + +static void calc_mult_shift(int mode, void *mult, unsigned int *shift, + unsigned long long max_sec, + unsigned long long cycles_per_usec) +{ + unsigned long long max_ticks; + max_ticks = max_sec * cycles_per_usec * 1000000ULL; + + switch (mode) { + case CLOCK64_MULT_SHIFT: { + unsigned long long max_mult, tmp; + unsigned int sft = 0; + + /* + * Calculate the largest multiplier that will not + * produce a 64-bit overflow in the multiplication + * step of the clock ticks to nsec conversion + */ + max_mult = ULLONG_MAX / max_ticks; + dprintf("max_ticks=%llu, __builtin_clzll=%d, max_mult=%llu\n", max_ticks, __builtin_clzll(max_ticks), max_mult); + + /* + * Find the largest shift count that will produce + * a multiplier less than max_mult + */ + tmp = max_mult * cycles_per_usec / 1000; + while (tmp > 1) { + tmp >>= 1; + sft++; + dprintf("tmp=%llu, sft=%u\n", tmp, sft); + } + + *shift = sft; + *((unsigned long long *)mult) = (unsigned long long) ((1ULL << sft) * 1000 / cycles_per_usec); + break; + } + case CLOCK64_EMULATE_128: { + unsigned long long max_mult[2], tmp[2] = { }; + unsigned int sft = 0; + + /* + * Calculate the largest multiplier that will not + * produce a 128-bit overflow in the multiplication + * step of the clock ticks to nsec conversion, + * but use only 64-bit integers in the process + */ + max_mult[0] = max_mult[1] = ULLONG_MAX; + do_div(max_mult, max_ticks, max_mult); + dprintf("max_ticks=%llu, __builtin_clzll=%d, max_mult=0x%016llx%016llx\n", + max_ticks, __builtin_clzll(max_ticks), max_mult[1], max_mult[0]); + + /* + * Find the largest shift count that will produce + * a multiplier less than max_mult + */ + do_div(max_mult, cycles_per_usec, tmp); + do_div(tmp, 1000ULL, tmp); + while (tmp[0] > 1 || tmp[1] > 1) { + do_shift(tmp, 1); + sft++; + dprintf("tmp=0x%016llx%016llx, sft=%u\n", tmp[1], tmp[0], sft); + } + + *shift = sft; +// *((unsigned long long *)mult) = (__uint128_t) (((__uint128_t)1 << sft) * 1000 / cycles_per_usec); + break; + } + case CLOCK64_2STAGE: { + unsigned long long tmp; +/* + * This clock tick to nsec conversion requires two stages. + * + * Stage 1: Determine how many ~MAX_CLOCK_SEC_2STAGE periods worth of clock ticks + * have elapsed and set nsecs to the appropriate value for those + * ~MAX_CLOCK_SEC_2STAGE periods. + * Stage 2: Subtract the ticks for the elapsed ~MAX_CLOCK_SEC_2STAGE periods from + * Stage 1. Convert remaining clock ticks to nsecs and add to previously + * set nsec value. + * + * To optimize the arithmetic operations, use the greatest power of 2 ticks + * less than the number of ticks in MAX_CLOCK_SEC_2STAGE seconds. + * + */ + // Use a period shorter than MAX_CLOCK_SEC here for better accuracy + calc_mult_shift(CLOCK64_MULT_SHIFT, mult, shift, MAX_CLOCK_SEC_2STAGE, cycles_per_usec); + + // Find the greatest power of 2 clock ticks that is less than the ticks in MAX_CLOCK_SEC_2STAGE + max_cycles_shift = max_cycles_mask = 0; + tmp = MAX_CLOCK_SEC_2STAGE * 1000000ULL * cycles_per_usec; + dprintf("tmp=%llu, max_cycles_shift=%u\n", tmp, max_cycles_shift); + while (tmp > 1) { + tmp >>= 1; + max_cycles_shift++; + dprintf("tmp=%llu, max_cycles_shift=%u\n", tmp, max_cycles_shift); + } + // if use use (1ULL << max_cycles_shift) * 1000 / cycles_per_usec here we will + // have a discontinuity every (1ULL << max_cycles_shift) cycles + nsecs_for_max_cycles = (1ULL << max_cycles_shift) * *((unsigned long long *)mult) >> *shift; + + // Use a bitmask to calculate ticks % (1ULL << max_cycles_shift) + for (tmp = 0; tmp < max_cycles_shift; tmp++) + max_cycles_mask |= 1ULL << tmp; + + dprintf("max_cycles_shift=%u, 2^max_cycles_shift=%llu, nsecs_for_max_cycles=%llu, max_cycles_mask=%016llx\n", + max_cycles_shift, (1ULL << max_cycles_shift), + nsecs_for_max_cycles, max_cycles_mask); + + + break; + } + case CLOCK64_LOCK: { +/* + * This clock tick to nsec conversion also requires two stages. + * + * Stage 1: Add to nsec the current running total of elapsed long periods + * Stage 2: Subtract from clock ticks the tick count corresponding to the + * most recently elapsed long period. Convert the remaining ticks to + * nsec and add to the previous nsec value. + * + * In practice the elapsed nsec from Stage 1 and the tick count subtracted + * in Stage 2 will be maintained in a separate thread. + * + */ + calc_mult_shift(CLOCK64_2STAGE, mult, shift, MAX_CLOCK_SEC, cycles_per_usec); + cycles_start = 0; + break; + } + case CLOCK128_MULT_SHIFT: { + __uint128_t max_mult, tmp; + unsigned int sft = 0; + + /* + * Calculate the largest multiplier that will not + * produce a 128-bit overflow in the multiplication + * step of the clock ticks to nsec conversion + */ + max_mult = ((__uint128_t) ULLONG_MAX) << 64 | ULLONG_MAX; + max_mult /= max_ticks; + dprintf("max_ticks=%llu, __builtin_clzll=%d, max_mult=0x%016llx%016llx\n", + max_ticks, __builtin_clzll(max_ticks), + (unsigned long long) (max_mult >> 64), + (unsigned long long) max_mult); + + /* + * Find the largest shift count that will produce + * a multiplier less than max_mult + */ + tmp = max_mult * cycles_per_usec / 1000; + while (tmp > 1) { + tmp >>= 1; + sft++; + dprintf("tmp=0x%016llx%016llx, sft=%u\n", + (unsigned long long) (tmp >> 64), + (unsigned long long) tmp, sft); + } + + *shift = sft; + *((__uint128_t *)mult) = (__uint128_t) (((__uint128_t)1 << sft) * 1000 / cycles_per_usec); + break; + } + } +} + +static int discontinuity(int mode, int delta_ticks, int delta_nsec, + unsigned long long start, unsigned long len) +{ + int i; + unsigned long mismatches = 0, bad_mismatches = 0; + unsigned long long delta, max_mismatch = 0; + unsigned long long *ns = nsecs; + + for (i = 0; i < len; ns++, i++) { + *ns = get_nsec(mode, start + i); + if (i - delta_ticks >= 0) { + if (*ns > *(ns - delta_ticks)) + delta = *ns - *(ns - delta_ticks); + else + delta = *(ns - delta_ticks) - *ns; + if (delta > delta_nsec) + delta -= delta_nsec; + else + delta = delta_nsec - delta; + if (delta) { + mismatches++; + if (delta > 1) + bad_mismatches++; + if (delta > max_mismatch) + max_mismatch = delta; + } + } + if (!bad_mismatches) + assert(max_mismatch == 0 || max_mismatch == 1); + if (!mismatches) + assert(max_mismatch == 0); + } + + printf("%lu discontinuities (%lu%%) (%lu errors > 1ns, max delta = %lluns) for ticks = %llu...%llu\n", + mismatches, (mismatches * 100) / len, bad_mismatches, max_mismatch, start, + start + len - 1); + return mismatches; +} + +#define MIN_TICKS 1ULL +#define LEN 1000000000ULL +#define NSEC_ONE_SEC 1000000000ULL +#define TESTLEN 9 + +static long long test_clock(int mode, int cycles_per_usec, int fast_test, + int quiet, int delta_ticks, int delta_nsec) +{ + int i; + long long delta; + unsigned long long max_ticks; + unsigned long long nsecs; + void *mult; + unsigned long long test_ns[TESTLEN] = + {NSEC_ONE_SEC, NSEC_ONE_SEC, + NSEC_ONE_SEC, NSEC_ONE_SEC*60, NSEC_ONE_SEC*60*60, + NSEC_ONE_SEC*60*60*2, NSEC_ONE_SEC*60*60*4, + NSEC_ONE_SEC*60*60*8, NSEC_ONE_SEC*60*60*24}; + unsigned long long test_ticks[TESTLEN]; + + max_ticks = MAX_CLOCK_SEC * (unsigned long long) cycles_per_usec * 1000000ULL; + + switch(mode) { + case CLOCK64_MULT_SHIFT: + mult = &clock_mult; + break; + case CLOCK64_EMULATE_128: + mult = clock_mult64_128; + break; + case CLOCK64_2STAGE: + mult = &clock_mult; + break; + case CLOCK64_LOCK: + mult = &clock_mult; + break; + case CLOCK128_MULT_SHIFT: + mult = &clock_mult128; + break; + default: + assert(0); + } + calc_mult_shift(mode, mult, &clock_shift, MAX_CLOCK_SEC, cycles_per_usec); + nsecs = get_nsec(mode, max_ticks); + delta = nsecs/1000000 - MAX_CLOCK_SEC*1000; + + if (mode == CLOCK64_2STAGE) { + test_ns[0] = nsecs_for_max_cycles - 1; + test_ns[1] = nsecs_for_max_cycles; + test_ticks[0] = (1ULL << max_cycles_shift) - 1; + test_ticks[1] = (1ULL << max_cycles_shift); + + for (i = 2; i < TESTLEN; i++) + test_ticks[i] = test_ns[i] / 1000 * cycles_per_usec; + } + else { + for (i = 0; i < TESTLEN; i++) + test_ticks[i] = test_ns[i] / 1000 * cycles_per_usec; + } + + if (!quiet) { + printf("cycles_per_usec=%d, delta_ticks=%d, delta_nsec=%d, max_ticks=%llu, shift=%u, 2^shift=%llu\n", + cycles_per_usec, delta_ticks, delta_nsec, max_ticks, clock_shift, (1ULL << clock_shift)); + switch(mode) { + case CLOCK64_LOCK: + case CLOCK64_2STAGE: + case CLOCK64_MULT_SHIFT: { + printf("clock_mult=%llu, clock_mult / 2^clock_shift=%f\n", + clock_mult, (double) clock_mult / (1ULL << clock_shift)); + break; + } + case CLOCK64_EMULATE_128: { + printf("clock_mult=0x%016llx%016llx\n", + clock_mult64_128[1], clock_mult64_128[0]); + break; + } + case CLOCK128_MULT_SHIFT: { + printf("clock_mult=0x%016llx%016llx\n", + (unsigned long long) (clock_mult128 >> 64), + (unsigned long long) clock_mult128); + break; + } + } + printf("get_nsec(max_ticks) = %lluns, should be %lluns, error<=abs(%lld)ms\n", + nsecs, MAX_CLOCK_SEC*1000000000ULL, delta); + } + + for (i = 0; i < TESTLEN; i++) + { + nsecs = get_nsec(mode, test_ticks[i]); + delta = nsecs > test_ns[i] ? nsecs - test_ns[i] : test_ns[i] - nsecs; + if (!quiet || delta > 0) + printf("get_nsec(%llu)=%llu, expected %llu, delta=%llu\n", + test_ticks[i], nsecs, test_ns[i], delta); + } + + if (!fast_test) { + discontinuity(mode, delta_ticks, delta_nsec, max_ticks - LEN + 1, LEN); + discontinuity(mode, delta_ticks, delta_nsec, MIN_TICKS, LEN); + } + + if (!quiet) + printf("\n\n"); + + return delta; +} + +int main(int argc, char *argv[]) +{ + nsecs = malloc(LEN * sizeof(unsigned long long)); + + test_clock(CLOCK64_LOCK, 3333, 1, 0, 0, 0); + test_clock(CLOCK64_LOCK, 1000, 1, 0, 1, 1); + test_clock(CLOCK64_LOCK, 1100, 1, 0, 11, 10); + test_clock(CLOCK64_LOCK, 3000, 1, 0, 3, 1); + test_clock(CLOCK64_LOCK, 3333, 1, 0, 3333, 1000); + test_clock(CLOCK64_LOCK, 3392, 1, 0, 424, 125); + test_clock(CLOCK64_LOCK, 4500, 1, 0, 9, 2); + test_clock(CLOCK64_LOCK, 5000, 1, 0, 5, 1); + + free(nsecs); + return 0; +} diff --git a/time.c b/time.c index 279ee48..edfe779 100644 --- a/time.c +++ b/time.c @@ -3,23 +3,23 @@ #include "fio.h" -static struct timeval genesis; +static struct timespec genesis; static unsigned long ns_granularity; -void timeval_add_msec(struct timeval *tv, unsigned int msec) +void timespec_add_msec(struct timespec *ts, unsigned int msec) { - unsigned long adj_usec = 1000 * msec; + unsigned long adj_nsec = 1000000 * msec; - tv->tv_usec += adj_usec; - if (adj_usec >= 1000000) { - unsigned long adj_sec = adj_usec / 1000000; + ts->tv_nsec += adj_nsec; + if (adj_nsec >= 1000000000) { + unsigned long adj_sec = adj_nsec / 1000000000UL; - tv->tv_usec -= adj_sec * 1000000; - tv->tv_sec += adj_sec; + ts->tv_nsec -= adj_sec * 1000000000UL; + ts->tv_sec += adj_sec; } - if (tv->tv_usec >= 1000000){ - tv->tv_usec -= 1000000; - tv->tv_sec++; + if (ts->tv_nsec >= 1000000000UL){ + ts->tv_nsec -= 1000000000UL; + ts->tv_sec++; } } @@ -28,7 +28,7 @@ void timeval_add_msec(struct timeval *tv, unsigned int msec) */ uint64_t usec_spin(unsigned int usec) { - struct timeval start; + struct timespec start; uint64_t t; fio_gettime(&start, NULL); @@ -41,7 +41,7 @@ uint64_t usec_spin(unsigned int usec) uint64_t usec_sleep(struct thread_data *td, unsigned long usec) { struct timespec req; - struct timeval tv; + struct timespec tv; uint64_t t = 0; do { @@ -111,13 +111,10 @@ static void parent_update_ramp(struct thread_data *td) bool ramp_time_over(struct thread_data *td) { - struct timeval tv; - if (!td->o.ramp_time || td->ramp_time_over) return true; - fio_gettime(&tv, NULL); - if (utime_since(&td->epoch, &tv) >= td->o.ramp_time) { + if (utime_since_now(&td->epoch) >= td->o.ramp_time) { td->ramp_time_over = 1; reset_all_stats(td); td_set_runstate(td, TD_RAMP); @@ -138,8 +135,7 @@ void fio_time_init(void) * Check the granularity of the nanosleep function */ for (i = 0; i < 10; i++) { - struct timeval tv; - struct timespec ts; + struct timespec tv, ts; unsigned long elapsed; fio_gettime(&tv, NULL); @@ -170,7 +166,7 @@ void set_epoch_time(struct thread_data *td, int log_unix_epoch) } } -void fill_start_time(struct timeval *t) +void fill_start_time(struct timespec *t) { memcpy(t, &genesis, sizeof(genesis)); } diff --git a/tools/hist/fiologparser_hist.py b/tools/hist/fiologparser_hist.py index ead5e54..ad97a54 100755 --- a/tools/hist/fiologparser_hist.py +++ b/tools/hist/fiologparser_hist.py @@ -373,7 +373,7 @@ if __name__ == '__main__': help='print warning messages to stderr') arg('--group_nr', - default=19, + default=29, type=int, help='FIO_IO_U_PLAT_GROUP_NR as defined in stat.h') diff --git a/verify.c b/verify.c index 1c39fa2..ffd8707 100644 --- a/verify.c +++ b/verify.c @@ -1167,7 +1167,7 @@ static void __fill_hdr(struct thread_data *td, struct io_u *io_u, hdr->rand_seed = rand_seed; hdr->offset = io_u->offset + header_num * td->o.verify_interval; hdr->time_sec = io_u->start_time.tv_sec; - hdr->time_usec = io_u->start_time.tv_usec; + hdr->time_usec = io_u->start_time.tv_nsec / 1000; hdr->thread = td->thread_number; hdr->numberio = io_u->numberio; hdr->crc32 = fio_crc32c(p, offsetof(struct verify_header, crc32)); -- 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