cpuidle: revise tsc-save/restore to reduce tsc skew between cpus
authorKeir Fraser <keir.fraser@citrix.com>
Fri, 5 Dec 2008 13:03:44 +0000 (13:03 +0000)
committerKeir Fraser <keir.fraser@citrix.com>
Fri, 5 Dec 2008 13:03:44 +0000 (13:03 +0000)
Signed-off-by: Wei Gang <gang.wei@intel.com>
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
xen/arch/x86/acpi/cpu_idle.c
xen/arch/x86/time.c
xen/include/xen/time.h

index 95f8bd4f8fd70c5d3aab11720e30d1c8cfec9402..3f7b16bfdd75b7f9bf804fbacf5a1e50c6d1ee10 100644 (file)
@@ -317,8 +317,6 @@ static void acpi_processor_idle(void)
          * stopped by H/W. Without carefully handling of TSC/APIC stop issues,
          * deep C state can't work correctly.
          */
-        /* preparing TSC stop */
-        cstate_save_tsc();
         /* preparing APIC stop */
         lapic_timer_off();
 
index 037811fde8efedfdff0a902b07f138419a7a732f..c4dc8f4ae0275eeecf570ece8f579e33452fc85a 100644 (file)
@@ -48,11 +48,9 @@ struct time_scale {
 
 struct cpu_time {
     u64 local_tsc_stamp;
-    u64 cstate_tsc_stamp;
     s_time_t stime_local_stamp;
     s_time_t stime_master_stamp;
     struct time_scale tsc_scale;
-    u64 cstate_plt_count_stamp;
 };
 
 struct platform_timesource {
@@ -151,6 +149,19 @@ static inline u64 scale_delta(u64 delta, struct time_scale *scale)
     return product;
 }
 
+/* Compute the reciprocal of the given time_scale. */
+static inline struct time_scale scale_reciprocal(struct time_scale scale)
+{
+    u32 q, r;
+
+    asm (
+        "divl %4"
+        : "=a" (q), "=d" (r)
+        : "0" (1), "1" (0), "r" (scale.mul_frac) );
+
+    return (struct time_scale) { .shift = -scale.shift, .mul_frac = q };
+}
+
 /*
  * cpu_mask that denotes the CPUs that needs timer interrupt coming in as
  * IPIs in place of local APIC timers
@@ -644,29 +655,23 @@ static void init_platform_timer(void)
            freq_string(pts->frequency), pts->name);
 }
 
-void cstate_save_tsc(void)
+void cstate_restore_tsc(void)
 {
     struct cpu_time *t = &this_cpu(cpu_time);
+    struct time_scale sys_to_tsc = scale_reciprocal(t->tsc_scale);
+    s_time_t stime_delta;
+    u64 tsc_delta;
 
     if ( tsc_invariant )
         return;
 
-    t->cstate_plt_count_stamp = plt_src.read_counter();
-    rdtscll(t->cstate_tsc_stamp);
-}
+    stime_delta = read_platform_stime() - t->stime_master_stamp;
+    if ( stime_delta < 0 )
+        stime_delta = 0;
 
-void cstate_restore_tsc(void)
-{
-    struct cpu_time *t = &this_cpu(cpu_time);
-    u64 plt_count_delta, tsc_delta;
-
-    if ( tsc_invariant )
-        return;
+    tsc_delta = scale_delta(stime_delta, &sys_to_tsc);
 
-    plt_count_delta = (plt_src.read_counter() -
-                       t->cstate_plt_count_stamp) & plt_mask;
-    tsc_delta = scale_delta(plt_count_delta, &plt_scale) * cpu_khz/1000000UL;
-    wrmsrl(MSR_IA32_TSC, t->cstate_tsc_stamp + tsc_delta);
+    wrmsrl(MSR_IA32_TSC, t->local_tsc_stamp + tsc_delta);
 }
 
 /***************************************************************************
index a74b69224ab79fb5d1f06a39ca0648c9b7c67256..38ead9275526f039437cc073cce8f3de6652dc74 100644 (file)
@@ -13,7 +13,6 @@
 #include <asm/time.h>
 
 extern int init_xen_time(void);
-extern void cstate_save_tsc(void);
 extern void cstate_restore_tsc(void);
 
 extern unsigned long cpu_khz;