All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jes Sorensen <jes@wildopensource.com>
To: linux-ia64@vger.kernel.org
Subject: [Linux-ia64] [patch] 2.4 timer_interrupt/gettimeoffset machvec
Date: Tue, 08 Apr 2003 20:35:56 +0000	[thread overview]
Message-ID: <marc-linux-ia64-105590723705438@msgid-missing> (raw)

Hi,

On systems with a drifting ITC, such as the SGI SN2, the current
implementation of timer_interrupt() and gettimeoffset() wont work as
they rely heavily on ar.itc.

On SN2 this has been solved by using the synchronized RTC in the system,
which results a different implementation of the two above mentioned
functions.

To get around this I suggest we change gettimeoffset() and
timer_intererupt() to be machvec's.

Patch against a not too old Bjorn-2.4 kernel attached. Tested on DIG.

Cheers,
Jes

diff -X /home/jes/exclude-linux -urN linux-2.4.21-pre5-030312-pretimeofday/arch/ia64/kernel/time.c linux-2.4.21-pre5-030312/arch/ia64/kernel/time.c
--- linux-2.4.21-pre5-030312-pretimeofday/arch/ia64/kernel/time.c	Thu Nov 28 18:53:09 2002
+++ linux-2.4.21-pre5-030312/arch/ia64/kernel/time.c	Fri Apr  4 18:44:21 2003
@@ -33,7 +33,7 @@
 
 #endif
 
-static void
+void
 do_profile (unsigned long ip)
 {
 	extern unsigned long prof_cpu_mask;
@@ -61,8 +61,8 @@
  * Return the number of micro-seconds that elapsed since the last update to jiffy.  The
  * xtime_lock must be at least read-locked when calling this routine.
  */
-static inline unsigned long
-gettimeoffset (void)
+unsigned long
+__ia64_gettimeoffset (void)
 {
 	unsigned long elapsed_cycles, lost = jiffies - wall_jiffies;
 	unsigned long now, last_tick;
@@ -146,8 +146,8 @@
 	tv->tv_usec = usec;
 }
 
-static void
-timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+void
+__ia64_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 	unsigned long new_itm;
 
@@ -284,7 +284,7 @@
 }
 
 static struct irqaction timer_irqaction = {
-	handler:	timer_interrupt,
+	handler:	NULL,
 	flags:		SA_INTERRUPT,
 	name:		"timer"
 };
