net: tcp: Don't increment snd.nxt
[akaros.git] / kern / src / time.c
index 9364ca0..2515803 100644 (file)
@@ -5,13 +5,14 @@
 #include <multiboot.h>
 #include <pmap.h>
 #include <smp.h>
+#include <ros/procinfo.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() 
+static void train_timing(void)
 {
        uint64_t min_overhead = UINT64_MAX;
        uint64_t max_overhead = 0;
@@ -20,7 +21,7 @@ void train_timing()
 
        /* 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;
+       __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();
@@ -37,84 +38,88 @@ void train_timing()
                max_overhead = MAX(max_overhead, diff);
        }
        enable_irqsave(&irq_state);
-       system_timing.timing_overhead = min_overhead;
+       __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);
 }