aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorhawkes@sgi.com <hawkes@sgi.com>2006-02-14 10:40:17 -0800
committerTony Luck <tony.luck@intel.com>2006-02-15 13:37:04 -0800
commitdefbb2c929cbe89dc92239b303cd33d3c85e9a83 (patch)
tree85dbcfa407d4bfaecbce4f3556a73033b8f70caf /arch
parent4c2cd96696ae0896ce4bcf725b9f0eaffafeb640 (diff)
downloadkernel_samsung_smdk4412-defbb2c929cbe89dc92239b303cd33d3c85e9a83.zip
kernel_samsung_smdk4412-defbb2c929cbe89dc92239b303cd33d3c85e9a83.tar.gz
kernel_samsung_smdk4412-defbb2c929cbe89dc92239b303cd33d3c85e9a83.tar.bz2
[IA64] ia64: simplify and fix udelay()
The original ia64 udelay() was simple, but flawed for platforms without synchronized ITCs: a preemption and migration to another CPU during the while-loop likely resulted in too-early termination or very, very lengthy looping. The first fix (now in 2.6.15) broke the delay loop into smaller, non-preemptible chunks, reenabling preemption between the chunks. This fix is flawed in that the total udelay is computed to be the sum of just the non-premptible while-loop pieces, i.e., not counting the time spent in the interim preemptible periods. If an interrupt or a migration occurs during one of these interim periods, then that time is invisible and only serves to lengthen the effective udelay(). This new fix backs out the current flawed fix and returns to a simple udelay(), fully preemptible and interruptible. It implements two simple alternative udelay() routines: one a default generic version that uses ia64_get_itc(), and the other an sn-specific version that uses that platform's RTC. Signed-off-by: John Hawkes <hawkes@sgi.com> Signed-off-by: Tony Luck <tony.luck@intel.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/ia64/kernel/time.c39
-rw-r--r--arch/ia64/sn/kernel/sn2/timer.c19
2 files changed, 36 insertions, 22 deletions
diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c
index a094ec4..307d01e 100644
--- a/arch/ia64/kernel/time.c
+++ b/arch/ia64/kernel/time.c
@@ -250,32 +250,27 @@ time_init (void)
set_normalized_timespec(&wall_to_monotonic, -xtime.tv_sec, -xtime.tv_nsec);
}
-#define SMALLUSECS 100
-
-void
-udelay (unsigned long usecs)
+/*
+ * Generic udelay assumes that if preemption is allowed and the thread
+ * migrates to another CPU, that the ITC values are synchronized across
+ * all CPUs.
+ */
+static void
+ia64_itc_udelay (unsigned long usecs)
{
- unsigned long start;
- unsigned long cycles;
- unsigned long smallusecs;
+ unsigned long start = ia64_get_itc();
+ unsigned long end = start + usecs*local_cpu_data->cyc_per_usec;
- /*
- * Execute the non-preemptible delay loop (because the ITC might
- * not be synchronized between CPUS) in relatively short time
- * chunks, allowing preemption between the chunks.
- */
- while (usecs > 0) {
- smallusecs = (usecs > SMALLUSECS) ? SMALLUSECS : usecs;
- preempt_disable();
- cycles = smallusecs*local_cpu_data->cyc_per_usec;
- start = ia64_get_itc();
+ while (time_before(ia64_get_itc(), end))
+ cpu_relax();
+}
- while (ia64_get_itc() - start < cycles)
- cpu_relax();
+void (*ia64_udelay)(unsigned long usecs) = &ia64_itc_udelay;
- preempt_enable();
- usecs -= smallusecs;
- }
+void
+udelay (unsigned long usecs)
+{
+ (*ia64_udelay)(usecs);
}
EXPORT_SYMBOL(udelay);
diff --git a/arch/ia64/sn/kernel/sn2/timer.c b/arch/ia64/sn/kernel/sn2/timer.c
index deb9baf..56a88b6 100644
--- a/arch/ia64/sn/kernel/sn2/timer.c
+++ b/arch/ia64/sn/kernel/sn2/timer.c
@@ -14,6 +14,7 @@
#include <asm/hw_irq.h>
#include <asm/system.h>
+#include <asm/timex.h>
#include <asm/sn/leds.h>
#include <asm/sn/shub_mmr.h>
@@ -28,9 +29,27 @@ static struct time_interpolator sn2_interpolator = {
.source = TIME_SOURCE_MMIO64
};
+/*
+ * sn udelay uses the RTC instead of the ITC because the ITC is not
+ * synchronized across all CPUs, and the thread may migrate to another CPU
+ * if preemption is enabled.
+ */
+static void
+ia64_sn_udelay (unsigned long usecs)
+{
+ unsigned long start = rtc_time();
+ unsigned long end = start +
+ usecs * sn_rtc_cycles_per_second / 1000000;
+
+ while (time_before((unsigned long)rtc_time(), end))
+ cpu_relax();
+}
+
void __init sn_timer_init(void)
{
sn2_interpolator.frequency = sn_rtc_cycles_per_second;
sn2_interpolator.addr = RTC_COUNTER_ADDR;
register_time_interpolator(&sn2_interpolator);
+
+ ia64_udelay = &ia64_sn_udelay;
}