@@ -292,6 +294,7 @@
 void __init
 time_init (void)
 {
+	timer_irqaction.handler = platform_timer_interrupt;
 	register_percpu_irq(IA64_TIMER_VECTOR, &timer_irqaction);
 	efi_gettimeofday((struct timeval *) &xtime);
 	ia64_init_itm();
diff -X /home/jes/exclude-linux -urN linux-2.4.21-pre5-030312-pretimeofday/arch/ia64/sn/kernel/setup.c linux-2.4.21-pre5-030312/arch/ia64/sn/kernel/setup.c
--- linux-2.4.21-pre5-030312-pretimeofday/arch/ia64/sn/kernel/setup.c	Wed Mar 12 17:24:55 2003
+++ linux-2.4.21-pre5-030312/arch/ia64/sn/kernel/setup.c	Sun Apr  6 17:52:15 2003
@@ -76,6 +76,7 @@
 
 extern void bte_init_node (nodepda_t *, cnodeid_t);
 extern void bte_init_cpu (void);
+extern void sn_timer_init (void);
 
 unsigned long sn_rtc_cycles_per_second;   
 unsigned long sn_rtc_usec_per_cyc;
@@ -313,6 +314,8 @@
 	 * Turn off "floating-point assist fault" warnings by default.
 	 */
 	current->thread.flags |= IA64_THREAD_FPEMU_NOPRINT;
+
+	sn_timer_init();
 }
 
 /**
diff -X /home/jes/exclude-linux -urN linux-2.4.21-pre5-030312-pretimeofday/arch/ia64/sn/kernel/sn2/Makefile linux-2.4.21-pre5-030312/arch/ia64/sn/kernel/sn2/Makefile
--- linux-2.4.21-pre5-030312-pretimeofday/arch/ia64/sn/kernel/sn2/Makefile	Wed Mar 12 17:24:55 2003
+++ linux-2.4.21-pre5-030312/arch/ia64/sn/kernel/sn2/Makefile	Fri Apr  4 17:25:34 2003
@@ -42,6 +42,6 @@
 
 O_TARGET        = sn2.o
 
-obj-y          = cache.o iomv.o ptc_deadlock.o sn2_smp.o sn_proc_fs.o
+obj-y          = cache.o iomv.o ptc_deadlock.o sn2_smp.o sn_proc_fs.o timer.o
 
 include $(TOPDIR)/Rules.make
diff -X /home/jes/exclude-linux -urN linux-2.4.21-pre5-030312-pretimeofday/arch/ia64/sn/kernel/sn2/timer.c linux-2.4.21-pre5-030312/arch/ia64/sn/kernel/sn2/timer.c
--- linux-2.4.21-pre5-030312-pretimeofday/arch/ia64/sn/kernel/sn2/timer.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.21-pre5-030312/arch/ia64/sn/kernel/sn2/timer.c	Sun Apr  6 19:07:50 2003
@@ -0,0 +1,176 @@
+/*
+ * linux/arch/ia64/sn/kernel/sn2/timer.c
+ *
+ * Copyright (C) 2003 Silicon Graphics, Inc.
+ * Copyright (C) 1998-2001 Hewlett-Packard Co
+ * Copyright (C) 1998-2000 Stephane Eranian <eranian@hpl.hp.com>
+ * Copyright (C) 1999-2001 David Mosberger <davidm@hpl.hp.com>
+ * Copyright (C) 1999 Don Dugger <don.dugger@intel.com>
+ * Copyright (C) 1999-2000 VA Linux Systems
+ * Copyright (C) 1999-2000 Walt Drummond <drummond@valinux.com>
+ */
+
+#include <linux/config.h>
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/interrupt.h>
+#include <linux/efi.h>
+
+#include <asm/delay.h>
+#include <asm/hw_irq.h>
+#include <asm/ptrace.h>
+#include <asm/sal.h>
+#include <asm/system.h>
+
+#include <asm/sn/leds.h>
+#include <asm/sn/clksupport.h>
+
+extern rwlock_t xtime_lock;
+extern void do_profile (unsigned long ip);
+
+extern unsigned long sn_rtc_cycles_per_second;
+extern unsigned long sn_rtc_usec_per_cyc;
+static unsigned long sn_rtc_per_itc;
+static unsigned long sn_rtc_delta;
+static volatile unsigned long last_rtc_val;
+
+/**
+ * gettimeoffset - number of usecs elapsed since &xtime was last updated
+ *
+ * This function is used by do_gettimeofday() to determine the number
+ * of usecs that have elapsed since the last update to &xtime.  On SN
+ * this is accomplished using the RTC built in to each Hub chip; each
+ * is guaranteed to be synchronized by the PROM, so a local read will
+ * suffice (get_cycles() does this for us).  A snapshot of the RTC value
+ * is taken on every timer interrupt and this function more or less
+ * subtracts that snapshot value from the current value.
+ *
+ * Note that if a lot of processing was done during the last timer
+ * interrupt then &xtime may be some number of jiffies out of date.
+ * This function must account for that.
+ */
+extern unsigned long wall_jiffies; /* from kernel/timer.c */
+
+unsigned long
+sn2_gettimeoffset(void)
+{
+	unsigned long elapsed_rtc_cycles, lost = jiffies - wall_jiffies;
+	unsigned long local_last_rtc_val, current_rtc_val, last_tick;
+
+	local_last_rtc_val = last_rtc_val;
+	last_tick = local_last_rtc_val -
+		(((lost + 1)* sn_rtc_delta) >> SN_RTC_PER_ITC_SHIFT);
+	current_rtc_val = get_cycles();
+	if ((long)(current_rtc_val - last_tick) < 0) {
+		/* This happens when a timer interrupt occurred on cpu 0
+		 * and our local jiffies/wall_jiffies values are out of
+		 * sync.  The frlock around this code will detect the
+		 * inconsistency and try again */
+		return 0; 
+	}
+
+	elapsed_rtc_cycles = current_rtc_val - last_tick;
+	return (elapsed_rtc_cycles * (long)sn_rtc_usec_per_cyc) >>
+		IA64_USEC_PER_CYC_SHIFT; 
+}
+
+void
+sn2_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned long new_itm;
+
+	if (!pda.hb_count--) {
+		pda.hb_count = HZ/2;
+		pda.hb_state ^= LED_CPU_HEARTBEAT;
+		set_led_bits(pda.hb_state, LED_CPU_HEARTBEAT);
+	}
+
+	if (pda.pio_shub_war_cam_addr)
+		*pda.pio_shub_war_cam_addr = 0x8000000000000010UL;
+
+#if 1	/* XXX - what is this? - do we really need it? - Jes */
+#define SN_LB_INT_WAR_INTERVAL 100
+	if (pda.sn_lb_int_war_ticks = 0) {
+		sn_lb_int_war_check();
+	}
+	pda.sn_lb_int_war_ticks++;
+	if (pda.sn_lb_int_war_ticks >= SN_LB_INT_WAR_INTERVAL)
+		pda.sn_lb_int_war_ticks = 0;
+#endif
+
+	new_itm = local_cpu_data->itm_next;
+
+	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 instruction
+		 * number by four so that we can use a prof_shift of 2
+		 * to get instruction-level instead of just
+		 * bundle-level accuracy.
+		 */
+		if (!user_mode(regs))
+			do_profile(regs->cr_iip + 4*ia64_psr(regs)->ri);
+
+#ifdef CONFIG_SMP
+		smp_do_timer(regs);
+#endif
+		new_itm += local_cpu_data->itm_delta;
+
+		if (smp_processor_id() = 0) {
+			long last_rtc_del;
+			/*
+			 * Here we are in the timer irq handler. We have 
+			 * irqs locally disabled, but we don't know if the
+			 * timer_bh is running on another CPU. We need to
+			 * avoid to SMP race by acquiring the xtime_lock.
+			 */
+			write_lock(&xtime_lock);
+
+	  		/* Used by gettimeoffset on 'drifty' platforms */
+			last_rtc_del = ((((long)new_itm -
+					  (long)ia64_get_itc()) *
+					 (long)sn_rtc_per_itc) >>
+					SN_RTC_PER_ITC_SHIFT);
+			last_rtc_val = (unsigned long)last_rtc_del +
+				GET_RTC_COUNTER();
+
+			do_timer(regs);
+			local_cpu_data->itm_next = new_itm;
+			write_unlock(&xtime_lock);
+		} else
+			local_cpu_data->itm_next = new_itm;
+
+		if (time_after(new_itm, ia64_get_itc()))
+			break;
+	}
+
+	do {
+		/*
+		 * 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 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() +
+				   local_cpu_data->itm_delta/2))
+			new_itm += local_cpu_data->itm_delta;
+		ia64_set_itm(new_itm);
+		/* double check, in case we got hit by a (slow) PMI: */
+	} while (time_after_eq(ia64_get_itc(), new_itm));
+}
+
+void __init
+sn_timer_init (void)
+{
+	sn_rtc_per_itc = (sn_rtc_cycles_per_second << SN_RTC_PER_ITC_SHIFT) /
+		local_cpu_data->itc_freq;
+	sn_rtc_delta = local_cpu_data->itm_delta * sn_rtc_per_itc;
+}
diff -X /home/jes/exclude-linux -urN linux-2.4.21-pre5-030312-pretimeofday/include/asm-ia64/machvec.h linux-2.4.21-pre5-030312/include/asm-ia64/machvec.h
--- linux-2.4.21-pre5-030312-pretimeofday/include/asm-ia64/machvec.h	Sun Mar 16 10:55:27 2003
+++ linux-2.4.21-pre5-030312/include/asm-ia64/machvec.h	Fri Apr  4 19:23:31 2003
@@ -64,6 +64,8 @@
 typedef void ia64_mv_outb_t (unsigned char, unsigned long);
 typedef void ia64_mv_outw_t (unsigned short, unsigned long);
 typedef void ia64_mv_outl_t (unsigned int, unsigned long);
