blob: a124de95fd5226e348a1da2d674bf75d9938d1db [file] [log] [blame]
/* Basic TSC compatability helpers, callable from Akaros or Linux. Supports:
* uint64_t read_tsc()
* uint64_t read_tsc_serialized()
* uint64_t get_tsc_freq()
* uint64_t get_tsc_overhead()
*
* Note this relies on specifics of procinfo, which isn't stable. If procinfo
* changes, this will need to change as well. You'll know when this doesn't
* compile (say, if timing_overhead moves). */
#pragma once
#if defined(__i386__) || defined(__x86_64__)
#else
#error "Platform not supported for read_tsc()"
#endif
__BEGIN_DECLS
#ifdef __ros__
#include <parlib/arch/arch.h>
#include <ros/procinfo.h>
static inline uint64_t get_tsc_freq(void)
{
return __procinfo.tsc_freq;
}
static inline uint64_t get_tsc_overhead(void)
{
return __procinfo.timing_overhead;
}
#else /* ! _ros_ (linux) */
#include <sys/time.h>
#include <stdint.h>
#include <stdbool.h>
/* Akaros has this helper in ros/common.h. (it returns a bool btw)
*
* We wraparound if UINT_MAX < a * b, which is also UINT_MAX / a < b. */
static inline int mult_will_overflow_u64(uint64_t a, uint64_t b)
{
if (!a)
return false;
return (uint64_t)(-1) / a < b;
}
# ifdef __i386__
static inline uint64_t read_tsc(void)
{
uint64_t tsc;
asm volatile("rdtsc" : "=A" (tsc));
return tsc;
}
static inline uint64_t read_tsc_serialized(void)
{
uint64_t tsc;
asm volatile("lfence; rdtsc" : "=A" (tsc));
return tsc;
}
# elif __x86_64__
static inline uint64_t read_tsc(void)
{
uint32_t lo, hi;
/* We cannot use "=A", since this would use %rax on x86_64 */
asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
return (uint64_t)hi << 32 | lo;
}
static inline uint64_t read_tsc_serialized(void)
{
uint32_t lo, hi;
asm volatile("lfence; rdtsc" : "=a" (lo), "=d" (hi));
return (uint64_t)hi << 32 | lo;
}
# else
# error "Which arch is this?"
# endif /* __i386__ | __x86_64__ */
static inline uint64_t get_tsc_freq(void)
{
struct timeval prev;
struct timeval curr;
uint64_t beg = read_tsc_serialized();
gettimeofday(&prev, 0);
while (1) {
gettimeofday(&curr, 0);
if (curr.tv_sec > (prev.tv_sec + 1) ||
(curr.tv_sec > prev.tv_sec && curr.tv_usec >
prev.tv_usec))
break;
}
uint64_t end = read_tsc_serialized();
return end - beg;
}
/* Don't have a good way to get the overhead on Linux in userspace. */
static inline uint64_t get_tsc_overhead(void)
{
return 0;
}
static inline uint64_t tsc2sec(uint64_t tsc_time)
{
return tsc_time / get_tsc_freq();
}
static inline uint64_t tsc2msec(uint64_t tsc_time)
{
if (mult_will_overflow_u64(tsc_time, 1000))
return tsc2sec(tsc_time) * 1000;
else
return (tsc_time * 1000) / get_tsc_freq();
}
static inline uint64_t tsc2usec(uint64_t tsc_time)
{
if (mult_will_overflow_u64(tsc_time, 1000000))
return tsc2msec(tsc_time) * 1000;
else
return (tsc_time * 1000000) / get_tsc_freq();
}
static inline uint64_t tsc2nsec(uint64_t tsc_time)
{
if (mult_will_overflow_u64(tsc_time, 1000000000))
return tsc2usec(tsc_time) * 1000;
else
return (tsc_time * 1000000000) / get_tsc_freq();
}
static inline uint64_t sec2tsc(uint64_t sec)
{
if (mult_will_overflow_u64(sec, get_tsc_freq()))
return (uint64_t)(-1);
else
return sec * get_tsc_freq();
}
static inline uint64_t msec2tsc(uint64_t msec)
{
if (mult_will_overflow_u64(msec, get_tsc_freq()))
return sec2tsc(msec / 1000);
else
return (msec * get_tsc_freq()) / 1000;
}
static inline uint64_t usec2tsc(uint64_t usec)
{
if (mult_will_overflow_u64(usec, get_tsc_freq()))
return msec2tsc(usec / 1000);
else
return (usec * get_tsc_freq()) / 1000000;
}
static inline uint64_t nsec2tsc(uint64_t nsec)
{
if (mult_will_overflow_u64(nsec, get_tsc_freq()))
return usec2tsc(nsec / 1000);
else
return (nsec * get_tsc_freq()) / 1000000000;
}
#endif /* ! _ros_ */
__END_DECLS