# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1216028612 -3600
# Node ID 79517ed2a1081120ed4490f34453c6161dc4a38b
# Parent 39c2cab9e765ece9e7958ba08852f2340e7d537b
x86: PIT broadcast to fix local APIC timer stop issue for Deep C state
Local APIC timer may stop at deep C state (C3/C4...) entry. Initial
HPET broadcast working in legacy replacing mode, broke RTC intr, so
was bypassed. This patch add the logic that use platform timer (PIT)
to reenable local APIC timer at C state entry/exit.
Currently, only keep PIT enabled with 100Hz freq. The next step is
trying to dynamically enable/disable PIT while needed, and give it
lower freq.
Signed-off-by: Yu Ke <ke.yu@xxxxxxxxx>
Signed-off-by: Tian Kevin <kevin.tian@xxxxxxxxx>
Signed-off-by: Wei Gang <gang.wei@xxxxxxxxx>
Signed-off-by: Keir Fraser <keir.fraser@xxxxxxxxxx>
---
xen/arch/x86/acpi/cpu_idle.c | 23 +++++++++++++++++----
xen/arch/x86/setup.c | 2 -
xen/arch/x86/time.c | 47 ++++++++++++++++++++++++++++++++++++++++++-
xen/include/asm-x86/time.h | 4 +++
4 files changed, 70 insertions(+), 6 deletions(-)
diff -r 39c2cab9e765 -r 79517ed2a108 xen/arch/x86/acpi/cpu_idle.c
--- a/xen/arch/x86/acpi/cpu_idle.c Mon Jul 14 10:12:07 2008 +0100
+++ b/xen/arch/x86/acpi/cpu_idle.c Mon Jul 14 10:43:32 2008 +0100
@@ -56,6 +56,9 @@
#define ACPI_PROCESSOR_MAX_C2_LATENCY 100
#define ACPI_PROCESSOR_MAX_C3_LATENCY 1000
+static void (*lapic_timer_off)(void);
+static void (*lapic_timer_on)(void);
+
extern u32 pmtmr_ioport;
extern void (*pm_idle) (void);
@@ -437,7 +440,7 @@ static void acpi_processor_idle(void)
/* preparing TSC stop */
cstate_save_tsc();
/* preparing APIC stop */
- hpet_broadcast_enter();
+ lapic_timer_off();
/* Get start time (ticks) */
t1 = inl(pmtmr_ioport);
@@ -446,8 +449,6 @@ static void acpi_processor_idle(void)
/* Get end time (ticks) */
t2 = inl(pmtmr_ioport);
- /* recovering APIC */
- hpet_broadcast_exit();
/* recovering TSC */
cstate_restore_tsc();
@@ -460,6 +461,8 @@ static void acpi_processor_idle(void)
/* Re-enable interrupts */
local_irq_enable();
+ /* recovering APIC */
+ lapic_timer_on();
/* Compute time (ticks) that we were actually asleep */
sleep_ticks = ticks_elapsed(t1, t2);
/* Do not account our idle-switching overhead: */
@@ -752,8 +755,20 @@ static int check_cx(struct acpi_processo
if ( cx->type == ACPI_STATE_C3 )
{
/* We must be able to use HPET in place of LAPIC timers. */
- if ( !hpet_broadcast_is_available() )
+ if ( hpet_broadcast_is_available() )
+ {
+ lapic_timer_off = hpet_broadcast_enter;
+ lapic_timer_on = hpet_broadcast_exit;
+ }
+ else if ( pit_broadcast_is_available() )
+ {
+ lapic_timer_off = pit_broadcast_enter;
+ lapic_timer_on = pit_broadcast_exit;
+ }
+ else
+ {
return -EINVAL;
+ }
/* All the logic here assumes flags.bm_check is same across all CPUs */
if ( !bm_check_flag )
diff -r 39c2cab9e765 -r 79517ed2a108 xen/arch/x86/setup.c
--- a/xen/arch/x86/setup.c Mon Jul 14 10:12:07 2008 +0100
+++ b/xen/arch/x86/setup.c Mon Jul 14 10:43:32 2008 +0100
@@ -96,7 +96,7 @@ boolean_param("noapic", skip_ioapic_setu
/* **** Linux config option: propagated to domain0. */
/* xen_cpuidle: xen control cstate. */
-static int xen_cpuidle;
+/*static*/ int xen_cpuidle;
boolean_param("cpuidle", xen_cpuidle);
int early_boot = 1;
diff -r 39c2cab9e765 -r 79517ed2a108 xen/arch/x86/time.c
--- a/xen/arch/x86/time.c Mon Jul 14 10:12:07 2008 +0100
+++ b/xen/arch/x86/time.c Mon Jul 14 10:43:32 2008 +0100
@@ -147,6 +147,32 @@ static inline u64 scale_delta(u64 delta,
return product;
}
+/*
+ * cpu_mask that denotes the CPUs that needs timer interrupt coming in as
+ * IPIs in place of local APIC timers
+ */
+extern int xen_cpuidle;
+static cpumask_t pit_broadcast_mask;
+
+static void smp_send_timer_broadcast_ipi(void)
+{
+ int cpu = smp_processor_id();
+ cpumask_t mask;
+
+ cpus_and(mask, cpu_online_map, pit_broadcast_mask);
+
+ if ( cpu_isset(cpu, mask) )
+ {
+ cpu_clear(cpu, mask);
+ raise_softirq(TIMER_SOFTIRQ);
+ }
+
+ if ( !cpus_empty(mask) )
+ {
+ cpumask_raise_softirq(mask, TIMER_SOFTIRQ);
+ }
+}
+
static void timer_interrupt(int irq, void *dev_id, struct cpu_user_regs *regs)
{
ASSERT(local_irq_is_enabled());
@@ -160,6 +186,9 @@ static void timer_interrupt(int irq, voi
/* Rough hack to allow accurate timers to sort-of-work with no APIC. */
if ( !cpu_has_apic )
raise_softirq(TIMER_SOFTIRQ);
+
+ if ( xen_cpuidle )
+ smp_send_timer_broadcast_ipi();
/* Emulate a 32-bit PIT counter. */
if ( using_pit )
@@ -1006,9 +1035,10 @@ void __init early_time_init(void)
setup_irq(0, &irq0);
}
+/* keep pit enabled for pit_broadcast working while cpuidle enabled */
static int disable_pit_irq(void)
{
- if ( !using_pit && cpu_has_apic )
+ if ( !using_pit && cpu_has_apic && !xen_cpuidle )
{
/* Disable PIT CH0 timer interrupt. */
outb_p(0x30, PIT_MODE);
@@ -1025,6 +1055,21 @@ static int disable_pit_irq(void)
return 0;
}
__initcall(disable_pit_irq);
+
+void pit_broadcast_enter(void)
+{
+ cpu_set(smp_processor_id(), pit_broadcast_mask);
+}
+
+void pit_broadcast_exit(void)
+{
+ cpu_clear(smp_processor_id(), pit_broadcast_mask);
+}
+
+int pit_broadcast_is_available(void)
+{
+ return xen_cpuidle;
+}
void send_timer_event(struct vcpu *v)
{
diff -r 39c2cab9e765 -r 79517ed2a108 xen/include/asm-x86/time.h
--- a/xen/include/asm-x86/time.h Mon Jul 14 10:12:07 2008 +0100
+++ b/xen/include/asm-x86/time.h Mon Jul 14 10:43:32 2008 +0100
@@ -34,4 +34,8 @@ struct tm;
struct tm;
struct tm wallclock_time(void);
+void pit_broadcast_enter(void);
+void pit_broadcast_exit(void);
+int pit_broadcast_is_available(void);
+
#endif /* __X86_TIME_H__ */
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|