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