Moved timing parameters into proc_global_info (XCC)
[akaros.git] / kern / src / time.c
1 #include <arch/arch.h>
2 #include <time.h>
3 #include <stdio.h>
4 #include <schedule.h>
5 #include <multiboot.h>
6 #include <pmap.h>
7 #include <smp.h>
8 #include <ros/procinfo.h>
9
10 /* Determines the overhead of tsc timing.  Note the start/stop calls are
11  * inlined, so we're trying to determine the lowest amount of overhead
12  * attainable by using the TSC (or whatever timing source).
13  *
14  * For more detailed TSC measurements, use test_rdtsc() in k/a/i/rdtsc_test.c */
15 void train_timing() 
16 {
17         uint64_t min_overhead = UINT64_MAX;
18         uint64_t max_overhead = 0;
19         uint64_t time, diff;
20         int8_t irq_state = 0;
21
22         /* Reset this, in case we run it again.  The use of start/stop to determine
23          * the overhead relies on timing_overhead being 0. */
24         __proc_global_info.tsc_overhead = 0;
25         /* timing might use cpuid, in which case we warm it up to avoid some extra
26          * variance */
27         time = start_timing();
28         diff = stop_timing(time);
29         time = start_timing();
30         diff = stop_timing(time);
31         time = start_timing();
32         diff = stop_timing(time);
33         disable_irqsave(&irq_state);
34         for (int i = 0; i < 10000; i++) {
35                 time = start_timing();
36                 diff = stop_timing(time);
37                 min_overhead = MIN(min_overhead, diff);
38                 max_overhead = MAX(max_overhead, diff);
39         }
40         enable_irqsave(&irq_state);
41         __proc_global_info.tsc_overhead = min_overhead;
42         printk("TSC overhead (Min: %llu, Max: %llu)\n", min_overhead, max_overhead);
43 }
44
45 /* Convenience wrapper called when a core's timer interrupt goes off.  Not to be
46  * confused with global timers (like the PIC).  Do not put your code here.  If
47  * you want something to happen in the future, set an alarm. */
48 void timer_interrupt(struct hw_trapframe *hw_tf, void *data)
49 {
50         __trigger_tchain(&per_cpu_info[core_id()].tchain, hw_tf);
51 }
52
53 /* We can overflow/wraparound when we multiply up, but we have to divide last,
54  * or else we lose precision.  If we're too big and will overflow, we'll
55  * sacrifice precision for correctness, and degrade to the next lower level
56  * (losing 3 digits worth).  The recursive case shouldn't overflow, since it
57  * called something that scaled down the tsc_time by more than 1000. */
58 uint64_t tsc2sec(uint64_t tsc_time)
59 {
60         return tsc_time / __proc_global_info.tsc_freq;
61 }
62
63 uint64_t tsc2msec(uint64_t tsc_time)
64 {
65         if (mult_will_overflow_u64(tsc_time, 1000))
66                 return tsc2sec(tsc_time) * 1000;
67         else
68                 return (tsc_time * 1000) / __proc_global_info.tsc_freq;
69 }
70
71 uint64_t tsc2usec(uint64_t tsc_time)
72 {
73         if (mult_will_overflow_u64(tsc_time, 1000000))
74                 return tsc2msec(tsc_time) * 1000;
75         else
76                 return (tsc_time * 1000000) / __proc_global_info.tsc_freq;
77 }
78
79 uint64_t tsc2nsec(uint64_t tsc_time)
80 {
81         if (mult_will_overflow_u64(tsc_time, 1000000000))
82                 return tsc2usec(tsc_time) * 1000;
83         else
84                 return (tsc_time * 1000000000) / __proc_global_info.tsc_freq;
85 }
86
87 uint64_t sec2tsc(uint64_t sec)
88 {
89         if (mult_will_overflow_u64(sec, __proc_global_info.tsc_freq)) {
90                 /* in this case, we simply can't express the number of ticks */
91                 warn("Wraparound in sec2tsc(), rounding up");
92                 return (uint64_t)(-1);
93         } else {
94                 return sec * __proc_global_info.tsc_freq;
95         }
96 }
97
98 uint64_t msec2tsc(uint64_t msec)
99 {
100         if (mult_will_overflow_u64(msec, __proc_global_info.tsc_freq))
101                 return sec2tsc(msec / 1000);
102         else
103                 return (msec * __proc_global_info.tsc_freq) / 1000;
104 }
105
106 uint64_t usec2tsc(uint64_t usec)
107 {
108         if (mult_will_overflow_u64(usec, __proc_global_info.tsc_freq))
109                 return msec2tsc(usec / 1000);
110         else
111                 return (usec * __proc_global_info.tsc_freq) / 1000000;
112 }
113
114 uint64_t nsec2tsc(uint64_t nsec)
115 {
116         if (mult_will_overflow_u64(nsec, __proc_global_info.tsc_freq))
117                 return usec2tsc(nsec / 1000);
118         else
119                 return (nsec * __proc_global_info.tsc_freq) / 1000000000;
120 }
121
122 /* TODO: figure out what epoch time TSC == 0 is and store that as boot_tsc */
123 static uint64_t boot_sec = 1242129600; /* nanwan's birthday */
124
125 uint64_t epoch_tsc(void)
126 {
127         return read_tsc() + sec2tsc(boot_sec);
128 }
129
130 uint64_t epoch_sec(void)
131 {
132         return tsc2sec(epoch_tsc());
133 }
134
135 uint64_t epoch_msec(void)
136 {
137         return tsc2msec(epoch_tsc());
138 }
139
140 uint64_t epoch_usec(void)
141 {
142         return tsc2usec(epoch_tsc());
143 }
144
145 uint64_t epoch_nsec(void)
146 {
147         return tsc2nsec(epoch_tsc());
148 }
149
150 void tsc2timespec(uint64_t tsc_time, struct timespec *ts)
151 {
152         ts->tv_sec = tsc2sec(tsc_time);
153         /* subtract off everything but the remainder */
154         tsc_time -= sec2tsc(ts->tv_sec);
155         ts->tv_nsec = tsc2nsec(tsc_time);
156 }