Allows no EXT2 block device
[akaros.git] / kern / src / time.c
1 #include <arch/arch.h>
2 #include <ros/time.h>
3 #include <stdio.h>
4 #include <schedule.h>
5 #include <multiboot.h>
6 #include <pmap.h>
7 #include <smp.h>
8
9 /* timing_overhead
10  * Any user space process that links to this file will get its own copy.  
11  * This means it will manually have to call tune_timing itself before it 
12  * makes its first measurement.
13  */
14 uint64_t timing_overhead = 0;
15
16 /* start_timing()
17  * This function simply reads the tsc in a serialized fashion and returns its
18  * value.  It is pusposefully annotated with a noinline so that the overheads 
19  * assocaited with calling it are as deterministic as possible.
20  */
21 uint64_t start_timing()
22 {
23     return read_tsc_serialized();
24 }
25
26 /* stop_timing()
27  * This function reads the tsc in a serialized fashion and subtracts the value
28  * it reads from the value passed in as a paramter in order to determine the 
29  * difference between the two values.  A global timing_overhead value is also 
30  * subtracted to compensate for the overhead associated with calling both
31  * start and stop timing and returning their values.
32  * This function is purposefully annotated with a noinline so that 
33  * the overheads assocaited with calling it are as deterministic as possible.
34  */
35 uint64_t stop_timing(uint64_t val)
36 {
37     uint64_t diff = (read_tsc_serialized() - val - timing_overhead);
38         if ((int64_t) diff < 0) 
39                 return 1;
40         return diff;
41 }
42
43 /* train_timing()
44  * This function is intended to train the timing_overhead variable for use by
45  * stop_timing().  It runs through a loop calling start/stop and averaging the 
46  * overhead of calling them without doing any useful work in between.
47  */
48 void train_timing() 
49 {
50         int i;
51         // set training overhead to be something large
52         register uint64_t training_overhead = 0xffffffff;
53         register uint64_t time, diff;
54
55         //Do this 3 times outside the loop to warm up cpuid
56         time = start_timing();
57         diff = stop_timing(time);
58         time = start_timing();
59         diff = stop_timing(time);
60         time = start_timing();
61         diff = stop_timing(time);
62         for(i=0; i<10000; i++) {
63                 time = start_timing();
64                 diff = stop_timing(time);
65                 
66                 /* In case diff was negative, I want to add its absolute value
67                  * to the cumulative error, otherwise, just diff itself
68                  */
69                 if((int64_t)diff < 0)
70                         diff = (uint64_t)(~0) - diff + 1;
71                 training_overhead = MIN(training_overhead, diff);
72         }
73         timing_overhead = training_overhead;
74 }
75
76 /* Convenience wrapper called when a core's timer interrupt goes off.  Not to be
77  * confused with global timers (like the PIC).  Do not put your code here.  If
78  * you want something to happen in the future, set an alarm. */
79 void timer_interrupt(struct trapframe *tf, void *data)
80 {
81         struct timer_chain *pcpui_tchain = &per_cpu_info[core_id()].tchain;
82         trigger_tchain(pcpui_tchain);
83 }
84
85 /* We can overflow/wraparound when we multiply up, but we have to divide last,
86  * or else we lose precision.  If we're too big and will overflow, we'll
87  * sacrifice precision for correctness, and degrade to the next lower level
88  * (losing 3 digits worth).  The recursive case shouldn't overflow, since it
89  * called something that scaled down the tsc_time by more than 1000. */
90 uint64_t tsc2sec(uint64_t tsc_time)
91 {
92         return tsc_time / system_timing.tsc_freq;
93 }
94
95 uint64_t tsc2msec(uint64_t tsc_time)
96 {
97         if (mult_will_overflow_u64(tsc_time, 1000))
98                 return tsc2sec(tsc_time) * 1000;
99         else 
100                 return (tsc_time * 1000) / system_timing.tsc_freq;
101 }
102
103 uint64_t tsc2usec(uint64_t tsc_time)
104 {
105         if (mult_will_overflow_u64(tsc_time, 1000000))
106                 return tsc2msec(tsc_time) * 1000;
107         else
108                 return (tsc_time * 1000000) / system_timing.tsc_freq;
109 }
110
111 uint64_t tsc2nsec(uint64_t tsc_time)
112 {
113         if (mult_will_overflow_u64(tsc_time, 1000000000))
114                 return tsc2usec(tsc_time) * 1000;
115         else
116                 return (tsc_time * 1000000000) / system_timing.tsc_freq;
117 }
118
119 uint64_t sec2tsc(uint64_t sec)
120 {
121         if (mult_will_overflow_u64(sec, system_timing.tsc_freq)) {
122                 /* in this case, we simply can't express the number of ticks */
123                 warn("Wraparound in sec2tsc(), rounding up");
124                 return (uint64_t)(-1);
125         } else {
126                 return sec * system_timing.tsc_freq;
127         }
128 }
129
130 uint64_t msec2tsc(uint64_t msec)
131 {
132         if (mult_will_overflow_u64(msec, system_timing.tsc_freq))
133                 return sec2tsc(msec / 1000);
134         else
135                 return (msec * system_timing.tsc_freq) / 1000;
136 }
137
138 uint64_t usec2tsc(uint64_t usec)
139 {
140         if (mult_will_overflow_u64(usec, system_timing.tsc_freq))
141                 return msec2tsc(usec / 1000);
142         else
143                 return (usec * system_timing.tsc_freq) / 1000000;
144 }
145
146 uint64_t nsec2tsc(uint64_t nsec)
147 {
148         if (mult_will_overflow_u64(nsec, system_timing.tsc_freq))
149                 return usec2tsc(nsec / 1000);
150         else
151                 return (nsec * system_timing.tsc_freq) / 1000000000;
152 }