# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1207822285 -3600
# Node ID 8d750b7acfa363dead3cefab03d79918735c18ac
# Parent 5b7a3e040683ba25766879e89c0c6b87b198f5e2
x86: Conditionally disable PIT 100HZ timer interrupt
100HZ PIT timer interrupt set a 10ms upper limit for C state
residency, which makes Xen not power friendly. This patch disable PIT
timer interrupt in the conditions:
- CPU has APIC support, and
- PIT is not used as platform time source
Signed-off-by: Yu Ke <ke.yu@xxxxxxxxx>
Signed-off-by: Keir Fraser <keir.fraser@xxxxxxxxxx>
---
xen/arch/x86/time.c | 151 ++++++++++++++++++++++++++++++++--------------------
1 files changed, 93 insertions(+), 58 deletions(-)
diff -r 5b7a3e040683 -r 8d750b7acfa3 xen/arch/x86/time.c
--- a/xen/arch/x86/time.c Thu Apr 10 10:12:04 2008 +0100
+++ b/xen/arch/x86/time.c Thu Apr 10 11:11:25 2008 +0100
@@ -67,19 +67,16 @@ static DEFINE_PER_CPU(struct cpu_time, c
static DEFINE_PER_CPU(struct cpu_time, cpu_time);
/*
- * Protected by platform_timer_lock, which must be acquired with interrupts
- * disabled because plt_overflow() is called from PIT ch0 interrupt context.
- */
-static s_time_t stime_platform_stamp;
-static u64 platform_timer_stamp;
-static DEFINE_SPINLOCK(platform_timer_lock);
-
-/*
- * Folding platform timer into 64-bit software counter is a really critical
- * operation! We therefore do it directly in PIT ch0 interrupt handler.
- */
-static u32 plt_overflow_jiffies;
-static void plt_overflow(void);
+ * We simulate a 32-bit platform timer from the 16-bit PIT ch2 counter.
+ * Otherwise overflow happens too quickly (~50ms) for us to guarantee that
+ * softirq handling will happen in time.
+ *
+ * The pit_lock protects the 16- and 32-bit stamp fields as well as the
+ */
+static DEFINE_SPINLOCK(pit_lock);
+static u16 pit_stamp16;
+static u32 pit_stamp32;
+static int using_pit;
/*
* 32-bit division of integer dividend and integer divisor yielding
@@ -146,21 +143,36 @@ static inline u64 scale_delta(u64 delta,
return product;
}
-void timer_interrupt(int irq, void *dev_id, struct cpu_user_regs *regs)
+static void timer_interrupt(int irq, void *dev_id, struct cpu_user_regs *regs)
{
ASSERT(local_irq_is_enabled());
+ /* Only for start-of-day interruopt tests in io_apic.c. */
(*(volatile unsigned long *)&pit0_ticks)++;
/* Rough hack to allow accurate timers to sort-of-work with no APIC. */
if ( !cpu_has_apic )
raise_softirq(TIMER_SOFTIRQ);
- if ( --plt_overflow_jiffies == 0 )
- plt_overflow();
-}
-
-static struct irqaction irq0 = { timer_interrupt, "timer", NULL};
+ /* Emulate a 32-bit PIT counter. */
+ if ( using_pit )
+ {
+ u16 count;
+
+ spin_lock_irq(&pit_lock);
+
+ outb(0x80, PIT_MODE);
+ count = inb(PIT_CH2);
+ count |= inb(PIT_CH2) << 8;
+
+ pit_stamp32 += (u16)(pit_stamp16 - count);
+ pit_stamp16 = count;
+
+ spin_unlock_irq(&pit_lock);
+ }
+}
+
+static struct irqaction irq0 = { timer_interrupt, "timer", NULL };
/* ------ Calibrate the TSC -------
* Return processor ticks per second / CALIBRATE_FRAC.
@@ -294,12 +306,21 @@ static char *freq_string(u64 freq)
static u32 read_pit_count(void)
{
- u16 count;
- ASSERT(spin_is_locked(&platform_timer_lock));
+ u16 count16;
+ u32 count32;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pit_lock, flags);
+
outb(0x80, PIT_MODE);
- count = inb(PIT_CH2);
- count |= inb(PIT_CH2) << 8;
- return ~count;
+ count16 = inb(PIT_CH2);
+ count16 |= inb(PIT_CH2) << 8;
+
+ count32 = pit_stamp32 + (u16)(pit_stamp16 - count16);
+
+ spin_unlock_irqrestore(&pit_lock, flags);
+
+ return count32;
}
static void init_pit(struct platform_timesource *pts)
@@ -307,7 +328,8 @@ static void init_pit(struct platform_tim
pts->name = "PIT";
pts->frequency = CLOCK_TICK_RATE;
pts->read_counter = read_pit_count;
- pts->counter_bits = 16;
+ pts->counter_bits = 32;
+ using_pit = 1;
}
/************************************************************
@@ -465,24 +487,28 @@ static int init_pmtimer(struct platform_
static struct platform_timesource plt_src; /* details of chosen timesource */
static u32 plt_mask; /* hardware-width mask */
-static u32 plt_overflow_period; /* jiffies between calls to plt_overflow() */
+static u64 plt_overflow_period; /* ns between calls to plt_overflow() */
static struct time_scale plt_scale; /* scale: platform counter -> nanosecs */
/* Protected by platform_timer_lock. */
-static u64 plt_count64; /* 64-bit platform counter stamp */
-static u32 plt_count; /* hardware-width platform counter stamp */
-
-static void plt_overflow(void)
+static DEFINE_SPINLOCK(platform_timer_lock);
+static s_time_t stime_platform_stamp; /* System time at below platform time */
+static u64 platform_timer_stamp; /* Platform time at above system time */
+static u64 plt_stamp64; /* 64-bit platform counter stamp */
+static u32 plt_stamp; /* hardware-width platform counter stamp */
+static struct timer plt_overflow_timer;
+
+static void plt_overflow(void *unused)
{
u32 count;
- unsigned long flags;
-
- spin_lock_irqsave(&platform_timer_lock, flags);
+
+ spin_lock(&platform_timer_lock);
count = plt_src.read_counter();
- plt_count64 += (count - plt_count) & plt_mask;
- plt_count = count;
- plt_overflow_jiffies = plt_overflow_period;
- spin_unlock_irqrestore(&platform_timer_lock, flags);
+ plt_stamp64 += (count - plt_stamp) & plt_mask;
+ plt_stamp = count;
+ spin_unlock(&platform_timer_lock);
+
+ set_timer(&plt_overflow_timer, NOW() + plt_overflow_period);
}
static s_time_t __read_platform_stime(u64 platform_time)
@@ -496,12 +522,11 @@ static s_time_t read_platform_stime(void
{
u64 count;
s_time_t stime;
- unsigned long flags;
-
- spin_lock_irqsave(&platform_timer_lock, flags);
- count = plt_count64 + ((plt_src.read_counter() - plt_count) & plt_mask);
+
+ spin_lock(&platform_timer_lock);
+ count = plt_stamp64 + ((plt_src.read_counter() - plt_stamp) & plt_mask);
stime = __read_platform_stime(count);
- spin_unlock_irqrestore(&platform_timer_lock, flags);
+ spin_unlock(&platform_timer_lock);
return stime;
}
@@ -510,27 +535,25 @@ static void platform_time_calibration(vo
{
u64 count;
s_time_t stamp;
- unsigned long flags;
-
- spin_lock_irqsave(&platform_timer_lock, flags);
- count = plt_count64 + ((plt_src.read_counter() - plt_count) & plt_mask);
+
+ spin_lock(&platform_timer_lock);
+ count = plt_stamp64 + ((plt_src.read_counter() - plt_stamp) & plt_mask);
stamp = __read_platform_stime(count);
stime_platform_stamp = stamp;
platform_timer_stamp = count;
- spin_unlock_irqrestore(&platform_timer_lock, flags);
+ spin_unlock(&platform_timer_lock);
}
static void resume_platform_timer(void)
{
/* No change in platform_stime across suspend/resume. */
- platform_timer_stamp = plt_count64;
- plt_count = plt_src.read_counter();
+ platform_timer_stamp = plt_stamp64;
+ plt_stamp = plt_src.read_counter();
}
static void init_platform_timer(void)
{
struct platform_timesource *pts = &plt_src;
- u64 overflow_period;
int rc = -1;
if ( opt_clocksource[0] != '\0' )
@@ -560,13 +583,12 @@ static void init_platform_timer(void)
set_time_scale(&plt_scale, pts->frequency);
- overflow_period = scale_delta(1ull << (pts->counter_bits-1), &plt_scale);
- do_div(overflow_period, MILLISECS(1000/HZ));
- plt_overflow_period = overflow_period;
- plt_overflow();
- printk("Platform timer overflows in %d jiffies.\n", plt_overflow_period);
-
- platform_timer_stamp = plt_count64;
+ plt_overflow_period = scale_delta(
+ 1ull << (pts->counter_bits-1), &plt_scale);
+ init_timer(&plt_overflow_timer, plt_overflow, NULL, 0);
+ plt_overflow(NULL);
+
+ platform_timer_stamp = plt_stamp64;
printk("Platform timer is %s %s\n",
freq_string(pts->frequency), pts->name);
@@ -968,6 +990,19 @@ void __init early_time_init(void)
setup_irq(0, &irq0);
}
+static int __init late_time_init(void)
+{
+ if ( !using_pit && cpu_has_apic )
+ {
+ /* Disable PIT CH0 timer interrupt. */
+ outb_p(0x30, PIT_MODE);
+ outb_p(0, PIT_CH0);
+ outb_p(0, PIT_CH0);
+ }
+ return 0;
+}
+__initcall(late_time_init);
+
void send_timer_event(struct vcpu *v)
{
send_guest_vcpu_virq(v, VIRQ_TIMER);
@@ -1018,7 +1053,7 @@ int dom0_pit_access(struct ioreq *ioreq)
int dom0_pit_access(struct ioreq *ioreq)
{
/* Is Xen using Channel 2? Then disallow direct dom0 access. */
- if ( plt_src.read_counter == read_pit_count )
+ if ( using_pit )
return 0;
switch ( ioreq->addr )
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|