ia64/xen-unstable

changeset 18051:79517ed2a108

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@intel.com>
Signed-off-by: Tian Kevin <kevin.tian@intel.com>
Signed-off-by: Wei Gang <gang.wei@intel.com>
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Mon Jul 14 10:43:32 2008 +0100 (2008-07-14)
parents 39c2cab9e765
children 20dca27907d9
files xen/arch/x86/acpi/cpu_idle.c xen/arch/x86/setup.c xen/arch/x86/time.c xen/include/asm-x86/time.h
line diff
     1.1 --- a/xen/arch/x86/acpi/cpu_idle.c	Mon Jul 14 10:12:07 2008 +0100
     1.2 +++ b/xen/arch/x86/acpi/cpu_idle.c	Mon Jul 14 10:43:32 2008 +0100
     1.3 @@ -56,6 +56,9 @@
     1.4  #define ACPI_PROCESSOR_MAX_C2_LATENCY   100
     1.5  #define ACPI_PROCESSOR_MAX_C3_LATENCY   1000
     1.6  
     1.7 +static void (*lapic_timer_off)(void);
     1.8 +static void (*lapic_timer_on)(void);
     1.9 +
    1.10  extern u32 pmtmr_ioport;
    1.11  extern void (*pm_idle) (void);
    1.12  
    1.13 @@ -437,7 +440,7 @@ static void acpi_processor_idle(void)
    1.14          /* preparing TSC stop */
    1.15          cstate_save_tsc();
    1.16          /* preparing APIC stop */
    1.17 -        hpet_broadcast_enter();
    1.18 +        lapic_timer_off();
    1.19  
    1.20          /* Get start time (ticks) */
    1.21          t1 = inl(pmtmr_ioport);
    1.22 @@ -446,8 +449,6 @@ static void acpi_processor_idle(void)
    1.23          /* Get end time (ticks) */
    1.24          t2 = inl(pmtmr_ioport);
    1.25  
    1.26 -        /* recovering APIC */
    1.27 -        hpet_broadcast_exit();
    1.28          /* recovering TSC */
    1.29          cstate_restore_tsc();
    1.30  
    1.31 @@ -460,6 +461,8 @@ static void acpi_processor_idle(void)
    1.32  
    1.33          /* Re-enable interrupts */
    1.34          local_irq_enable();
    1.35 +        /* recovering APIC */
    1.36 +        lapic_timer_on();
    1.37          /* Compute time (ticks) that we were actually asleep */
    1.38          sleep_ticks = ticks_elapsed(t1, t2);
    1.39          /* Do not account our idle-switching overhead: */
    1.40 @@ -752,8 +755,20 @@ static int check_cx(struct acpi_processo
    1.41      if ( cx->type == ACPI_STATE_C3 )
    1.42      {
    1.43          /* We must be able to use HPET in place of LAPIC timers. */
    1.44 -        if ( !hpet_broadcast_is_available() )
    1.45 +        if ( hpet_broadcast_is_available() )
    1.46 +        {
    1.47 +            lapic_timer_off = hpet_broadcast_enter;
    1.48 +            lapic_timer_on = hpet_broadcast_exit;
    1.49 +        }
    1.50 +        else if ( pit_broadcast_is_available() )
    1.51 +        {
    1.52 +            lapic_timer_off = pit_broadcast_enter;
    1.53 +            lapic_timer_on = pit_broadcast_exit;
    1.54 +        }
    1.55 +        else
    1.56 +        {
    1.57              return -EINVAL;
    1.58 +        }
    1.59  
    1.60          /* All the logic here assumes flags.bm_check is same across all CPUs */
    1.61          if ( !bm_check_flag )
     2.1 --- a/xen/arch/x86/setup.c	Mon Jul 14 10:12:07 2008 +0100
     2.2 +++ b/xen/arch/x86/setup.c	Mon Jul 14 10:43:32 2008 +0100
     2.3 @@ -96,7 +96,7 @@ boolean_param("noapic", skip_ioapic_setu
     2.4  
     2.5  /* **** Linux config option: propagated to domain0. */
     2.6  /* xen_cpuidle: xen control cstate. */
     2.7 -static int xen_cpuidle;
     2.8 +/*static*/ int xen_cpuidle;
     2.9  boolean_param("cpuidle", xen_cpuidle);
    2.10  
    2.11  int early_boot = 1;
     3.1 --- a/xen/arch/x86/time.c	Mon Jul 14 10:12:07 2008 +0100
     3.2 +++ b/xen/arch/x86/time.c	Mon Jul 14 10:43:32 2008 +0100
     3.3 @@ -147,6 +147,32 @@ static inline u64 scale_delta(u64 delta,
     3.4      return product;
     3.5  }
     3.6  
     3.7 +/*
     3.8 + * cpu_mask that denotes the CPUs that needs timer interrupt coming in as
     3.9 + * IPIs in place of local APIC timers
    3.10 + */
    3.11 +extern int xen_cpuidle;
    3.12 +static cpumask_t pit_broadcast_mask;
    3.13 +
    3.14 +static void smp_send_timer_broadcast_ipi(void)
    3.15 +{
    3.16 +    int cpu = smp_processor_id();
    3.17 +    cpumask_t mask;
    3.18 +
    3.19 +    cpus_and(mask, cpu_online_map, pit_broadcast_mask);
    3.20 +
    3.21 +    if ( cpu_isset(cpu, mask) )
    3.22 +    {
    3.23 +        cpu_clear(cpu, mask);
    3.24 +        raise_softirq(TIMER_SOFTIRQ);
    3.25 +    }
    3.26 +
    3.27 +    if ( !cpus_empty(mask) )
    3.28 +    {
    3.29 +        cpumask_raise_softirq(mask, TIMER_SOFTIRQ);
    3.30 +    }
    3.31 +}
    3.32 +
    3.33  static void timer_interrupt(int irq, void *dev_id, struct cpu_user_regs *regs)
    3.34  {
    3.35      ASSERT(local_irq_is_enabled());
    3.36 @@ -161,6 +187,9 @@ static void timer_interrupt(int irq, voi
    3.37      if ( !cpu_has_apic )
    3.38          raise_softirq(TIMER_SOFTIRQ);
    3.39  
    3.40 +    if ( xen_cpuidle )
    3.41 +        smp_send_timer_broadcast_ipi();
    3.42 +
    3.43      /* Emulate a 32-bit PIT counter. */
    3.44      if ( using_pit )
    3.45      {
    3.46 @@ -1006,9 +1035,10 @@ void __init early_time_init(void)
    3.47      setup_irq(0, &irq0);
    3.48  }
    3.49  
    3.50 +/* keep pit enabled for pit_broadcast working while cpuidle enabled */
    3.51  static int disable_pit_irq(void)
    3.52  {
    3.53 -    if ( !using_pit && cpu_has_apic )
    3.54 +    if ( !using_pit && cpu_has_apic && !xen_cpuidle )
    3.55      {
    3.56          /* Disable PIT CH0 timer interrupt. */
    3.57          outb_p(0x30, PIT_MODE);
    3.58 @@ -1026,6 +1056,21 @@ static int disable_pit_irq(void)
    3.59  }
    3.60  __initcall(disable_pit_irq);
    3.61  
    3.62 +void pit_broadcast_enter(void)
    3.63 +{
    3.64 +    cpu_set(smp_processor_id(), pit_broadcast_mask);
    3.65 +}
    3.66 +
    3.67 +void pit_broadcast_exit(void)
    3.68 +{
    3.69 +    cpu_clear(smp_processor_id(), pit_broadcast_mask);
    3.70 +}
    3.71 +
    3.72 +int pit_broadcast_is_available(void)
    3.73 +{
    3.74 +    return xen_cpuidle;
    3.75 +}
    3.76 +
    3.77  void send_timer_event(struct vcpu *v)
    3.78  {
    3.79      send_guest_vcpu_virq(v, VIRQ_TIMER);
     4.1 --- a/xen/include/asm-x86/time.h	Mon Jul 14 10:12:07 2008 +0100
     4.2 +++ b/xen/include/asm-x86/time.h	Mon Jul 14 10:43:32 2008 +0100
     4.3 @@ -34,4 +34,8 @@ int cpu_frequency_change(u64 freq);
     4.4  struct tm;
     4.5  struct tm wallclock_time(void);
     4.6  
     4.7 +void pit_broadcast_enter(void);
     4.8 +void pit_broadcast_exit(void);
     4.9 +int pit_broadcast_is_available(void);
    4.10 +
    4.11  #endif /* __X86_TIME_H__ */