+typedef unsigned long ia64_mv_gettimeoffset_t (void);
+typedef void ia64_mv_timer_interrupt_t(int, void *, struct pt_regs *);
 
 extern void machvec_noop (void);
 
@@ -115,6 +117,8 @@
 #  define platform_outb		ia64_mv.outb
 #  define platform_outw		ia64_mv.outw
 #  define platform_outl		ia64_mv.outl
+#  define platform_gettimeoffset	ia64_mv.gettimeoffset
+#  define platform_timer_interrupt	ia64_mv.timer_interrupt
 # endif
 
 struct ia64_machine_vector {
@@ -151,6 +155,8 @@
 	ia64_mv_outb_t *outb;
 	ia64_mv_outw_t *outw;
 	ia64_mv_outl_t *outl;
+	ia64_mv_gettimeoffset_t *gettimeoffset;
+	ia64_mv_timer_interrupt_t *timer_interrupt;
 };
 
 #define MACHVEC_INIT(name)			\
@@ -187,7 +193,9 @@
 	platform_inl,				\
 	platform_outb,				\
 	platform_outw,				\
-	platform_outl				\
+	platform_outl,				\
+	platform_gettimeoffset,			\
+	platform_timer_interrupt		\
 }
 
 extern struct ia64_machine_vector ia64_mv;
