blob: f298ba757dbc122eee895a579ddb4db7491fb1ed [file] [log] [blame]
#include <arch/arch.h>
#include <time.h>
#include <stdio.h>
#include <schedule.h>
#include <multiboot.h>
#include <pmap.h>
#include <smp.h>
/* Determines the overhead of tsc timing. Note the start/stop calls are
* inlined, so we're trying to determine the lowest amount of overhead
* attainable by using the TSC (or whatever timing source).
*
* For more detailed TSC measurements, use test_rdtsc() in k/a/i/rdtsc_test.c */
void train_timing()
{
uint64_t min_overhead = UINT64_MAX;
uint64_t max_overhead = 0;
uint64_t time, diff;
int8_t irq_state = 0;
/* Reset this, in case we run it again. The use of start/stop to determine
* the overhead relies on timing_overhead being 0. */
system_timing.timing_overhead = 0;
/* timing might use cpuid, in which case we warm it up to avoid some extra
* variance */
time = start_timing();
diff = stop_timing(time);
time = start_timing();
diff = stop_timing(time);
time = start_timing();
diff = stop_timing(time);
disable_irqsave(&irq_state);
for (int i = 0; i < 10000; i++) {
time = start_timing();
diff = stop_timing(time);
min_overhead = MIN(min_overhead, diff);
max_overhead = MAX(max_overhead, diff);
}
enable_irqsave(&irq_state);
system_timing.timing_overhead = min_overhead;
printk("TSC overhead (Min: %llu, Max: %llu)\n", min_overhead, max_overhead);
}
void udelay_sched(uint64_t usec)
{
struct timer_chain *tchain = &per_cpu_info[core_id()].tchain;
struct alarm_waiter a_waiter;
init_awaiter(&a_waiter, 0);
set_awaiter_rel(&a_waiter, usec);
set_alarm(tchain, &a_waiter);
sleep_on_awaiter(&a_waiter);
}
/* Convenience wrapper called when a core's timer interrupt goes off. Not to be
* confused with global timers (like the PIC). Do not put your code here. If
* you want something to happen in the future, set an alarm. */
void timer_interrupt(struct hw_trapframe *hw_tf, void *data)
{
__trigger_tchain(&per_cpu_info[core_id()].tchain, hw_tf);
}
/* We can overflow/wraparound when we multiply up, but we have to divide last,
* or else we lose precision. If we're too big and will overflow, we'll
* sacrifice precision for correctness, and degrade to the next lower level
* (losing 3 digits worth). The recursive case shouldn't overflow, since it
* called something that scaled down the tsc_time by more than 1000. */
uint64_t tsc2sec(uint64_t tsc_time)
{
return tsc_time / system_timing.tsc_freq;
}
uint64_t tsc2msec(uint64_t tsc_time)
{
if (mult_will_overflow_u64(tsc_time, 1000))
return tsc2sec(tsc_time) * 1000;
else
return (tsc_time * 1000) / system_timing.tsc_freq;
}
uint64_t tsc2usec(uint64_t tsc_time)
{
if (mult_will_overflow_u64(tsc_time, 1000000))
return tsc2msec(tsc_time) * 1000;
else
return (tsc_time * 1000000) / system_timing.tsc_freq;
}
uint64_t tsc2nsec(uint64_t tsc_time)
{
if (mult_will_overflow_u64(tsc_time, 1000000000))
return tsc2usec(tsc_time) * 1000;
else
return (tsc_time * 1000000000) / system_timing.tsc_freq;
}
uint64_t sec2tsc(uint64_t sec)
{
if (mult_will_overflow_u64(sec, system_timing.tsc_freq)) {
/* in this case, we simply can't express the number of ticks */
warn("Wraparound in sec2tsc(), rounding up");
return (uint64_t)(-1);
} else {
return sec * system_timing.tsc_freq;
}
}
uint64_t msec2tsc(uint64_t msec)
{
if (mult_will_overflow_u64(msec, system_timing.tsc_freq))
return sec2tsc(msec / 1000);
else
return (msec * system_timing.tsc_freq) / 1000;
}
uint64_t usec2tsc(uint64_t usec)
{
if (mult_will_overflow_u64(usec, system_timing.tsc_freq))
return msec2tsc(usec / 1000);
else
return (usec * system_timing.tsc_freq) / 1000000;
}
uint64_t nsec2tsc(uint64_t nsec)
{
if (mult_will_overflow_u64(nsec, system_timing.tsc_freq))
return usec2tsc(nsec / 1000);
else
return (nsec * system_timing.tsc_freq) / 1000000000;
}
/* TODO: figure out what epoch time TSC == 0 is and store that as boot_tsc */
static uint64_t boot_sec = 1242129600; /* nanwan's birthday */
uint64_t epoch_tsc(void)
{
return read_tsc() + sec2tsc(boot_sec);
}
uint64_t epoch_sec(void)
{
return tsc2sec(epoch_tsc());
}
uint64_t epoch_msec(void)
{
return tsc2msec(epoch_tsc());
}
uint64_t epoch_usec(void)
{
return tsc2usec(epoch_tsc());
}
uint64_t epoch_nsec(void)
{
return tsc2nsec(epoch_tsc());
}
void tsc2timespec(uint64_t tsc_time, struct timespec *ts)
{
ts->tv_sec = tsc2sec(tsc_time);
/* subtract off everything but the remainder */
tsc_time -= sec2tsc(ts->tv_sec);
ts->tv_nsec = tsc2nsec(tsc_time);
}