The following changes since commit 42634d2118b41f26151f5b708e83d40061570653: Merge branch 'master' into gfio (2012-12-14 20:37:21 +0100) are available in the git repository at: git://git.kernel.dk/fio.git gfio Bruce Cran (4): Fix $(CC) override: use system compiler except on HP-UX and Solaris. Create a new Windows installer product GUID for 2.0.12. Consistently indent using tabs in Windows files and remove trailing spaces. Add return statements to arch_init functions for ia64 and ppc. Dan Horák (1): implement get_cpu_clock() for s390/s390x Jens Axboe (33): cpu clock: add independent test for monotonic/sane TSC gettime: locking fix and debug check for identical sequence gettime: include per-cpu clock calibration in cpu clock test Add --cpuclock-test and clocksource= option help gettime: fix race/bug with threads and time keeping gettime: use pthread_{set,get}specific() for TLS gettime: make last_cycles thread local too windowsaio: fix typo Fio 2.0.12 Fixup CPUID for 32-bit x86 Fio 2.0.12.1 Use clock_gettime() for CPU clock calibration Fio 2.0.12.2 Include <stdint.h> before checking __WORDSIZE t/axmap: update tester for lfsr_init() taking a seed argument clock: turn expensive division into multiply + cheap division Move 'tsc_reliable' outside of ARCH_HAVE_CPU_CLOCK gettime: even rounding, don't always round up libaio: use container_of() instead of silly casting zipf/pareto: use size= if given, not always device size Merge branch 'master' of ssh://brick.kernel.dk/data/git/fio zipf/pareto: use min of file size and io size for init Start of transition from unsigned long long to uint64_t Use uintptr_t for engine data time: convert to uint64_t Enable -ffast-math Use variable[] instead of GCC variable[0] syntax server: move fio_net_cmd to the end of fio_net_int_cmd Fix potential null pointer dereference on verify and requeue events Fio 2.0.13 Fix variable type warning Merge branch 'master' into gfio graph: fix bogus used initialized warning FIO-VERSION-GEN | 2 +- HOWTO | 16 ++ Makefile | 8 +- README | 1 + arch/arch-ia64.h | 1 + arch/arch-ppc.h | 1 + arch/arch-s390.h | 18 +++ arch/arch-x86-common.h | 17 +-- arch/arch.h | 2 + backend.c | 7 +- crc/crc32c-intel.c | 2 +- engines/binject.c | 14 +- engines/libaio.c | 4 +- engines/windowsaio.c | 2 +- file.h | 18 ++-- filesetup.c | 6 +- fio.1 | 21 +++ gettime.c | 277 +++++++++++++++++++++++++++++++---- gettime.h | 2 + graph.c | 2 +- init.c | 12 ++- os/windows/install.wxs | 4 +- os/windows/posix/include/dlfcn.h | 2 +- os/windows/posix/include/sys/uio.h | 2 +- os/windows/posix/include/sys/un.h | 2 +- server.h | 6 +- stat.h | 2 +- t/axmap.c | 8 +- time.c | 2 +- time.h | 13 +- 30 files changed, 380 insertions(+), 94 deletions(-) --- Diff of recent changes: diff --git a/FIO-VERSION-GEN b/FIO-VERSION-GEN index 035ddaf..85e4cda 100755 --- a/FIO-VERSION-GEN +++ b/FIO-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=FIO-VERSION-FILE -DEF_VER=fio-2.0.11 +DEF_VER=fio-2.0.13 LF=' ' diff --git a/HOWTO b/HOWTO index 529c967..a1e6590 100644 --- a/HOWTO +++ b/HOWTO @@ -1256,6 +1256,22 @@ percentile_list=float_list Overwrite the default list of percentiles the values of completion latency below which 99.5% and 99.9% of the observed latencies fell, respectively. +clocksource=str Use the given clocksource as the base of timing. The + supported options are: + + gettimeofday gettimeofday(2) + + clock_gettime clock_gettime(2) + + cpu Internal CPU clock source + + cpu is the preferred clocksource if it is reliable, as it + is very fast (and fio is heavy on time calls). Fio will + automatically use this clocksource if it's supported and + considered reliable on the system it is running on, unless + another clocksource is specifically set. For x86/x86-64 CPUs, + this means supporting TSC Invariant. + gtod_reduce=bool Enable all of the gettimeofday() reducing options (disable_clat, disable_slat, disable_bw) plus reduce precision of the timeout somewhat to really shrink diff --git a/Makefile b/Makefile index bc0a2f8..e6e9e8c 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,8 @@ -ifneq ($(origin CC), environment) -CC = $(CROSS_COMPILE)gcc -endif +CC ?= gcc DEBUGFLAGS = -D_FORTIFY_SOURCE=2 -DFIO_INC_DEBUG CPPFLAGS= -D_GNU_SOURCE -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 \ $(DEBUGFLAGS) -OPTFLAGS= -O3 -g $(EXTFLAGS) +OPTFLAGS= -O3 -g -ffast-math $(EXTFLAGS) CFLAGS = -std=gnu99 -Wwrite-strings -Wall $(OPTFLAGS) LIBS = -lm -lz $(EXTLIBS) PROGS = fio @@ -41,6 +39,7 @@ ifeq ($(UNAME), Android) CPPFLAGS += -DFIO_NO_HAVE_SHM_H endif ifeq ($(UNAME), SunOS) + CC = gcc SOURCE += fifo.c lib/strsep.c helpers.c engines/posixaio.c \ engines/solarisaio.c LIBS += -lpthread -ldl -laio -lrt -lnsl -lsocket @@ -63,6 +62,7 @@ ifeq ($(UNAME), AIX) LDFLAGS += -L/opt/freeware/lib -Wl,-blibpath:/opt/freeware/lib:/usr/lib:/lib -Wl,-bmaxdata:0x80000000 endif ifeq ($(UNAME), HP-UX) + CC = gcc SOURCE += fifo.c helpers.c lib/getopt_long.c lib/strsep.c engines/posixaio.c LIBS += -lpthread -ldl -lrt CFLAGS += -D_LARGEFILE64_SOURCE diff --git a/README b/README index ceac385..7c4552d 100644 --- a/README +++ b/README @@ -129,6 +129,7 @@ $ fio --terse-version=type Terse version output format (default 3, or 2 or 4). --version Print version info and exit --help Print this page + --cpuclock-test Perform test/validation of CPU clock --cmdhelp=cmd Print command help, "all" for all of them --enghelp=engine Print ioengine help, or list available ioengines --enghelp=engine,cmd Print help for an ioengine cmd diff --git a/arch/arch-ia64.h b/arch/arch-ia64.h index 8ccbd86..2df55ba 100644 --- a/arch/arch-ia64.h +++ b/arch/arch-ia64.h @@ -47,6 +47,7 @@ extern int tsc_reliable; static inline int arch_init(char *envp[]) { tsc_reliable = 1; + return 0; } #define ARCH_HAVE_FFZ diff --git a/arch/arch-ppc.h b/arch/arch-ppc.h index 0f20375..e73093d 100644 --- a/arch/arch-ppc.h +++ b/arch/arch-ppc.h @@ -63,6 +63,7 @@ extern int tsc_reliable; static inline int arch_init(char *envp[]) { tsc_reliable = 1; + return 0; } #define ARCH_HAVE_FFZ diff --git a/arch/arch-s390.h b/arch/arch-s390.h index fe51791..bcd9163 100644 --- a/arch/arch-s390.h +++ b/arch/arch-s390.h @@ -22,4 +22,22 @@ #define read_barrier() asm volatile("bcr 15,0" : : : "memory") #define write_barrier() asm volatile("bcr 15,0" : : : "memory") +static inline unsigned long long get_cpu_clock(void) +{ + unsigned long long clk; + + __asm__ __volatile__("stck %0" : "=Q" (clk) : : "cc"); + return clk; +} + +#define ARCH_HAVE_INIT +extern int tsc_reliable; +static inline int arch_init(char *envp[]) +{ + tsc_reliable = 1; + return 0; +} + +#define ARCH_HAVE_CPU_CLOCK + #endif diff --git a/arch/arch-x86-common.h b/arch/arch-x86-common.h index 1e62354..d533d22 100644 --- a/arch/arch-x86-common.h +++ b/arch/arch-x86-common.h @@ -4,24 +4,17 @@ static inline void do_cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx) { - unsigned int id = *eax; - - asm("movl %4, %%eax;" - "cpuid;" - "movl %%eax, %0;" - "movl %%ebx, %1;" - "movl %%ecx, %2;" - "movl %%edx, %3;" - : "=r" (*eax), "=r" (*ebx), "=r" (*ecx), "=r" (*edx) - : "r" (id) - : "eax", "ebx", "ecx", "edx"); + asm volatile("cpuid" + : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx) + : "0" (*eax), "2" (*ecx) + : "memory"); } #define ARCH_HAVE_INIT extern int tsc_reliable; static inline int arch_init(char *envp[]) { - unsigned int eax, ebx, ecx, edx; + unsigned int eax, ebx, ecx = 0, edx; /* * Check for TSC diff --git a/arch/arch.h b/arch/arch.h index 3ee5ac4..f6a8e99 100644 --- a/arch/arch.h +++ b/arch/arch.h @@ -1,6 +1,8 @@ #ifndef ARCH_H #define ARCH_H +#include <stdint.h> + #ifdef __WORDSIZE #define BITS_PER_LONG __WORDSIZE #else diff --git a/backend.c b/backend.c index 8bcb7a2..8f16a16 100644 --- a/backend.c +++ b/backend.c @@ -422,6 +422,7 @@ static void do_verify(struct thread_data *td) io_u = NULL; while (!td->terminate) { + enum fio_ddir ddir; int ret2, full; update_tv_cache(td); @@ -456,6 +457,8 @@ static void do_verify(struct thread_data *td) else io_u->end_io = verify_io_u; + ddir = io_u->ddir; + ret = td_io_queue(td, io_u); switch (ret) { case FIO_Q_COMPLETED: @@ -507,7 +510,7 @@ sync_done: break; } - if (break_on_this_error(td, io_u->ddir, &ret)) + if (break_on_this_error(td, ddir, &ret)) break; /* @@ -1013,6 +1016,8 @@ static void *thread_main(void *data) } else td->pid = gettid(); + fio_local_clock_init(td->o.use_thread); + dprint(FD_PROCESS, "jobs pid=%d started\n", (int) td->pid); if (is_backend) diff --git a/crc/crc32c-intel.c b/crc/crc32c-intel.c index 8e1cd58..0b0f193 100644 --- a/crc/crc32c-intel.c +++ b/crc/crc32c-intel.c @@ -81,7 +81,7 @@ uint32_t crc32c_intel(unsigned char const *data, unsigned long length) void crc32c_intel_probe(void) { if (!crc32c_probed) { - unsigned int eax, ebx, ecx, edx; + unsigned int eax, ebx, ecx = 0, edx; eax = 1; diff --git a/engines/binject.c b/engines/binject.c index f78d855..7b8522a 100644 --- a/engines/binject.c +++ b/engines/binject.c @@ -69,7 +69,7 @@ static unsigned int binject_read_commands(struct thread_data *td, void *p, one_more: events = 0; for_each_file(td, f, i) { - bf = (void *) f->engine_data; + bf = (struct binject_file *) f->engine_data; ret = read(bf->fd, p, left * sizeof(struct b_user_cmd)); if (ret < 0) { if (errno == EAGAIN) @@ -104,7 +104,7 @@ static int fio_binject_getevents(struct thread_data *td, unsigned int min, * Fill in the file descriptors */ for_each_file(td, f, i) { - bf = (void *) f->engine_data; + bf = (struct binject_file *) f->engine_data; /* * don't block for min events == 0 @@ -153,7 +153,7 @@ static int fio_binject_getevents(struct thread_data *td, unsigned int min, if (!min) { for_each_file(td, f, i) { - bf = (void *) f->engine_data; + bf = (struct binject_file *) f->engine_data; fcntl(bf->fd, F_SETFL, bd->fd_flags[i]); } } @@ -167,7 +167,7 @@ static int fio_binject_getevents(struct thread_data *td, unsigned int min, static int fio_binject_doio(struct thread_data *td, struct io_u *io_u) { struct b_user_cmd *buc = &io_u->buc; - struct binject_file *bf = (void *) io_u->file->engine_data; + struct binject_file *bf = (struct binject_file *) io_u->file->engine_data; int ret; ret = write(bf->fd, buc, sizeof(*buc)); @@ -181,7 +181,7 @@ static int fio_binject_prep(struct thread_data *td, struct io_u *io_u) { struct binject_data *bd = td->io_ops->data; struct b_user_cmd *buc = &io_u->buc; - struct binject_file *bf = (void *) io_u->file->engine_data; + struct binject_file *bf = (struct binject_file *) io_u->file->engine_data; if (io_u->xfer_buflen & (bf->bs - 1)) { log_err("read/write not sector aligned\n"); @@ -323,7 +323,7 @@ err_unmap: static int fio_binject_close_file(struct thread_data *td, struct fio_file *f) { - struct binject_file *bf = (void *) f->engine_data; + struct binject_file *bf = (struct binject_file *) f->engine_data; if (bf) { binject_unmap_dev(td, bf); @@ -357,7 +357,7 @@ static int fio_binject_open_file(struct thread_data *td, struct fio_file *f) bf = malloc(sizeof(*bf)); bf->bs = bs; bf->minor = bf->fd = -1; - f->engine_data = (uint64_t) bf; + f->engine_data = (uintptr_t) bf; if (binject_map_dev(td, bf, f->fd)) { err_close: diff --git a/engines/libaio.c b/engines/libaio.c index 748233c..e9ce0ce 100644 --- a/engines/libaio.c +++ b/engines/libaio.c @@ -14,8 +14,6 @@ #ifdef FIO_HAVE_LIBAIO -#define ev_to_iou(ev) (struct io_u *) ((unsigned long) (ev)->obj) - struct libaio_data { io_context_t aio_ctx; struct io_event *aio_events; @@ -64,7 +62,7 @@ static struct io_u *fio_libaio_event(struct thread_data *td, int event) struct io_u *io_u; ev = ld->aio_events + event; - io_u = ev_to_iou(ev); + io_u = container_of(ev->obj, struct io_u, iocb); if (ev->res != io_u->xfer_buflen) { if (ev->res > io_u->xfer_buflen) diff --git a/engines/windowsaio.c b/engines/windowsaio.c index edc390c..f1b0bc5 100644 --- a/engines/windowsaio.c +++ b/engines/windowsaio.c @@ -504,7 +504,7 @@ static int fio_windowsaio_io_u_init(struct thread_data *td, struct io_u *io_u) struct fio_overlapped *o; o = malloc(sizeof(*o)); - o->io_complete = FALSE: + o->io_complete = FALSE; o->io_u = io_u; o->o.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (!o->o.hEvent) { diff --git a/file.h b/file.h index e57bebc..5f125c3 100644 --- a/file.h +++ b/file.h @@ -84,20 +84,20 @@ struct fio_file { /* * size of the file, offset into file, and io size from that offset */ - unsigned long long real_file_size; - unsigned long long file_offset; - unsigned long long io_size; + uint64_t real_file_size; + uint64_t file_offset; + uint64_t io_size; - unsigned long long last_pos; - unsigned long long last_start; + uint64_t last_pos; + uint64_t last_start; - unsigned long long first_write; - unsigned long long last_write; + uint64_t first_write; + uint64_t last_write; /* * For use by the io engine */ - uint64_t engine_data; + uintptr_t engine_data; /* * if io is protected by a semaphore, this is set @@ -154,7 +154,7 @@ FILE_FLAG_FNS(partial_mmap); struct thread_data; extern void close_files(struct thread_data *); extern void close_and_free_files(struct thread_data *); -extern unsigned long long get_start_offset(struct thread_data *); +extern uint64_t get_start_offset(struct thread_data *); extern int __must_check setup_files(struct thread_data *); extern int __must_check file_invalidate_cache(struct thread_data *, struct fio_file *); extern int __must_check generic_open_file(struct thread_data *, struct fio_file *); diff --git a/filesetup.c b/filesetup.c index 60894c4..0c104ed 100644 --- a/filesetup.c +++ b/filesetup.c @@ -709,7 +709,7 @@ static unsigned long long get_fs_free_counts(struct thread_data *td) return ret; } -unsigned long long get_start_offset(struct thread_data *td) +uint64_t get_start_offset(struct thread_data *td) { return td->o.start_offset + (td->thread_number - 1) * td->o.offset_increment; @@ -907,10 +907,12 @@ static int __init_rand_distribution(struct thread_data *td, struct fio_file *f) { unsigned int range_size, seed; unsigned long nranges; + uint64_t file_size; range_size = min(td->o.min_bs[DDIR_READ], td->o.min_bs[DDIR_WRITE]); + file_size = min(f->real_file_size, f->io_size); - nranges = (f->real_file_size + range_size - 1) / range_size; + nranges = (file_size + range_size - 1) / range_size; seed = jhash(f->file_name, strlen(f->file_name), 0) * td->thread_number; if (!td->o.rand_repeatable) diff --git a/fio.1 b/fio.1 index 62c42c6..7a06fbc 100644 --- a/fio.1 +++ b/fio.1 @@ -1027,6 +1027,27 @@ given time in milliseconds. .BI disk_util \fR=\fPbool Generate disk utilization statistics if the platform supports it. Default: true. .TP +.BI clocksource \fR=\fPstr +Use the given clocksource as the base of timing. The supported options are: +.RS +.TP +.B gettimeofday +gettimeofday(2) +.TP +.B clock_gettime +clock_gettime(2) +.TP +.B cpu +Internal CPU clock source +.TP +.RE +.P +\fBcpu\fR is the preferred clocksource if it is reliable, as it is very fast +(and fio is heavy on time calls). Fio will automatically use this clocksource +if it's supported and considered reliable on the system it is running on, +unless another clocksource is specifically set. For x86/x86-64 CPUs, this +means supporting TSC Invariant. +.TP .BI gtod_reduce \fR=\fPbool Enable all of the gettimeofday() reducing options (disable_clat, disable_slat, disable_bw) plus reduce precision of the timeout somewhat to really shrink the diff --git a/gettime.c b/gettime.c index 9f23e3f..1648b17 100644 --- a/gettime.c +++ b/gettime.c @@ -11,14 +11,20 @@ #include "smalloc.h" #include "hash.h" +#include "os/os.h" #ifdef ARCH_HAVE_CPU_CLOCK static unsigned long cycles_per_usec; -static unsigned long last_cycles; -int tsc_reliable = 0; +static unsigned long inv_cycles_per_usec; #endif -static struct timeval last_tv; -static int last_tv_valid; +int tsc_reliable = 0; + +struct tv_valid { + struct timeval last_tv; + int last_tv_valid; + unsigned long last_cycles; +}; +static pthread_key_t tv_tls_key; enum fio_cs fio_clock_source = FIO_PREFERRED_CLOCK_SOURCE; int fio_clock_source_set = 0; @@ -115,12 +121,23 @@ static void fio_init gtod_init(void) #endif /* FIO_DEBUG_TIME */ +static int fill_clock_gettime(struct timespec *ts) +{ +#ifdef FIO_HAVE_CLOCK_MONOTONIC + return clock_gettime(CLOCK_MONOTONIC, ts); +#else + return clock_gettime(CLOCK_REALTIME, ts); +#endif +} + #ifdef FIO_DEBUG_TIME void fio_gettime(struct timeval *tp, void *caller) #else void fio_gettime(struct timeval *tp, void fio_unused *caller) #endif { + struct tv_valid *tv; + #ifdef FIO_DEBUG_TIME if (!caller) caller = __builtin_return_address(0); @@ -132,6 +149,8 @@ void fio_gettime(struct timeval *tp, void fio_unused *caller) return; } + tv = pthread_getspecific(tv_tls_key); + switch (fio_clock_source) { case CS_GTOD: gettimeofday(tp, NULL); @@ -139,11 +158,7 @@ void fio_gettime(struct timeval *tp, void fio_unused *caller) case CS_CGETTIME: { struct timespec ts; -#ifdef FIO_HAVE_CLOCK_MONOTONIC - if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) { -#else - if (clock_gettime(CLOCK_REALTIME, &ts) < 0) { -#endif + if (fill_clock_gettime(&ts) < 0) { log_err("fio: clock_gettime fails\n"); assert(0); } @@ -157,15 +172,15 @@ void fio_gettime(struct timeval *tp, void fio_unused *caller) unsigned long long usecs, t; t = get_cpu_clock(); - if (t < last_cycles) { + if (tv && t < tv->last_cycles) { dprint(FD_TIME, "CPU clock going back in time\n"); - t = last_cycles; - } + t = tv->last_cycles; + } else if (tv) + tv->last_cycles = t; - usecs = t / cycles_per_usec; + usecs = (t * inv_cycles_per_usec) / 16777216UL; tp->tv_sec = usecs / 1000000; tp->tv_usec = usecs % 1000000; - last_cycles = t; break; } #endif @@ -178,29 +193,38 @@ void fio_gettime(struct timeval *tp, void fio_unused *caller) * If Linux is using the tsc clock on non-synced processors, * sometimes time can appear to drift backwards. Fix that up. */ - if (last_tv_valid) { - if (tp->tv_sec < last_tv.tv_sec) - tp->tv_sec = last_tv.tv_sec; - else if (last_tv.tv_sec == tp->tv_sec && - tp->tv_usec < last_tv.tv_usec) - tp->tv_usec = last_tv.tv_usec; + if (tv) { + if (tv->last_tv_valid) { + if (tp->tv_sec < tv->last_tv.tv_sec) + tp->tv_sec = tv->last_tv.tv_sec; + else if (tv->last_tv.tv_sec == tp->tv_sec && + tp->tv_usec < tv->last_tv.tv_usec) + tp->tv_usec = tv->last_tv.tv_usec; + } + tv->last_tv_valid = 1; + memcpy(&tv->last_tv, tp, sizeof(*tp)); } - last_tv_valid = 1; - memcpy(&last_tv, tp, sizeof(*tp)); } #ifdef ARCH_HAVE_CPU_CLOCK static unsigned long get_cycles_per_usec(void) { + struct timespec ts; struct timeval s, e; unsigned long long c_s, c_e; - gettimeofday(&s, NULL); + fill_clock_gettime(&ts); + s.tv_sec = ts.tv_sec; + s.tv_usec = ts.tv_nsec / 1000; + c_s = get_cpu_clock(); do { unsigned long long elapsed; - gettimeofday(&e, NULL); + fill_clock_gettime(&ts); + e.tv_sec = ts.tv_sec; + e.tv_usec = ts.tv_nsec / 1000; + elapsed = utime_since(&s, &e); if (elapsed >= 1280) { c_e = get_cpu_clock(); @@ -249,11 +273,13 @@ static void calibrate_cpu_clock(void) dprint(FD_TIME, "cycles[%d]=%lu\n", i, cycles[i] / 10); avg /= samples; - avg = (avg + 9) / 10; + avg = (avg + 5) / 10; dprint(FD_TIME, "avg: %lu\n", avg); dprint(FD_TIME, "mean=%f, S=%f\n", mean, S); cycles_per_usec = avg; + inv_cycles_per_usec = 16777216UL / cycles_per_usec; + dprint(FD_TIME, "inv_cycles_per_usec=%lu\n", inv_cycles_per_usec); } #else static void calibrate_cpu_clock(void) @@ -261,12 +287,28 @@ static void calibrate_cpu_clock(void) } #endif +void fio_local_clock_init(int is_thread) +{ + struct tv_valid *t; + + t = calloc(sizeof(*t), 1); + if (pthread_setspecific(tv_tls_key, t)) + log_err("fio: can't set TLS key\n"); +} + +static void kill_tv_tls_key(void *data) +{ + free(data); +} + void fio_clock_init(void) { if (fio_clock_source == fio_clock_source_inited) return; - last_tv_valid = 0; + if (pthread_key_create(&tv_tls_key, kill_tv_tls_key)) + log_err("fio: can't create TLS key\n"); + fio_clock_source_inited = fio_clock_source; calibrate_cpu_clock(); @@ -282,10 +324,10 @@ void fio_clock_init(void) log_info("fio: clocksource=cpu may not be reliable\n"); } -unsigned long long utime_since(struct timeval *s, struct timeval *e) +uint64_t utime_since(struct timeval *s, struct timeval *e) { long sec, usec; - unsigned long long ret; + uint64_t ret; sec = e->tv_sec - s->tv_sec; usec = e->tv_usec - s->tv_usec; @@ -305,7 +347,7 @@ unsigned long long utime_since(struct timeval *s, struct timeval *e) return ret; } -unsigned long long utime_since_now(struct timeval *s) +uint64_t utime_since_now(struct timeval *s) { struct timeval t; @@ -313,7 +355,7 @@ unsigned long long utime_since_now(struct timeval *s) return utime_since(s, &t); } -unsigned long mtime_since(struct timeval *s, struct timeval *e) +uint64_t mtime_since(struct timeval *s, struct timeval *e) { long sec, usec, ret; @@ -334,7 +376,7 @@ unsigned long mtime_since(struct timeval *s, struct timeval *e) return ret; } -unsigned long mtime_since_now(struct timeval *s) +uint64_t mtime_since_now(struct timeval *s) { struct timeval t; void *p = __builtin_return_address(0); @@ -343,7 +385,178 @@ unsigned long mtime_since_now(struct timeval *s) return mtime_since(s, &t); } -unsigned long time_since_now(struct timeval *s) +uint64_t time_since_now(struct timeval *s) { return mtime_since_now(s) / 1000; } + +#if defined(FIO_HAVE_CPU_AFFINITY) && defined(ARCH_HAVE_CPU_CLOCK) + +#define CLOCK_ENTRIES 100000 + +struct clock_entry { + unsigned long seq; + unsigned long tsc; + unsigned long cpu; +}; + +struct clock_thread { + pthread_t thread; + int cpu; + pthread_mutex_t lock; + pthread_mutex_t started; + uint64_t *seq; + struct clock_entry *entries; +}; + +static inline uint64_t atomic64_inc_return(uint64_t *seq) +{ + return 1 + __sync_fetch_and_add(seq, 1); +} + +static void *clock_thread_fn(void *data) +{ + struct clock_thread *t = data; + struct clock_entry *c; + os_cpu_mask_t cpu_mask; + int i; + + memset(&cpu_mask, 0, sizeof(cpu_mask)); + fio_cpu_set(&cpu_mask, t->cpu); + + if (fio_setaffinity(gettid(), cpu_mask) == -1) { + log_err("clock setaffinity failed\n"); + return (void *) 1; + } + + pthread_mutex_lock(&t->lock); + pthread_mutex_unlock(&t->started); + + c = &t->entries[0]; + for (i = 0; i < CLOCK_ENTRIES; i++, c++) { + uint64_t seq, tsc; + + c->cpu = t->cpu; + do { + seq = atomic64_inc_return(t->seq); + tsc = get_cpu_clock(); + } while (seq != *t->seq); + + c->seq = seq; + c->tsc = tsc; + } + + log_info("cs: cpu%3d: %lu clocks seen\n", t->cpu, t->entries[CLOCK_ENTRIES - 1].tsc - t->entries[0].tsc); + return NULL; +} + +static int clock_cmp(const void *p1, const void *p2) +{ + const struct clock_entry *c1 = p1; + const struct clock_entry *c2 = p2; + + if (c1->seq == c2->seq) + log_err("cs: bug in atomic sequence!\n"); + + return c1->seq - c2->seq; +} + +int fio_monotonic_clocktest(void) +{ + struct clock_thread *threads; + unsigned int nr_cpus = cpus_online(); + struct clock_entry *entries; + unsigned long tentries, failed; + uint64_t seq = 0; + int i; + + fio_debug |= 1U << FD_TIME; + calibrate_cpu_clock(); + fio_debug &= ~(1U << FD_TIME); + + threads = malloc(nr_cpus * sizeof(struct clock_thread)); + tentries = CLOCK_ENTRIES * nr_cpus; + entries = malloc(tentries * sizeof(struct clock_entry)); + + log_info("cs: Testing %u CPUs\n", nr_cpus); + + for (i = 0; i < nr_cpus; i++) { + struct clock_thread *t = &threads[i]; + + t->cpu = i; + t->seq = &seq; + t->entries = &entries[i * CLOCK_ENTRIES]; + pthread_mutex_init(&t->lock, NULL); + pthread_mutex_init(&t->started, NULL); + pthread_mutex_lock(&t->lock); + pthread_create(&t->thread, NULL, clock_thread_fn, t); + } + + for (i = 0; i < nr_cpus; i++) { + struct clock_thread *t = &threads[i]; + + pthread_mutex_lock(&t->started); + } + + for (i = 0; i < nr_cpus; i++) { + struct clock_thread *t = &threads[i]; + + pthread_mutex_unlock(&t->lock); + } + + for (failed = i = 0; i < nr_cpus; i++) { + struct clock_thread *t = &threads[i]; + void *ret; + + pthread_join(t->thread, &ret); + if (ret) + failed++; + } + free(threads); + + if (failed) { + log_err("Clocksource test: %u threads failed\n", failed); + goto err; + } + + qsort(entries, tentries, sizeof(struct clock_entry), clock_cmp); + + for (failed = i = 0; i < tentries; i++) { + struct clock_entry *prev, *this = &entries[i]; + + if (!i) { + prev = this; + continue; + } + + if (prev->tsc > this->tsc) { + uint64_t diff = prev->tsc - this->tsc; + + log_info("cs: CPU clock mismatch (diff=%lu):\n", diff); + log_info("\t CPU%3lu: TSC=%lu, SEQ=%lu\n", prev->cpu, prev->tsc, prev->seq); + log_info("\t CPU%3lu: TSC=%lu, SEQ=%lu\n", this->cpu, this->tsc, this->seq); + failed++; + } + + prev = this; + } + + if (failed) + log_info("cs: Failed: %lu\n", failed); + else + log_info("cs: Pass!\n"); + +err: + free(entries); + return !!failed; +} + +#else /* defined(FIO_HAVE_CPU_AFFINITY) && defined(ARCH_HAVE_CPU_CLOCK) */ + +int fio_monotonic_clocktest(void) +{ + log_info("cs: current platform does not support CPU clocks\n"); + return 0; +} + +#endif diff --git a/gettime.h b/gettime.h index 64651a1..f0ad20c 100644 --- a/gettime.h +++ b/gettime.h @@ -15,6 +15,8 @@ extern void fio_gettime(struct timeval *, 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(void); +extern void fio_local_clock_init(int); extern struct timeval *fio_tv; diff --git a/graph.c b/graph.c index 7a7c792..5c865dc 100644 --- a/graph.c +++ b/graph.c @@ -180,7 +180,7 @@ static double maxdouble(double a, double b) static double find_double_values(struct graph_label *l, double_comparator cmp) { struct flist_head *entry; - double answer, tmp; + double answer = 0.0, tmp; int first = 1; if (flist_empty(&l->value_list)) diff --git a/init.c b/init.c index 6878322..dc0440e 100644 --- a/init.c +++ b/init.c @@ -207,6 +207,11 @@ static struct option l_opts[FIO_NR_OPTIONS] = { .val = 'C', }, { + .name = (char *) "cpuclock-test", + .has_arg = no_argument, + .val = 'T', + }, + { .name = NULL, }, }; @@ -1255,6 +1260,7 @@ static void usage(const char *name) printf(" --terse-version=x\tSet terse version output format to 'x'\n"); printf(" --version\t\tPrint version info and exit\n"); printf(" --help\t\tPrint this page\n"); + printf(" --cpuclock-test\tPerform test/validation of CPU clock\n"); printf(" --cmdhelp=cmd\t\tPrint command help, \"all\" for all of" " them\n"); printf(" --enghelp=engine\tPrint ioengine help, or list" @@ -1275,7 +1281,7 @@ static void usage(const char *name) printf(" --daemonize=pidfile\tBackground fio server, write pid to file\n"); printf(" --client=hostname\tTalk to remote backend fio server at hostname\n"); printf("\nFio was written by Jens Axboe <jens.axboe@xxxxxxxxxx>"); - printf("\n Jens Axboe <jaxboe@xxxxxxxxxxxx>\n"); + printf("\n Jens Axboe <jaxboe@xxxxxxxxxxxx>\n"); } #ifdef FIO_INC_DEBUG @@ -1663,6 +1669,10 @@ int parse_cmd_line(int argc, char *argv[], int client_type) optind++; } break; + case 'T': + do_exit++; + exit_val = fio_monotonic_clocktest(); + break; default: do_exit++; exit_val = 1; diff --git a/os/windows/install.wxs b/os/windows/install.wxs index 86098b0..5845d5f 100755 --- a/os/windows/install.wxs +++ b/os/windows/install.wxs @@ -7,10 +7,10 @@ <?define ProgramDirectory = ProgramFiles64Folder ?> <?endif?> - <Product Id="C3DC8A4F-1191-412F-8287-ACB6BA798F6A" + <Product Id="F9883688-6AB3-4BD1-AB93-91D39F12F003" Codepage="1252" Language="1033" Manufacturer="fio" Name="fio" - UpgradeCode="2338A332-5511-43cf-b9BD-5C60496CCFCC" Version="2.0.11"> + UpgradeCode="2338A332-5511-43cf-b9BD-5C60496CCFCC" Version="2.0.13"> <Package Comments="Contact: Your local administrator" Description="Flexible IO Tester" diff --git a/os/windows/posix/include/dlfcn.h b/os/windows/posix/include/dlfcn.h index 2953aec..aaffb52 100644 --- a/os/windows/posix/include/dlfcn.h +++ b/os/windows/posix/include/dlfcn.h @@ -1,6 +1,6 @@ #ifndef DLFCN_H #define DLFCN_H - + #define RTLD_LAZY 1 void *dlopen(const char *file, int mode); diff --git a/os/windows/posix/include/sys/uio.h b/os/windows/posix/include/sys/uio.h index 0fac2c0..077550c 100644 --- a/os/windows/posix/include/sys/uio.h +++ b/os/windows/posix/include/sys/uio.h @@ -6,7 +6,7 @@ struct iovec { - void *iov_base; /* Base address of a memory region for input or output */ + void *iov_base; /* Base address of a memory region for input or output */ size_t iov_len; /* The size of the memory pointed to by iov_base */ }; diff --git a/os/windows/posix/include/sys/un.h b/os/windows/posix/include/sys/un.h index ec11805..64e57a5 100644 --- a/os/windows/posix/include/sys/un.h +++ b/os/windows/posix/include/sys/un.h @@ -6,7 +6,7 @@ typedef int in_port_t; struct sockaddr_un { - sa_family_t sun_family; /* Address family */ + sa_family_t sun_family; /* Address family */ char sun_path[]; /* Socket pathname */ }; diff --git a/server.h b/server.h index 201e62d..fa88d24 100644 --- a/server.h +++ b/server.h @@ -27,7 +27,7 @@ struct fio_net_cmd { */ uint16_t cmd_crc16; /* cmd checksum */ uint16_t pdu_crc16; /* payload checksum */ - uint8_t payload[0]; /* payload */ + uint8_t payload[]; /* payload */ }; struct fio_net_cmd_reply { @@ -96,13 +96,13 @@ struct cmd_probe_pdu { struct cmd_single_line_pdu { uint16_t len; - uint8_t text[0]; + uint8_t text[]; }; struct cmd_line_pdu { uint16_t lines; uint16_t client_type; - struct cmd_single_line_pdu options[0]; + struct cmd_single_line_pdu options[]; }; struct cmd_job_pdu { diff --git a/stat.h b/stat.h index 8a1536e..f35f1f6 100644 --- a/stat.h +++ b/stat.h @@ -189,7 +189,7 @@ struct jobs_eta { * Network 'copy' of run_str[] */ uint32_t nr_threads; - uint8_t run_str[0]; + uint8_t run_str[]; }; extern void show_thread_status(struct thread_stat *ts, struct group_run_stats *rs); diff --git a/t/axmap.c b/t/axmap.c index 1f8c3e9..3f6043d 100644 --- a/t/axmap.c +++ b/t/axmap.c @@ -26,13 +26,17 @@ int main(int argc, char *argv[]) struct fio_lfsr lfsr; size_t size = (1UL << 28) - 200; struct axmap *map; + int seed = 1; - if (argc > 1) + if (argc > 1) { size = strtoul(argv[1], NULL, 10); + if (argc > 2) + seed = strtoul(argv[2], NULL, 10); + } printf("Using %llu entries\n", (unsigned long long) size); - lfsr_init(&lfsr, size); + lfsr_init(&lfsr, size, seed); map = axmap_new(size); while (size--) { diff --git a/time.c b/time.c index c4d1d4c..17f9f6f 100644 --- a/time.c +++ b/time.c @@ -53,7 +53,7 @@ void usec_sleep(struct thread_data *td, unsigned long usec) } while (!td->terminate); } -unsigned long mtime_since_genesis(void) +uint64_t mtime_since_genesis(void) { return mtime_since_now(&genesis); } diff --git a/time.h b/time.h index 3824102..d835191 100644 --- a/time.h +++ b/time.h @@ -1,19 +1,18 @@ #ifndef FIO_TIME_H #define FIO_TIME_H -extern unsigned long long utime_since(struct timeval *, struct timeval *); -extern unsigned long long utime_since_now(struct timeval *); -extern unsigned long mtime_since(struct timeval *, struct timeval *); -extern unsigned long mtime_since_now(struct timeval *); -extern unsigned long time_since_now(struct timeval *); -extern unsigned long mtime_since_genesis(void); +extern uint64_t utime_since(struct timeval *, struct timeval *); +extern uint64_t utime_since_now(struct timeval *); +extern uint64_t mtime_since(struct timeval *, struct timeval *); +extern uint64_t mtime_since_now(struct timeval *); +extern uint64_t time_since_now(struct timeval *); +extern uint64_t mtime_since_genesis(void); extern void usec_spin(unsigned int); extern void usec_sleep(struct thread_data *, unsigned long); extern void fill_start_time(struct timeval *); extern void set_genesis_time(void); extern int ramp_time_over(struct thread_data *); extern int in_ramp_time(struct thread_data *); -extern unsigned long long genesis_cycles; extern void fio_time_init(void); #endif -- 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