@@ -211,6 +219,8 @@
 extern ia64_mv_pci_dma_sync_single swiotlb_sync_single;
 extern ia64_mv_pci_dma_sync_sg swiotlb_sync_sg;
 extern ia64_mv_pci_dma_supported swiotlb_pci_dma_supported;
+extern ia64_mv_gettimeoffset_t __ia64_gettimeoffset;
+extern void __ia64_timer_interrupt(int, void *, struct pt_regs *);
 
 /*
  * Define default versions so we can extend machvec for new platforms without having
@@ -309,5 +319,11 @@
 #ifndef platform_outl
 # define platform_outl		__ia64_outl
 #endif
+#ifndef platform_gettimeoffset
+# define platform_gettimeoffset	__ia64_gettimeoffset
+#endif
+#ifndef platform_timer_interrupt
+# define platform_timer_interrupt	__ia64_timer_interrupt
+#endif
 
 #endif /* _ASM_IA64_MACHVEC_H */
diff -X /home/jes/exclude-linux -urN linux-2.4.21-pre5-030312-pretimeofday/include/asm-ia64/machvec_sn2.h linux-2.4.21-pre5-030312/include/asm-ia64/machvec_sn2.h
--- linux-2.4.21-pre5-030312-pretimeofday/include/asm-ia64/machvec_sn2.h	Sun Mar 16 10:55:27 2003
+++ linux-2.4.21-pre5-030312/include/asm-ia64/machvec_sn2.h	Fri Apr  4 19:23:57 2003
@@ -59,6 +59,8 @@
 extern ia64_mv_pci_dma_sync_single	sn_pci_dma_sync_single;
 extern ia64_mv_pci_dma_sync_sg		sn_pci_dma_sync_sg;
 extern ia64_mv_pci_dma_supported	sn_pci_dma_supported;
+extern ia64_mv_gettimeoffset_t		sn_gettimeoffset;
+extern ia64_mv_timer_interrupt_t	sn_timer_interrupt;
 
 /*
  * This stuff has dual use!
@@ -95,5 +97,7 @@
 #define platform_pci_dma_sync_single	sn_pci_dma_sync_single
 #define platform_pci_dma_sync_sg	sn_pci_dma_sync_sg
 #define platform_pci_dma_supported	sn_pci_dma_supported
+#define platform_gettimeoffset		sn_gettimeoffset
+#define platform_timer_interrupt	sn_timer_interrupt
 
 #endif /* _ASM_IA64_MACHVEC_SN2_H */
