patch-2.4.0-test2 linux/arch/ia64/kernel/time.c

Next file: linux/arch/ia64/kernel/traps.c
Previous file: linux/arch/ia64/kernel/sys_ia64.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0-test1/linux/arch/ia64/kernel/time.c linux/arch/ia64/kernel/time.c
@@ -34,7 +34,10 @@
 
 static struct {
 	unsigned long delta;
-	unsigned long next[NR_CPUS];
+	union {
+		unsigned long count;
+		unsigned char pad[SMP_CACHE_BYTES];
+	} next[NR_CPUS];
 } itm;
 
 static void
@@ -69,16 +72,27 @@
 static inline unsigned long
 gettimeoffset (void)
 {
-	unsigned long now = ia64_get_itc();
-	unsigned long elapsed_cycles, lost;
-
-	elapsed_cycles = now - (itm.next[smp_processor_id()] - itm.delta);
-
-	lost = lost_ticks;
-	if (lost)
-		elapsed_cycles += lost*itm.delta;
+#ifdef CONFIG_SMP
+	/*
+	 * The code below doesn't work for SMP because only CPU 0
+	 * keeps track of the time.
+	 */
+	return 0;
+#else
+	unsigned long now = ia64_get_itc(), last_tick;
+	unsigned long elapsed_cycles, lost = lost_ticks;
 
+	last_tick = (itm.next[smp_processor_id()].count - (lost+1)*itm.delta);
+# if 1
+	if ((long) (now - last_tick) < 0) {
+		printk("Yikes: now < last_tick (now=0x%lx,last_tick=%lx)!  No can do.\n",
+		       now, last_tick);
+		return 0;
+	}
+# endif
+	elapsed_cycles = now - last_tick;
 	return (elapsed_cycles*my_cpu_data.usec_per_cyc) >> IA64_USEC_PER_CYC_SHIFT;
+#endif
 }
 
 void
@@ -137,6 +151,7 @@
 	static unsigned long last_time;
 	static unsigned char count;
 	int cpu = smp_processor_id();
+	unsigned long new_itm;
 	int printed = 0;
 
 	/*
@@ -146,6 +161,12 @@
 	 * xtime_lock.
 	 */
 	write_lock(&xtime_lock);
+	new_itm = itm.next[cpu].count;
+
+	if (!time_after(ia64_get_itc(), new_itm))
+		printk("Oops: timer tick before it's due (itc=%lx,itm=%lx)\n",
+		       ia64_get_itc(), new_itm);
+
 	while (1) {
 		/*
 		 * Do kernel PC profiling here.  We multiply the
@@ -164,18 +185,10 @@
 		do_timer(regs);
 #endif
 
-		itm.next[cpu] += itm.delta;
-		/*
-		 * There is a race condition here: to be on the "safe"
-		 * side, we process timer ticks until itm.next is
-		 * ahead of the itc by at least half the timer
-		 * interval.  This should give us enough time to set
-		 * the new itm value without losing a timer tick.
-		 */
-		if (time_after(itm.next[cpu], ia64_get_itc() + itm.delta/2)) {
-			ia64_set_itm(itm.next[cpu]);
+		new_itm += itm.delta;
+		itm.next[cpu].count = new_itm;
+		if (time_after(new_itm, ia64_get_itc()))
 			break;
-		}
 
 #if !(defined(CONFIG_IA64_SOFTSDV_HACKS) && defined(CONFIG_SMP))
 		/*
@@ -188,28 +201,39 @@
 			last_time = jiffies;
 			if (!printed) {
 				printk("Lost clock tick on CPU %d (now=%lx, next=%lx)!!\n",
-				       cpu, ia64_get_itc(), itm.next[cpu]);
+				       cpu, ia64_get_itc(), itm.next[cpu].count);
 				printed = 1;
-			}
 # ifdef CONFIG_IA64_DEBUG_IRQ
-			printk("last_cli_ip=%lx\n", last_cli_ip);
+				printk("last_cli_ip=%lx\n", last_cli_ip);
 # endif
+			}
 		}
 #endif
 	}
 	write_unlock(&xtime_lock);
+
+	/*
+	 * If we're too close to the next clock tick for comfort, we
+	 * increase the saftey margin by intentionally dropping the
+	 * next tick(s).  We do NOT update itm.next accordingly
+	 * because that would force us to call do_timer() which in
+	 * turn would let our clock run too fast (with the potentially
+	 * devastating effect of losing monotony of time).
+	 */
+	while (!time_after(new_itm, ia64_get_itc() + itm.delta/2))
+		new_itm += itm.delta;
+	ia64_set_itm(new_itm);
 }
 
