mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
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>
This commit is contained in:
1 parent
73d5acf50f
commit
3eab82196b
4 files changed
+70
-6
No files matched your search
@@ -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_processor_power *power, xen_processor_cx_t *cx)
|
||||
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 )
|
||||
|
||||
@@ -96,7 +96,7 @@ boolean_param("noapic", skip_ioapic_setup);
|
||||
|
||||
/* **** 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;
|
||||
|
||||
+46
-1
@@ -147,6 +147,32 @@ static inline u64 scale_delta(u64 delta, struct time_scale *scale)
|
||||
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());
|
||||
@@ -161,6 +187,9 @@ static void timer_interrupt(int irq, void *dev_id, struct cpu_user_regs *regs)
|
||||
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);
|
||||
@@ -1026,6 +1056,21 @@ static int disable_pit_irq(void)
|
||||
}
|
||||
__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)
|
||||
{
|
||||
send_guest_vcpu_virq(v, VIRQ_TIMER);
|
||||
|
||||
@@ -34,4 +34,8 @@ int cpu_frequency_change(u64 freq);
|
||||
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__ */
|
||||