net: Add network_offset to blocks
[akaros.git] / kern / arch / x86 / rdtsc_test.c
index fe733ac..f2f0308 100644 (file)
  * min is less than 'normal', means that we could get negative numbers for some
  * measurements (the goal is to determine the overhead and subtract that from
  * our total measurement, and if we think the overhead is 32 but was actually 28
- * for a run, we could have issues). 
+ * for a run, we could have issues).
  *
  * But wait, there's more:
- * 
+ *
  * When we add code around (and inside) the measurement, things get even worse:
  * - If we put variable (a volatile btw) = j + i; in the loop, there's no real
  *   change.  I checked cases 1, 4 and 5, 1 being the intel recommended, 4 being
  * the cost of instructions regardless of whether or not the first or last
  * commands are rdtscp (I'm more concerned with the end time call, which is
  * where this hiding may be happening).  Perhaps the pipeline needs to be
- * drained (or something), and it takes a certain amount of time to do so, 
+ * drained (or something), and it takes a certain amount of time to do so,
  * regardless of a few extra instructions squeezed in.  Meaning we can't tell
  * the difference between 0 and a few cycles, and probably a few cycles are
- * 'free' / hidden by the rdtsc call. 
+ * 'free' / hidden by the rdtsc call.
  *
  * Bottom line?  Our measurements are inexact, despite the stable minimum and
  * low variance.  Everything will be +/- our max deviation, as well as
  * within their execution.  rdtsc probably takes its measurement earlier in the
  * instruction (~16-20 cycles/ticks earlier perhaps?), based on the 48->28
  * back-side step and the front-side 28->44 step.
- *             
+ *
  * Anyway, what matters is a relatively stable method without a lot of variance
  * that has a solid floor/min that we can detect at runtime (to run tests on a
  * given machine).  Using rdtscp for the start measurement seems unreliable
 #include <arch/arch.h>
 #include <stdio.h>
 #include <kmalloc.h>
+#include <time.h>
+#include <smp.h>
+#include <ros/procinfo.h>
 
 #define STAT_SIZE_DEF 10000
 #define LOOP_BOUND_DEF 1000
@@ -458,10 +461,10 @@ static inline void filltimes(uint64_t **times, unsigned int loop_bound,
                                                 :
                                                 : "%eax", "%ebx", "%ecx", "%edx");
                        #endif
-                       
+
                        start = ( ((uint64_t)start_high << 32) | start_low );
                        end = ( ((uint64_t)end_high << 32) | end_low );
-                       
+
                        if ( (int64_t)(end - start) < 0) {
                                printk("CRITICAL ERROR IN TAKING THE TIME!!!!!!\n"
                        "loop(%d) stat(%d) start = %llu, end = %llu, "
@@ -518,9 +521,9 @@ int test_rdtsc(unsigned int loop_bound, unsigned int stat_size)
        uint64_t tot_var = 0, max_dev_all = 0, var_of_vars = 0, var_of_mins = 0;
        loop_bound = loop_bound ?: LOOP_BOUND_DEF;
        stat_size = stat_size ?: STAT_SIZE_DEF;
-       
+
        printk("Running rdtsc tests...\n");
-       
+
        times = kmalloc(loop_bound * sizeof(uint64_t*), 0);
        if (!times) {
                printk("unable to allocate memory for times\n");
@@ -536,21 +539,21 @@ int test_rdtsc(unsigned int loop_bound, unsigned int stat_size)
                        return 0;
                }
        }
-       
+
        variances = kmalloc(loop_bound * sizeof(uint64_t), 0);
        if (!variances) {
                printk("unable to allocate memory for variances\n");
                // not bothering to free **times
                return 0;
        }
-       
+
        min_values = kmalloc(loop_bound * sizeof(uint64_t), 0);
        if (!min_values) {
                printk("unable to allocate memory for min_values\n");
                // not bothering to free **times or variances
                return 0;
        }
-       
+
        disable_irqsave(&state);
 
        filltimes(times, loop_bound, stat_size);
@@ -561,7 +564,7 @@ int test_rdtsc(unsigned int loop_bound, unsigned int stat_size)
                max_dev = 0;
                min_time = 0;
                max_time = 0;
-       
+
                for (i = 0; i < stat_size; i++) {
                        if ((min_time == 0) || (min_time > times[j][i]))
                                min_time = times[j][i];
@@ -576,23 +579,23 @@ int test_rdtsc(unsigned int loop_bound, unsigned int stat_size)
                        max_dev_all = max_dev;
                variances[j] = var_calc(times[j], stat_size);
                tot_var += variances[j];
-               
+
                printk("loop_size:%d >>>> variance(cycles): %llu; "
                "max_deviation: %llu; min time: %llu\n", j, variances[j],
                max_dev, min_time);
                prev_min = min_time;
        }
-       
+
        var_of_vars = var_calc(variances, loop_bound);
        var_of_mins = var_calc(min_values, loop_bound);
-       
+
        printk("total number of spurious min values = %d\n", spurious);
        /* is this next one the mean variance, not the total? */
        printk("total variance = %llu\n", (tot_var/loop_bound));
        printk("absolute max deviation = %llu\n", max_dev_all);
        printk("variance of variances = %llu\n", var_of_vars);
        printk("variance of minimum values = %llu\n", var_of_mins);
-       
+
        for (j = 0; j < loop_bound; j++) {
                kfree(times[j]);
        }
@@ -623,7 +626,7 @@ bool check_timing_stability(void)
        /* 2mil detected an SMI about 95% of the time on my nehalem. */
        for (int i = 0; i < 3000000; i++) {
                start = read_tsc_serialized();
-               for (int j = 0; j < 500; j++) 
+               for (int j = 0; j < 500; j++)
                        dummy = j;
                end = read_tsc_serialized();
                if ((int64_t)(end - start) < 0) {
@@ -754,9 +757,86 @@ void test_tsc_cycles(void)
                                      : : : "eax", "cc");
        }
        end = read_tsc_serialized();
-# warning "what happened to system_timing?"
-       end = end - start - 0; //system_timing.timing_overhead;
+       end = end - start - __proc_global_info.tsc_overhead;
        printk("%llu (100,000) ticks passed, run twice to load the icache\n", end);
 
        enable_irqsave(&irq_state);
 }
