parlib/linux: cache the value of the TSC frequency
[akaros.git] / user / parlib / include / parlib / tsc-compat.h
1 /* Basic TSC compatability helpers, callable from Akaros or Linux. Supports:
2  *              uint64_t read_tsc()
3  *              uint64_t read_tsc_serialized()
4  *              uint64_t get_tsc_freq()
5  *              uint64_t get_tsc_overhead()
6  *
7  * Note this relies on specifics of procinfo, which isn't stable.  If procinfo
8  * changes, this will need to change as well.  You'll know when this doesn't
9  * compile (say, if timing_overhead moves).  */
10
11 #pragma once
12
13 #if defined(__i386__) || defined(__x86_64__)
14 #else
15 #error "Platform not supported for read_tsc()"
16 #endif
17
18 __BEGIN_DECLS
19
20 #ifdef __ros__
21
22 #include <parlib/arch/arch.h>
23 #include <ros/procinfo.h>
24
25 static inline uint64_t get_tsc_freq(void)
26 {
27         return __procinfo.tsc_freq;
28 }
29
30 static inline uint64_t get_tsc_overhead(void)
31 {
32         return __procinfo.timing_overhead;
33 }
34
35 #else /* ! _ros_ (linux) */
36
37 #include <sys/time.h>
38 #include <stdint.h>
39 #include <stdbool.h>
40
41 /* Akaros has this helper in ros/common.h. (it returns a bool btw)
42  *
43  * We wraparound if UINT_MAX < a * b, which is also UINT_MAX / a < b. */
44 static inline int mult_will_overflow_u64(uint64_t a, uint64_t b)
45 {
46         if (!a)
47                 return false;
48         return (uint64_t)(-1) / a < b;
49 }
50
51 # ifdef __i386__
52
53 static inline uint64_t read_tsc(void)
54 {
55         uint64_t tsc;
56
57         asm volatile("rdtsc" : "=A" (tsc));
58         return tsc;
59 }
60
61 static inline uint64_t read_tsc_serialized(void)
62 {
63         uint64_t tsc;
64
65         asm volatile("lfence; rdtsc" : "=A" (tsc));
66         return tsc;
67 }
68
69 # elif __x86_64__
70
71 static inline uint64_t read_tsc(void)
72 {
73         uint32_t lo, hi;
74
75         /* We cannot use "=A", since this would use %rax on x86_64 */
76         asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
77         return (uint64_t)hi << 32 | lo;
78 }
79
80 static inline uint64_t read_tsc_serialized(void)
81 {
82         uint32_t lo, hi;
83
84         asm volatile("lfence; rdtsc" : "=a" (lo), "=d" (hi));
85         return (uint64_t)hi << 32 | lo;
86 }
87
88 # else
89 #  error "Which arch is this?"
90 # endif /* __i386__ | __x86_64__ */
91
92 static inline uint64_t get_tsc_freq(void)
93 {
94         static uint64_t freq;
95
96         struct timeval prev;
97         struct timeval curr;
98         uint64_t beg, end;
99
100         if (freq)
101                 return freq;
102
103         beg = read_tsc_serialized();
104
105         gettimeofday(&prev, 0);
106         while (1) {
107                 gettimeofday(&curr, 0);
108                 if (curr.tv_sec > (prev.tv_sec + 1) ||
109                         (curr.tv_sec > prev.tv_sec && curr.tv_usec >
110                          prev.tv_usec))
111                         break;
112         }
113         end = read_tsc_serialized();
114
115         freq = end - beg;
116         return freq;
117 }
118
119 /* Don't have a good way to get the overhead on Linux in userspace. */
120 static inline uint64_t get_tsc_overhead(void)
121 {
122         return 0;
123 }
124
125 static inline uint64_t tsc2sec(uint64_t tsc_time)
126 {
127         return tsc_time / get_tsc_freq();
128 }
129
130 static inline uint64_t tsc2msec(uint64_t tsc_time)
131 {
132         if (mult_will_overflow_u64(tsc_time, 1000))
133                 return tsc2sec(tsc_time) * 1000;
134         else
135                 return (tsc_time * 1000) / get_tsc_freq();
136 }
137
138 static inline uint64_t tsc2usec(uint64_t tsc_time)
139 {
140         if (mult_will_overflow_u64(tsc_time, 1000000))
141                 return tsc2msec(tsc_time) * 1000;
142         else
143                 return (tsc_time * 1000000) / get_tsc_freq();
144 }
145
146 static inline uint64_t tsc2nsec(uint64_t tsc_time)
147 {
148         if (mult_will_overflow_u64(tsc_time, 1000000000))
149                 return tsc2usec(tsc_time) * 1000;
150         else
151                 return (tsc_time * 1000000000) / get_tsc_freq();
152 }
153
154 static inline uint64_t sec2tsc(uint64_t sec)
155 {
156         if (mult_will_overflow_u64(sec, get_tsc_freq()))
157                 return (uint64_t)(-1);
158         else
159                 return sec * get_tsc_freq();
160 }
161
162 static inline uint64_t msec2tsc(uint64_t msec)
163 {
164         if (mult_will_overflow_u64(msec, get_tsc_freq()))
165                 return sec2tsc(msec / 1000);
166         else
167                 return (msec * get_tsc_freq()) / 1000;
168 }
169
170 static inline uint64_t usec2tsc(uint64_t usec)
171 {
172         if (mult_will_overflow_u64(usec, get_tsc_freq()))
173                 return msec2tsc(usec / 1000);
174         else
175                 return (usec * get_tsc_freq()) / 1000000;
176 }
177
178 static inline uint64_t nsec2tsc(uint64_t nsec)
179 {
180         if (mult_will_overflow_u64(nsec, get_tsc_freq()))
181                 return usec2tsc(nsec / 1000);
182         else
183                 return (nsec * get_tsc_freq()) / 1000000000;
184 }
185
186 #endif /* ! _ros_ */
187
188 __END_DECLS