diff -X /home/jes/exclude-linux -urN linux-2.4.21-pre5-030312-pretimeofday/include/asm-ia64/sn/clksupport.h linux-2.4.21-pre5-030312/include/asm-ia64/sn/clksupport.h
--- linux-2.4.21-pre5-030312-pretimeofday/include/asm-ia64/sn/clksupport.h	Wed Mar 12 17:24:59 2003
+++ linux-2.4.21-pre5-030312/include/asm-ia64/sn/clksupport.h	Sun Apr  6 18:59:05 2003
@@ -38,7 +38,7 @@
 
 extern nasid_t master_nasid;
 
-#define RTC_MASK		(0x007fffffffffffff)
+#define RTC_MASK		0x007fffffffffffffUL
 /* clocks are not synchronized yet on SN1  - used node 0 (problem if no NASID 0) */
 #define RTC_COUNTER_ADDR	((clkreg_t*)REMOTE_HUB_ADDR(master_nasid, PI_RT_COUNTER))
 #define RTC_COMPARE_A_ADDR      ((clkreg_t*)REMOTE_HUB_ADDR(master_nasid, PI_RT_COMPARE_A))
@@ -52,7 +52,7 @@
 #include <asm/sn/sn2/addrs.h>
 #include <asm/sn/sn2/shubio.h>
 #include <asm/sn/sn2/shub_mmr.h>
-#define RTC_MASK		(SH_RTC_MASK)
+#define RTC_MASK		SH_RTC_MASK
 #define RTC_COUNTER_ADDR	((clkreg_t*)LOCAL_MMR_ADDR(SH_RTC))
 #define RTC_COMPARE_A_ADDR      ((clkreg_t*)LOCAL_MMR_ADDR(SH_RTC))
 #define RTC_COMPARE_B_ADDR      ((clkreg_t*)LOCAL_MMR_ADDR(SH_RTC))
@@ -62,7 +62,7 @@
 #define RTC_INT_ENABLED_B_ADDR  ((clkreg_t*)LOCAL_MMR_ADDR(SH_RTC))
 #endif	/* CONFIG_IA64_SGI_SN1 */
 
-
+#define SN_RTC_PER_ITC_SHIFT	34
 #define GET_RTC_COUNTER()	(*RTC_COUNTER_ADDR)
 #define rtc_time()		GET_RTC_COUNTER()
 
diff -X /home/jes/exclude-linux -urN linux-2.4.21-pre5-030312-pretimeofday/include/asm-ia64/timex.h linux-2.4.21-pre5-030312/include/asm-ia64/timex.h
--- linux-2.4.21-pre5-030312-pretimeofday/include/asm-ia64/timex.h	Thu Nov 28 18:53:15 2002
+++ linux-2.4.21-pre5-030312/include/asm-ia64/timex.h	Fri Apr  4 15:07:17 2003
@@ -24,4 +24,6 @@
 #define vxtime_lock()		do {} while (0)
 #define vxtime_unlock()		do {} while (0)
 
+#define gettimeoffset()		platform_gettimeoffset()
+
 #endif /* _ASM_IA64_TIMEX_H */


             reply	other threads:[~2003-04-08 20:35 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2003-04-08 20:35 Jes Sorensen [this message]
2003-04-08 21:41 ` [Linux-ia64] [patch] 2.4 timer_interrupt/gettimeoffset machvec David Mosberger
2003-04-08 21:49 ` Jesse Barnes
2003-04-09 17:53 ` David Mosberger
2003-04-09 18:43 ` Grant Grundler
2003-04-14 23:22 ` Jesse Barnes
2003-04-17 23:02 ` Jes Sorensen
2003-04-17 23:43 ` David Mosberger
2003-04-18  0:00 ` Jes Sorensen
2003-04-18  1:21 ` Jes Sorensen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=marc-linux-ia64-105590723705438@msgid-missing \
    --to=jes@wildopensource.com \
    --cc=linux-ia64@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.