cpuidle: mwait on softirq_pending & remove wakeup ipis
authorKeir Fraser <keir.fraser@citrix.com>
Tue, 6 Apr 2010 05:52:11 +0000 (06:52 +0100)
committerKeir Fraser <keir.fraser@citrix.com>
Tue, 6 Apr 2010 05:52:11 +0000 (06:52 +0100)
For cpu which enter deep C state via monitor/mwait, wakeup can be done
by writing to the monitored memory. So once monitor softirq_pending,
we can remove the redundant ipis.

Signed-off-by: Yu Ke <ke.yu@intel.com>
Signed-off-by: Wei Gang <gang.wei@intel.com>
xen/arch/x86/acpi/cpu_idle.c
xen/arch/x86/hpet.c
xen/include/xen/cpuidle.h

index 51227842e4d719847b005d375eee4a206e7b67ad..bd4d68a14d3a3b6f8c91fdee55d746b1975567e5 100644 (file)
@@ -69,6 +69,14 @@ boolean_param("lapic_timer_c2_ok", local_apic_timer_c2_ok);
 
 static struct acpi_processor_power *__read_mostly processor_powers[NR_CPUS];
 
+static char* acpi_cstate_method_name[] =
+{
+    "NONE",
+    "SYSIO",
+    "FFH",
+    "HALT"
+};
+
 static void print_acpi_power(uint32_t cpu, struct acpi_processor_power *power)
 {
     uint32_t i, idle_usage = 0;
@@ -92,6 +100,7 @@ static void print_acpi_power(uint32_t cpu, struct acpi_processor_power *power)
         printk("type[C%d] ", power->states[i].type);
         printk("latency[%03d] ", power->states[i].latency);
         printk("usage[%08d] ", power->states[i].usage);
+        printk("method[%5s] ", acpi_cstate_method_name[power->states[i].entry_method]);
         printk("duration[%"PRId64"]\n", res);
     }
     printk("    C0:\tusage[%08d] duration[%"PRId64"]\n",
@@ -140,11 +149,43 @@ static void acpi_safe_halt(void)
 
 #define MWAIT_ECX_INTERRUPT_BREAK   (0x1)
 
+/*
+ * The bit is set iff cpu use monitor/mwait to enter C state
+ * with this flag set, CPU can be waken up from C state
+ * by writing to specific memory address, instead of sending IPI
+ */
+static cpumask_t cpuidle_mwait_flags;
+
+void cpuidle_wakeup_mwait(cpumask_t *mask)
+{
+    cpumask_t target;
+    int cpu;
+
+    cpus_and(target, *mask, cpuidle_mwait_flags);
+
+    /* cpu is 'mwait'ing at softirq_pending,
+       writing to it will wake up CPU */
+    for_each_cpu_mask(cpu, target)
+        set_bit(TIMER_SOFTIRQ, &softirq_pending(cpu));
+
+    cpus_andnot(*mask, *mask, cpuidle_mwait_flags);
+}
+
 static void mwait_idle_with_hints(unsigned long eax, unsigned long ecx)
 {
-    __monitor((void *)current, 0, 0);
+    int cpu = smp_processor_id();
+
+    __monitor((void *)&softirq_pending(cpu), 0, 0);
+
     smp_mb();
-    __mwait(eax, ecx);
+    if (!softirq_pending(cpu))
+    {
+        cpu_set(cpu, cpuidle_mwait_flags);
+
+        __mwait(eax, ecx);
+
+        cpu_clear(cpu, cpuidle_mwait_flags);
+    }
 }
 
 static void acpi_processor_ffh_cstate_enter(struct acpi_processor_cx *cx)
index 0e099de8ec98b95435b31fe609d26fb7057cfb31..1cedb1cb3cc2d434b9caf1bee27aa86633503922 100644 (file)
@@ -166,6 +166,8 @@ static int evt_do_broadcast(cpumask_t mask)
         ret = 1;
     }
 
+    cpuidle_wakeup_mwait(&mask);
+
     if ( !cpus_empty(mask) )
     {
        cpumask_raise_softirq(mask, TIMER_SOFTIRQ);
index 4f4a2995ca701fe271509737b62f0fdfb66a549a..1f8c3b5ae47ceb097c9e983893c7c3e376941bf9 100644 (file)
@@ -86,6 +86,8 @@ struct cpuidle_governor
 extern struct cpuidle_governor *cpuidle_current_governor;
 void cpuidle_disable_deep_cstate(void);
 
+extern void cpuidle_wakeup_mwait(cpumask_t *mask);
+
 #define CPUIDLE_DRIVER_STATE_START  1
 
 #endif /* _XEN_CPUIDLE_H */