Commit 7c323b14 authored by David Mosberger's avatar David Mosberger

ia64: fix spurious "timer tick before it's due" problem

Patch Bjorn Helgaas: Fix the "timer tick before it's due" complaint
from timer_interrupt().  The problem was that smp_callin() turned
on the periodic timer tick before syncing the ITC with the BP.

Syncing the ITC happens with interrupts disabled, and if you're
unlucky enough to (1) pend a timer interrupt, and (2) set the ITC
back before the ITM value that caused the timer interrupt, you
can get stuck for several iterations in the following cycle
(assume 100 clocks per tick):

    ITC     ITM
    ---	    ---
                    ia64_init_itm()
    100	    200	        schedule first tick at 200
                    ia64_sync_itc()
                        disable interrupts
    200     200         ITC == ITM; pend IT interrupt
    150                 set ITC to sync with BP
                        enable interrupts
                        recognize pending IT interrupt
                        disable IT interrupts
                    timer_interrupt()
    160     200         notice that 160 < 200,
                            printk "timer tick before it's due")
    200     200         ITC == ITM; pend IT interrupt
	    300         set ITM for next tick
                        re-enable IT interrupt
                        recognize pending IT interrupt
                        disable IT interrupts
                    timer_interrupt()
    260     300         notice that 260 < 300,
                            printk "timer tick before it's due")

    ...	            repeat until you're tired or timer_interrupt()
                    takes long enough that the ITC lands after the
                    ITM

This patch syncs the ITC with the BP before starting up the
periodic tick, so the above scenario should never happen.

This doesn't change how the timer tick on the BP is started;
that happens quite early (and must be early because things
like calibrate_delay() depend on jiffies updates).
parent e3f32fdc
...@@ -202,6 +202,14 @@ ia64_sync_itc (unsigned int master) ...@@ -202,6 +202,14 @@ ia64_sync_itc (unsigned int master)
} t[NUM_ROUNDS]; } t[NUM_ROUNDS];
#endif #endif
/*
* Make sure local timer ticks are disabled while we sync. If
* they were enabled, we'd have to worry about nasty issues
* like setting the ITC ahead of (or a long time before) the
* next scheduled tick.
*/
BUG_ON((ia64_get_itv() & (1 << 16)) == 0);
go[MASTER] = 1; go[MASTER] = 1;
if (smp_call_function_single(master, sync_master, NULL, 1, 0) < 0) { if (smp_call_function_single(master, sync_master, NULL, 1, 0) < 0) {
...@@ -247,16 +255,6 @@ ia64_sync_itc (unsigned int master) ...@@ -247,16 +255,6 @@ ia64_sync_itc (unsigned int master)
printk(KERN_INFO "CPU %d: synchronized ITC with CPU %u (last diff %ld cycles, " printk(KERN_INFO "CPU %d: synchronized ITC with CPU %u (last diff %ld cycles, "
"maxerr %lu cycles)\n", smp_processor_id(), master, delta, rt); "maxerr %lu cycles)\n", smp_processor_id(), master, delta, rt);
/*
* Check whether we sync'd the itc ahead of the next timer interrupt. If so, just
* reset it.
*/
if (time_after(ia64_get_itc(), local_cpu_data->itm_next)) {
Dprintk("CPU %d: oops, jumped a timer tick; resetting timer.\n",
smp_processor_id());
ia64_cpu_local_tick();
}
} }
/* /*
...@@ -288,11 +286,6 @@ smp_callin (void) ...@@ -288,11 +286,6 @@ smp_callin (void)
smp_setup_percpu_timer(); smp_setup_percpu_timer();
/*
* Get our bogomips.
*/
ia64_init_itm();
ia64_mca_cmc_vector_setup(); /* Setup vector on AP & enable */ ia64_mca_cmc_vector_setup(); /* Setup vector on AP & enable */
#ifdef CONFIG_PERFMON #ifdef CONFIG_PERFMON
...@@ -300,11 +293,6 @@ smp_callin (void) ...@@ -300,11 +293,6 @@ smp_callin (void)
#endif #endif
local_irq_enable(); local_irq_enable();
calibrate_delay();
local_cpu_data->loops_per_jiffy = loops_per_jiffy;
#ifdef CONFIG_IA32_SUPPORT
ia32_gdt_init();
#endif
if (!(sal_platform_features & IA64_SAL_PLATFORM_FEATURE_ITC_DRIFT)) { if (!(sal_platform_features & IA64_SAL_PLATFORM_FEATURE_ITC_DRIFT)) {
/* /*
...@@ -317,6 +305,17 @@ smp_callin (void) ...@@ -317,6 +305,17 @@ smp_callin (void)
ia64_sync_itc(0); ia64_sync_itc(0);
} }
/*
* Get our bogomips.
*/
ia64_init_itm();
calibrate_delay();
local_cpu_data->loops_per_jiffy = loops_per_jiffy;
#ifdef CONFIG_IA32_SUPPORT
ia32_gdt_init();
#endif
/* /*
* Allow the master to continue. * Allow the master to continue.
*/ */
......
...@@ -44,6 +44,12 @@ ia64_set_itv (unsigned long val) ...@@ -44,6 +44,12 @@ ia64_set_itv (unsigned long val)
ia64_srlz_d(); ia64_srlz_d();
} }
static __inline__ unsigned long
ia64_get_itv (void)
{
return ia64_getreg(_IA64_REG_CR_ITV);
}
static __inline__ void static __inline__ void
ia64_set_itc (unsigned long val) ia64_set_itc (unsigned long val)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment