net: tcp: Don't increment snd.nxt
[akaros.git] / kern / src / time.c
index 1815258..2515803 100644 (file)
 #include <arch/arch.h>
-#include <ros/time.h>
+#include <time.h>
 #include <stdio.h>
 #include <schedule.h>
 #include <multiboot.h>
 #include <pmap.h>
 #include <smp.h>
+#include <ros/procinfo.h>
 
-/* timing_overhead
- * Any user space process that links to this file will get its own copy.  
- * This means it will manually have to call tune_timing itself before it 
- * makes its first measurement.
- */
-uint64_t timing_overhead = 0;
-
-/* start_timing()
- * This function simply reads the tsc in a serialized fashion and returns its
- * value.  It is pusposefully annotated with a noinline so that the overheads 
- * assocaited with calling it are as deterministic as possible.
- */
-uint64_t start_timing()
-{
-    return read_tsc_serialized();
-}
-
-/* stop_timing()
- * This function reads the tsc in a serialized fashion and subtracts the value
- * it reads from the value passed in as a paramter in order to determine the 
- * difference between the two values.  A global timing_overhead value is also 
- * subtracted to compensate for the overhead associated with calling both
- * start and stop timing and returning their values.
- * This function is purposefully annotated with a noinline so that 
- * the overheads assocaited with calling it are as deterministic as possible.
- */
-uint64_t stop_timing(uint64_t val)
-{
-    uint64_t diff = (read_tsc_serialized() - val - timing_overhead);
-       if ((int64_t) diff < 0) 
-               return 1;
-       return diff;
-}
-
-/* train_timing()
- * This function is intended to train the timing_overhead variable for use by
- * stop_timing().  It runs through a loop calling start/stop and averaging the 
- * overhead of calling them without doing any useful work in between.
- */
-void train_timing() 
+/* 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 */
+static void train_timing(void)
 {
-       int i;
-       // set training overhead to be something large
-       register uint64_t training_overhead = 0xffffffff;
-       register uint64_t time, diff;
+       uint64_t min_overhead = UINT64_MAX;
+       uint64_t max_overhead = 0;
+       uint64_t time, diff;
+       int8_t irq_state = 0;
 
-       //Do this 3 times outside the loop to warm up cpuid
+       /* Reset this, in case we run it again.  The use of start/stop to determine
+        * the overhead relies on timing_overhead being 0. */
+       __proc_global_info.tsc_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);
-       for(i=0; i<10000; i++) {
+       disable_irqsave(&irq_state);
+       for (int i = 0; i < 10000; i++) {
                time = start_timing();
                diff = stop_timing(time);
-               
-               /* In case diff was negative, I want to add its absolute value
-                * to the cumulative error, otherwise, just diff itself
-                */
-               if((int64_t)diff < 0)
-                       diff = (uint64_t)(~0) - diff + 1;
-               training_overhead = MIN(training_overhead, diff);
+               min_overhead = MIN(min_overhead, diff);
+               max_overhead = MAX(max_overhead, diff);
        }
-       timing_overhead = training_overhead;
+       enable_irqsave(&irq_state);
+       __proc_global_info.tsc_overhead = min_overhead;
+       printk("TSC overhead (Min: %llu, Max: %llu)\n", min_overhead, max_overhead);
 }
 
 /* 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 trapframe *tf, void *data)
+void timer_interrupt(struct hw_trapframe *hw_tf, void *data)
 {
-       struct timer_chain *pcpui_tchain = &per_cpu_info[core_id()].tchain;
-       trigger_tchain(pcpui_tchain);
+       __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)
+/*
+ * We use scaled integer arithmetic for converting between TSC clock cycles
+ * and nanoseconds. In each case we use a fixed shift value of 32 which
+ * gives a very high degree of accuracy.
+ *
+ * The actual scaling calculations rely on being able use the 128 bit
+ * product of two unsigned 64 bit numbers as an intermediate result
+ * in the calculation. Fortunately, on x86_64 at least, gcc's 128 bit
+ * support is sufficiently good that it generates optimal code for this
+ * calculation without the need to write any assembler.
+ */
+static inline uint64_t mult_shift_64(uint64_t a, uint64_t b, uint8_t shift)
 {
-       return tsc_time / system_timing.tsc_freq;
+       return ((unsigned __int128)a * b) >> shift;
 }
 