+
+static inline __attribute__((always_inline))
+uint64_t pmc_cycles(void)
+{
+       unsigned int a = 0, d = 0;
+       int ecx = (1 << 30) + 1;
+
+       asm volatile("lfence; rdpmc" : "=a"(a), "=d"(d) : "c"(ecx));
+       return ((uint64_t)a) | (((uint64_t)d) << 32);
+}
+
+/* Does a basic test for interference.  You should kfunc this, often after
+ * starting the monitor on another core.  You can spam it with ipi_spam().
+ * You'll also need the PMCs to run.  Easiest way is with:
+ * $ perf stat -e cycles sleep 9999999. */
+void interference_test(void)
+{
+       #define THRESHOLD 200
+       uint64_t low_samples[THRESHOLD] = {0};
+       uint64_t deadline = sec2tsc(5); /* assumes TSC and cycles are close */
+       uint64_t start, diff;
+       size_t nr_below_thresh = 0;
+       size_t nr_over_thresh = 0;
+       size_t total = 0;
+       size_t max = 0;
+
+    deadline += pmc_cycles();
+       enable_irq();
+       do {
+               total++;
+               start = pmc_cycles();
+               diff = pmc_cycles() - start;
+               if (diff < COUNT_OF(low_samples))
+                       low_samples[diff]++;
+               max = diff > max ? diff : max;
+               if (diff < THRESHOLD)
+                       nr_below_thresh++;
+               else
+                       nr_over_thresh++;
+               if (!start) {
+                       warn("rdpmc got 0, is perf stat -e cycles running? (aborting)");
+                       break;
+               }
+       } while (start < deadline);
+       disable_irq();
+
+       printk("\nCore %d\n--------\n", core_id());
+       for (int i = 0; i < COUNT_OF(low_samples); i++) {
+               if (low_samples[i])
+                       printk("\t[ %2d ] : %lu\n", i, low_samples[i]);
+       }
+       printk("Total loops %lu, threshold %u\n", total, THRESHOLD);
+       printk("Nr over thresh %lu\n", nr_over_thresh);
+       printk("Nr below thresh %lu\n", nr_below_thresh);
+       printk("Max %lu\n", max);
+}
+
+/* Kfunc this to spam a core with IPIs */
+void ipi_spam(int coreid)
+{
+       for (int i = 0; i < 1000; i++) {
+               send_ipi(coreid, I_POKE_CORE);
+               udelay(1000);
+       }
+}
+
+/* Kfunc this to halt with IRQs off.  Note this doesn't fully work as
+ * advertised.  Keyboard and NIC IRQs still wake it up, but LAPIC timers don't
+ * seem to. */
+void superhalt(void)
+{
+       unsigned int x86_cstate = X86_MWAIT_C2;
+
+       disable_irq();
+       asm volatile("monitor" : : "a"(KERNBASE), "c"(0), "d"(0));
+       asm volatile("mwait" : : "c"(0x0), "a"(x86_cstate) : "memory");
+       printk("Core %d woke from superhalt!\n", core_id());
+}