[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-changelog] [xen-unstable] x86: Conditionally disable PIT 100HZ timer interrupt



# 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


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.