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