/* * Clock functions */ #include #include #include "fio.h" #include "smalloc.h" #include "hash.h" static int clock_gettime_works; static struct timeval last_tv; static int last_tv_valid; static struct timeval *fio_tv; int fio_gtod_offload = 0; int fio_gtod_cpu = -1; #ifdef FIO_DEBUG_TIME #define HASH_BITS 8 #define HASH_SIZE (1 << HASH_BITS) static struct flist_head hash[HASH_SIZE]; static int gtod_inited; struct gtod_log { struct flist_head list; void *caller; unsigned long calls; }; static struct gtod_log *find_hash(void *caller) { unsigned long h = hash_ptr(caller, HASH_BITS); struct flist_head *entry; flist_for_each(entry, &hash[h]) { struct gtod_log *log = flist_entry(entry, struct gtod_log, list); if (log->caller == caller) return log; } return NULL; } static struct gtod_log *find_log(void *caller) { struct gtod_log *log = find_hash(caller); if (!log) { unsigned long h; log = malloc(sizeof(*log)); INIT_FLIST_HEAD(&log->list); log->caller = caller; log->calls = 0; h = hash_ptr(caller, HASH_BITS); flist_add_tail(&log->list, &hash[h]); } return log; } static void gtod_log_caller(void *caller) { if (gtod_inited) { struct gtod_log *log = find_log(caller); log->calls++; } } static void fio_exit fio_dump_gtod(void) { unsigned long total_calls = 0; int i; for (i = 0; i < HASH_SIZE; i++) { struct flist_head *entry; struct gtod_log *log; flist_for_each(entry, &hash[i]) { log = flist_entry(entry, struct gtod_log, list); printf("function %p, calls %lu\n", log->caller, log->calls); total_calls += log->calls; } } printf("Total %lu gettimeofday\n", total_calls); } static void fio_init gtod_init(void) { int i; for (i = 0; i < HASH_SIZE; i++) INIT_FLIST_HEAD(&hash[i]); gtod_inited = 1; } #endif /* FIO_DEBUG_TIME */ #ifdef FIO_DEBUG_TIME void fio_gettime(struct timeval *tp, void *caller) #else void fio_gettime(struct timeval *tp, void fio_unused *caller) #endif { #ifdef FIO_DEBUG_TIME if (!caller) caller = __builtin_return_address(0); gtod_log_caller(caller); #endif if (fio_tv) { memcpy(tp, fio_tv, sizeof(*tp)); return; } else if (!clock_gettime_works) { gtod: gettimeofday(tp, NULL); } else { struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) { clock_gettime_works = 0; goto gtod; } tp->tv_sec = ts.tv_sec; tp->tv_usec = ts.tv_nsec / 1000; } /* * 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; } last_tv_valid = 1; memcpy(&last_tv, tp, sizeof(*tp)); } void fio_gtod_init(void) { fio_tv = smalloc(sizeof(struct timeval)); assert(fio_tv); } void fio_gtod_update(void) { gettimeofday(fio_tv, NULL); }