-#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC
+#if defined(CONFIG_ITANIUM_ASTEP_SPECIFIC) || defined(CONFIG_IA64_SOFTSDV_HACKS)
 
-void 
+/*
+ * Interrupts must be disabled before calling this routine.
+ */
+void
 ia64_reset_itm (void)
 {
-	unsigned long flags;
-
-	local_irq_save(flags);
 	timer_interrupt(0, 0, ia64_task_regs(current));
-	local_irq_restore(flags);
 }
 
 #endif /* CONFIG_ITANIUM_ASTEP_SPECIFIC */
@@ -220,11 +244,14 @@
 void __init
 ia64_cpu_local_tick(void)
 {
+#ifdef CONFIG_IA64_SOFTSDV_HACKS
+	ia64_set_itc(0);
+#endif
+
 	/* arrange for the cycle counter to generate a timer interrupt: */
 	ia64_set_itv(TIMER_IRQ, 0);
-	ia64_set_itc(0);
-	itm.next[smp_processor_id()] = ia64_get_itc() + itm.delta;
-	ia64_set_itm(itm.next[smp_processor_id()]);
+	itm.next[smp_processor_id()].count = ia64_get_itc() + itm.delta;
+	ia64_set_itm(itm.next[smp_processor_id()].count);
 }
 
 void __init
@@ -254,25 +281,7 @@
 		itc_ratio.num = 3;
 		itc_ratio.den = 1;
 	}
-#if defined(CONFIG_IA64_LION_HACKS)
-	/* Our Lion currently returns base freq 104.857MHz, which
-	   ain't right (it really is 100MHz).  */
-	printk("SAL/PAL returned: base-freq=%lu, itc-ratio=%lu/%lu, proc-ratio=%lu/%lu\n",
-	       platform_base_freq, itc_ratio.num, itc_ratio.den,
-	       proc_ratio.num, proc_ratio.den);
-	platform_base_freq = 100000000;
-#elif 0 && defined(CONFIG_IA64_BIGSUR_HACKS)
-	/* BigSur with 991020 firmware returned itc-ratio=9/2 and base
-	   freq 75MHz, which wasn't right.  The 991119 firmware seems
-	   to return the right values, so this isn't necessary
-	   anymore... */
-	printk("SAL/PAL returned: base-freq=%lu, itc-ratio=%lu/%lu, proc-ratio=%lu/%lu\n",
-	       platform_base_freq, itc_ratio.num, itc_ratio.den,
-	       proc_ratio.num, proc_ratio.den);
-	platform_base_freq = 100000000;
-	proc_ratio.num = 5; proc_ratio.den = 1;
-	itc_ratio.num  = 5; itc_ratio.den  = 1;
-#elif defined(CONFIG_IA64_SOFTSDV_HACKS)
+#ifdef CONFIG_IA64_SOFTSDV_HACKS
 	platform_base_freq = 10000000;
 	proc_ratio.num = 4; proc_ratio.den = 1;
 	itc_ratio.num  = 4; itc_ratio.den  = 1;
@@ -290,8 +299,9 @@
 
         itc_freq = (platform_base_freq*itc_ratio.num)/itc_ratio.den;
         itm.delta = itc_freq / HZ;
-        printk("timer: base freq=%lu.%03luMHz, ITC ratio=%lu/%lu, ITC freq=%lu.%03luMHz\n",
-               platform_base_freq / 1000000, (platform_base_freq / 1000) % 1000,
+        printk("timer: CPU %d base freq=%lu.%03luMHz, ITC ratio=%lu/%lu, ITC freq=%lu.%03luMHz\n",
+	       smp_processor_id(),
+	       platform_base_freq / 1000000, (platform_base_freq / 1000) % 1000,
                itc_ratio.num, itc_ratio.den, itc_freq / 1000000, (itc_freq / 1000) % 1000);
 
 	my_cpu_data.proc_freq = (platform_base_freq*proc_ratio.num)/proc_ratio.den;
@@ -313,6 +323,8 @@
 time_init (void)
 {
 	/* we can't do request_irq() here because the kmalloc() would fail... */
+	irq_desc[TIMER_IRQ].status |= IRQ_PER_CPU;
+	irq_desc[TIMER_IRQ].handler = &irq_type_ia64_sapic;
 	setup_irq(TIMER_IRQ, &timer_irqaction);
 
 	efi_gettimeofday(&xtime);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)