-uint64_t tsc2msec(uint64_t tsc_time)
+static uint64_t cycles_to_nsec_mult;
+static uint64_t nsec_to_cycles_mult;
+
+#define CYCLES_TO_NSEC_SHIFT   32
+#define NSEC_TO_CYCLES_SHIFT   32
+
+static void cycles_to_nsec_init(uint64_t tsc_freq_hz)
 {
-       if (mult_will_overflow_u64(tsc_time, 1000))
-               return tsc2sec(tsc_time) * 1000;
-       else 
-               return (tsc_time * 1000) / system_timing.tsc_freq;
+       cycles_to_nsec_mult = (NSEC_PER_SEC << CYCLES_TO_NSEC_SHIFT) / tsc_freq_hz;
 }
 
-uint64_t tsc2usec(uint64_t tsc_time)
+static void nsec_to_cycles_init(uint64_t tsc_freq_hz)
 {
-       if (mult_will_overflow_u64(tsc_time, 1000000))
-               return tsc2msec(tsc_time) * 1000;
-       else
-               return (tsc_time * 1000000) / system_timing.tsc_freq;
+       uint64_t divisor = NSEC_PER_SEC;
+
+       /*
+        * In the unlikely event that the TSC frequency is greater
+        * than (1 << 32) we have to lose a little precision to
+        * avoid overflow in the calculation of the multiplier.
+        */
+       while (tsc_freq_hz >= ((uint64_t)1 << NSEC_TO_CYCLES_SHIFT)) {
+               tsc_freq_hz >>= 1;
+               divisor >>= 1;
+       }
+       nsec_to_cycles_mult = (tsc_freq_hz << NSEC_TO_CYCLES_SHIFT) / divisor;
 }
 
 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;
+       return mult_shift_64(tsc_time, cycles_to_nsec_mult, CYCLES_TO_NSEC_SHIFT);
 }
 
-uint64_t sec2tsc(uint64_t sec)
+uint64_t nsec2tsc(uint64_t nsec)
 {
-       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;
-       }
+       return mult_shift_64(nsec, nsec_to_cycles_mult, NSEC_TO_CYCLES_SHIFT);
 }
 
-uint64_t msec2tsc(uint64_t msec)
+/*
+ * Return nanoseconds since the UNIX epoch, 1st January, 1970.
+ */
+uint64_t epoch_nsec(void)
 {
-       if (mult_will_overflow_u64(msec, system_timing.tsc_freq))
-               return sec2tsc(msec / 1000);
-       else
-               return (msec * system_timing.tsc_freq) / 1000;
-}
+       uint64_t cycles = read_tsc() - __proc_global_info.tsc_cycles_last;
 
-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;
+       return __proc_global_info.walltime_ns_last + tsc2nsec(cycles);
 }
 
-uint64_t nsec2tsc(uint64_t nsec)
+void time_init(void)
 {
-       if (mult_will_overflow_u64(nsec, system_timing.tsc_freq))
-               return usec2tsc(nsec / 1000);
-       else
-               return (nsec * system_timing.tsc_freq) / 1000000000;
+       train_timing();
+
+       __proc_global_info.walltime_ns_last = read_persistent_clock();
+       __proc_global_info.tsc_cycles_last  = read_tsc();
+
+       cycles_to_nsec_init(__proc_global_info.tsc_freq);
+       nsec_to_cycles_init(__proc_global_info.tsc_freq);
 }