[LinuxPPS] [PATCH 2/3] pps: capture MONOTONIC_RAW timestamps as well

Alexander Gordeev lasaine at lvk.cs.msu.su
Thu Oct 1 22:37:02 CEST 2009


MONOTONIC_RAW clock timestamps are ideally suited for frequency
calculation and also fit well into the original NTP hardpps design. Now
phase and frequency can be adjusted separately: the former based on
REALTIME clock and the latter based on MONOTONIC_RAW clock. This way
phase can be adjusted using an old good adjtime which makes its job much
faster then full PLL/FLL.
A new function getnstime_raw_and_real is added to timekeeping subsystem
to capture both timestamps at the same time and atomically..

Signed-off-by: Alexander Gordeev <lasaine at lvk.cs.msu.su>
---
 arch/x86/kernel/irq_32.c          |   13 +++++++----
 arch/x86/kernel/irq_64.c          |   13 +++++++----
 drivers/pps/clients/ktimer.c      |    6 ++--
 drivers/pps/clients/pps-ldisc.c   |   11 +++++----
 drivers/pps/clients/pps_parport.c |   34 +++++++++++++++++---------------
 drivers/pps/kapi.c                |   15 ++++++++++++-
 include/linux/pps.h               |    7 ++++-
 include/linux/serial_core.h       |    9 ++++---
 include/linux/time.h              |    1 +
 include/linux/tty_ldisc.h         |    6 ++--
 kernel/time/timekeeping.c         |   39 +++++++++++++++++++++++++++++++++++++
 11 files changed, 109 insertions(+), 45 deletions(-)

diff --git a/arch/x86/kernel/irq_32.c b/arch/x86/kernel/irq_32.c
index e57d4f5..a4ccb3f 100644
--- a/arch/x86/kernel/irq_32.c
+++ b/arch/x86/kernel/irq_32.c
@@ -28,8 +28,10 @@ DEFINE_PER_CPU(struct pt_regs *, irq_regs);
 EXPORT_PER_CPU_SYMBOL(irq_regs);
 
 #ifdef CONFIG_PPS_IRQ_EVENTS
-struct timespec pps_irq_ts[NR_IRQS];
-EXPORT_SYMBOL(pps_irq_ts);
+struct timespec pps_irq_ts_real[NR_IRQS];
+EXPORT_SYMBOL(pps_irq_ts_real);
+struct timespec pps_irq_ts_mono_raw[NR_IRQS];
+EXPORT_SYMBOL(pps_irq_ts_mono_raw);
 #endif
 
 #ifdef CONFIG_DEBUG_STACKOVERFLOW
@@ -204,10 +206,10 @@ bool handle_irq(unsigned irq, struct pt_regs *regs)
 	int overflow;
 
 #ifdef CONFIG_PPS_IRQ_EVENTS
-	struct timespec ts;
+	struct timespec ts_mono_raw, ts_real;
 
 	/* Get IRQ timestamps as soon as possible for the PPS layer */
-	getnstimeofday(&ts);
+	getnstime_raw_and_real(&ts_mono_raw, &ts_real);
 #endif
 
 	overflow = check_stack_overflow();
@@ -218,7 +220,8 @@ bool handle_irq(unsigned irq, struct pt_regs *regs)
 
 #ifdef CONFIG_PPS_IRQ_EVENTS
 	/* Then, after sanity check, store the IRQ timestamp */
-	pps_irq_ts[irq] = ts;
+	pps_irq_ts_mono_raw[irq] = ts_mono_raw;
+	pps_irq_ts_real[irq] = ts_real;
 #endif
 
 	if (!execute_on_irq_stack(overflow, desc, irq)) {
diff --git a/arch/x86/kernel/irq_64.c b/arch/x86/kernel/irq_64.c
index aeabd9e..c217e01 100644
--- a/arch/x86/kernel/irq_64.c
+++ b/arch/x86/kernel/irq_64.c
@@ -28,8 +28,10 @@ DEFINE_PER_CPU(struct pt_regs *, irq_regs);
 EXPORT_PER_CPU_SYMBOL(irq_regs);
 
 #ifdef CONFIG_PPS_IRQ_EVENTS
-struct timespec pps_irq_ts[NR_IRQS];
-EXPORT_SYMBOL(pps_irq_ts);
+struct timespec pps_irq_ts_real[NR_IRQS];
+EXPORT_SYMBOL(pps_irq_ts_real);
+struct timespec pps_irq_ts_mono_raw[NR_IRQS];
+EXPORT_SYMBOL(pps_irq_ts_mono_raw);
 #endif
 
 /*
@@ -58,10 +60,10 @@ bool handle_irq(unsigned irq, struct pt_regs *regs)
 {
 	struct irq_desc *desc;
 #ifdef CONFIG_PPS_IRQ_EVENTS
-	struct timespec ts;
+	struct timespec ts_mono_raw, ts_real;
 
 	/* Get IRQ timestamps as soon as possible for the PPS layer */
-	getnstimeofday(&ts);
+	getnstime_raw_and_real(&ts_mono_raw, &ts_real);
 #endif
 
 	stack_overflow_check(regs);
@@ -72,7 +74,8 @@ bool handle_irq(unsigned irq, struct pt_regs *regs)
 
 #ifdef CONFIG_PPS_IRQ_EVENTS
 	/* Then, after sanity check, store the IRQ timestamp */
-	pps_irq_ts[irq] = ts;
+	pps_irq_ts_mono_raw[irq] = ts_mono_raw;
+	pps_irq_ts_real[irq] = ts_real;
 #endif
 
 	generic_handle_irq_desc(irq, desc);
diff --git a/drivers/pps/clients/ktimer.c b/drivers/pps/clients/ktimer.c
index 259baa7..439a5c1 100644
--- a/drivers/pps/clients/ktimer.c
+++ b/drivers/pps/clients/ktimer.c
@@ -41,11 +41,11 @@ static struct timer_list ktimer;
 
 static void pps_ktimer_event(unsigned long ptr)
 {
-	struct timespec __ts;
+	struct timespec __ts, __ts_raw;
 	struct pps_ktime ts;
 
 	/* First of all we get the time stamp... */
-	getnstimeofday(&__ts);
+	getnstime_raw_and_real(&__ts_raw, &__ts);
 
 	pr_info("PPS event at %lu\n", jiffies);
 
@@ -53,7 +53,7 @@ static void pps_ktimer_event(unsigned long ptr)
 	ts.sec = __ts.tv_sec;
 	ts.nsec = __ts.tv_nsec;
 
-	pps_event(source, &ts, PPS_CAPTUREASSERT, NULL);
+	pps_event(source, &ts, &__ts_raw, PPS_CAPTUREASSERT, NULL);
 
 	mod_timer(&ktimer, jiffies + HZ);
 }
diff --git a/drivers/pps/clients/pps-ldisc.c b/drivers/pps/clients/pps-ldisc.c
index f16396b..f35b317 100644
--- a/drivers/pps/clients/pps-ldisc.c
+++ b/drivers/pps/clients/pps-ldisc.c
@@ -27,26 +27,27 @@
 #define PPS_TTY_MAGIC		0x0001
 
 static void pps_tty_dcd_change(struct tty_struct *tty, unsigned int status,
-				struct timespec *ts)
+				struct timespec *ts, struct timespec *ts_raw)
 {
 	long id = (long) tty->disc_data;
-	struct timespec __ts;
+	struct timespec __ts, __ts_raw;
 	struct pps_ktime pps_ts;
 
 	/* First of all we get the time stamp... */
-	getnstimeofday(&__ts);
+	getnstime_raw_and_real(&__ts_raw, &__ts);
 
 	/* Does caller give us a timestamp? */
-	if (ts) {	/* Yes. Let's use it! */
+	if (ts && ts_raw) {	/* Yes. Let's use it! */
 		pps_ts.sec = ts->tv_sec;
 		pps_ts.nsec = ts->tv_nsec;
+		__ts_raw = *ts_raw;
 	} else {	/* No. Do it ourself! */
 		pps_ts.sec = __ts.tv_sec;
 		pps_ts.nsec = __ts.tv_nsec;
 	}
 
 	/* Now do the PPS event report */
-	pps_event(id, &pps_ts, status ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR,
+	pps_event(id, &pps_ts, &__ts_raw, status ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR,
 			NULL);
 
 	pr_debug("PPS %s at %lu on source #%d\n",
diff --git a/drivers/pps/clients/pps_parport.c b/drivers/pps/clients/pps_parport.c
index 8ade87c..ae38168 100644
--- a/drivers/pps/clients/pps_parport.c
+++ b/drivers/pps/clients/pps_parport.c
@@ -131,7 +131,7 @@ static struct pps_client_pp device = {
 	}
 };
 
-/* calibrated getnstimeofday time */
+/* calibrated getnstime_raw_and_real time */
 unsigned long gntd_time;
 
 /* calibrate port write time */
@@ -149,14 +149,14 @@ static void calibrate_port(struct pps_client_pp *dev)
 
 	for (i = 0; i < PORT_NTESTS; i++)
 	{
-		struct timespec a, b;
+		struct timespec a, b, c;
 		unsigned long d;
 		unsigned long irq_flags;
 
 		local_irq_save(irq_flags);
-		getnstimeofday(&a);
+		getnstime_raw_and_real(&a, &c);
 		port->ops->read_status(port);
-		getnstimeofday(&b);
+		getnstime_raw_and_real(&b, &c);
 		local_irq_restore(irq_flags);
 
 		b = timespec_sub(b, a);
@@ -185,7 +185,7 @@ static inline void timespec_to_pps_ktime(struct pps_ktime *kt, struct timespec t
 /* parport interrupt handler */
 static void parport_irq(void *handle)
 {
-	struct timespec ts;
+	struct timespec ts_mono_raw, ts_real;
 	struct pps_ktime kt;
 	struct pps_client_pp *dev = handle;
 	struct parport *port = dev->pardev->port;
@@ -193,13 +193,14 @@ static void parport_irq(void *handle)
 
 	/* first of all we get the time stamp ... */
 #ifdef CONFIG_PPS_IRQ_EVENTS
-	ts = pps_irq_ts[port->irq];
+	ts_mono_raw = pps_irq_ts_mono_raw[port->irq];
+	ts_real = pps_irq_ts_real[port->irq];
 #else
-	getnstimeofday(&ts);
+	getnstime_raw_and_real(&ts_mono_raw, &ts_real);
 #endif
 
 	/* ... and translate it to PPS time data struct */
-	timespec_to_pps_ktime(&kt, ts);
+	timespec_to_pps_ktime(&kt, ts_real);
 
 	/* check the signal (no signal means the pulse is lost this time) */
 	if (!SIGNAL_IS_SET(port)) {
@@ -208,16 +209,16 @@ static void parport_irq(void *handle)
 	}
 
 	/* fire assert event */
-	pps_event(dev->source, &kt, PPS_CAPTUREASSERT, NULL);
+	pps_event(dev->source, &kt, &ts_mono_raw, PPS_CAPTUREASSERT, NULL);
 
 	/* poll the port until the signal is unset */
 	for (i = RECEIVE_TIMEOUT; i; i--)
 		if (!SIGNAL_IS_SET(port)) {
-			getnstimeofday(&ts);
-			set_normalized_timespec(&ts, ts.tv_sec, ts.tv_nsec - gntd_time);
-			timespec_to_pps_ktime(&kt, ts);
+			getnstime_raw_and_real(&ts_mono_raw, &ts_real);
+			set_normalized_timespec(&ts_real, ts_real.tv_sec, ts_real.tv_nsec - gntd_time);
+			timespec_to_pps_ktime(&kt, ts_real);
 			/* fire clear event */
-			pps_event(dev->source, &kt, PPS_CAPTURECLEAR, NULL);
+			pps_event(dev->source, &kt, &ts_mono_raw, PPS_CAPTURECLEAR, NULL);
 			return;
 		}
 
@@ -271,11 +272,12 @@ static struct parport_driver pps_parport_driver = {
 	.detach = parport_detach,
 };
 
-/* calibrate getnstimeofday time */
+/* calibrate getnstime_raw_and_real time */
 #define GNTD_NTESTS	30
 static void __init calibrate_gntd(void)
 {
 	static struct timespec gntd[GNTD_NTESTS + 1];
+	struct timespec t;
 	int i;
 	unsigned long irq_flags;
 	struct time_stat gntd_stat = {
@@ -288,7 +290,7 @@ static void __init calibrate_gntd(void)
 	local_irq_save(irq_flags);
 	for (i = 0; i <= GNTD_NTESTS; i++)
 	{
-		getnstimeofday(gntd + i);
+		getnstime_raw_and_real(gntd + i, &t);
 	}
 	local_irq_restore(irq_flags);
 
@@ -302,7 +304,7 @@ static void __init calibrate_gntd(void)
 	}
 	gntd_time = ts_getavg(&gntd_stat);
 	gntd_disp = ts_getdisp(&gntd_stat);
-	pr_info(DRVNAME ": getnstimeofday takes %ldns with %ldns dispersion\n", gntd_time, gntd_disp);
+	pr_info(DRVNAME ": getnstime_raw_and_real takes %ldns with %ldns dispersion\n", gntd_time, gntd_disp);
 }
 
 /* module staff */
diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c
index edb5315..8e0bcec 100644
--- a/drivers/pps/kapi.c
+++ b/drivers/pps/kapi.c
@@ -298,10 +298,11 @@ void hardpps(const struct timespec *p_ts, long nsec)
  *	pps->info.echo(source, event, data);
  */
 
-void pps_event(int source, struct pps_ktime *ts, int event, void *data)
+void pps_event(int source, struct pps_ktime *ts, struct timespec *ts_raw, int event, void *data)
 {
 	struct pps_device *pps;
 	unsigned long flags;
+	long nsec = 0;
 
 	if ((event & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR)) == 0) {
 		printk(KERN_ERR "pps: unknown event (%x) for source %d\n",
@@ -331,6 +332,11 @@ void pps_event(int source, struct pps_ktime *ts, int event, void *data)
 
 		/* Save the time stamp */
 		pps->assert_tu = *ts;
+		if (likely(pps->assert_tu_raw.tv_sec != 0)) {
+			struct timespec d = timespec_sub(*ts_raw, pps->assert_tu_raw);
+			nsec = timespec_to_ns(&d);
+		}
+		pps->assert_tu_raw = *ts_raw;
 		pps->assert_sequence++;
 		pr_debug("capture assert seq #%u for source %d\n",
 			pps->assert_sequence, source);
@@ -342,6 +348,11 @@ void pps_event(int source, struct pps_ktime *ts, int event, void *data)
 
 		/* Save the time stamp */
 		pps->clear_tu = *ts;
+		if (likely(pps->clear_tu_raw.tv_sec != 0)) {
+			struct timespec d = timespec_sub(*ts_raw, pps->clear_tu_raw);
+			nsec = timespec_to_ns(&d);
+		}
+		pps->clear_tu_raw = *ts_raw;
 		pps->clear_sequence++;
 		pr_debug("capture clear seq #%u for source %d\n",
 			pps->clear_sequence, source);
@@ -349,7 +360,7 @@ void pps_event(int source, struct pps_ktime *ts, int event, void *data)
 	spin_lock(&pps_kc_hardpps_lock);
 	if (pps == pps_kc_hardpps_dev && event & pps_kc_hardpps_mode) {
 		struct timespec p_ts = { .tv_sec = ts->sec, .tv_nsec = ts->nsec };
-		hardpps(&p_ts, 0);
+		hardpps(&p_ts, nsec);
 	}
 	spin_unlock(&pps_kc_hardpps_lock);
 
diff --git a/include/linux/pps.h b/include/linux/pps.h
index 558d679..a103ab0 100644
--- a/include/linux/pps.h
+++ b/include/linux/pps.h
@@ -159,6 +159,8 @@ struct pps_device {
 	__u32 clear_sequence;			/* PPS' clear event seq # */
 	struct pps_ktime assert_tu;
 	struct pps_ktime clear_tu;
+	struct timespec assert_tu_raw;
+	struct timespec clear_tu_raw;
 	int current_mode;			/* PPS mode at event time */
 
 	int go;					/* PPS event is arrived? */
@@ -180,7 +182,8 @@ struct pps_device {
 
 extern spinlock_t pps_idr_lock;
 extern struct idr pps_idr;
-extern struct timespec pps_irq_ts[];
+extern struct timespec pps_irq_ts_real[];
+extern struct timespec pps_irq_ts_mono_raw[];
 
 extern struct device_attribute pps_attrs[];
 
@@ -201,7 +204,7 @@ extern int pps_register_source(struct pps_source_info *info,
 extern void pps_unregister_source(int source);
 extern int pps_register_cdev(struct pps_device *pps);
 extern void pps_unregister_cdev(struct pps_device *pps);
-extern void pps_event(int source, struct pps_ktime *ts, int event, void *data);
+extern void pps_event(int source, struct pps_ktime *ts, struct timespec *ts_raw, int event, void *data);
 
 #endif /* __KERNEL__ */
 
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index bbe91b4..98856e9 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -510,13 +510,14 @@ uart_handle_dcd_change(struct uart_port *port, unsigned int status)
 {
 	struct uart_info *info = port->info;
 	struct tty_ldisc *ld = tty_ldisc_ref(info->port.tty);
-	struct timespec ts;
+	struct timespec ts, ts_raw;
 
 	if (ld && ld->ops->dcd_change)
 #ifdef CONFIG_PPS_IRQ_EVENTS
-		ts = pps_irq_ts[port->irq];
+		ts = pps_irq_ts_real[port->irq];
+		ts_raw = pps_irq_ts_mono_raw[port->irq];
 #else
-		getnstimeofday(&ts);
+		getnstime_raw_and_real(&ts_raw, &ts);
 #endif
 
 	port->icount.dcd++;
@@ -533,7 +534,7 @@ uart_handle_dcd_change(struct uart_port *port, unsigned int status)
 	}
 
 	if (ld && ld->ops->dcd_change)
-		ld->ops->dcd_change(info->port.tty, status, &ts);
+		ld->ops->dcd_change(info->port.tty, status, &ts, &ts_raw);
 	if (ld)
 		tty_ldisc_deref(ld);
 }
diff --git a/include/linux/time.h b/include/linux/time.h
index 74568fe..6308d65 100644
--- a/include/linux/time.h
+++ b/include/linux/time.h
@@ -125,6 +125,7 @@ extern unsigned int alarm_setitimer(unsigned int seconds);
 extern int do_getitimer(int which, struct itimerval *value);
 extern void getnstimeofday(struct timespec *tv);
 extern void getrawmonotonic(struct timespec *ts);
+extern void getnstime_raw_and_real(struct timespec *ts_mono_raw, struct timespec *ts_real);
 extern void getboottime(struct timespec *ts);
 extern void monotonic_to_bootbased(struct timespec *ts);
 
diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h
index 526fbf4..73e81ac 100644
--- a/include/linux/tty_ldisc.h
+++ b/include/linux/tty_ldisc.h
@@ -101,10 +101,10 @@
  *	any pending driver I/O is completed.
  *
  * void (*dcd_change)(struct tty_struct *tty, unsigned int status,
- * 			struct timespec *ts)
+ * 			struct timespec *ts, struct timespec *ts_raw)
  *
  *	Tells the discipline that the DCD pin has changed its status and
- *	the relative timestamp. Pointer ts can be NULL.
+ *	the relative timestamps. Pointers ts and ts_raw can be NULL.
  */
 
 #include <linux/fs.h>
@@ -143,7 +143,7 @@ struct tty_ldisc_ops {
 			       char *fp, int count);
 	void	(*write_wakeup)(struct tty_struct *);
 	void	(*dcd_change)(struct tty_struct *, unsigned int,
-				struct timespec *);
+				struct timespec *, struct timespec *);
 
 	struct  module *owner;
 	
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 00c02c4..3c7b5be 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -118,6 +118,45 @@ void getnstimeofday(struct timespec *ts)
 
 EXPORT_SYMBOL(getnstimeofday);
 
+/**
+ * getnstime_raw_and_real - Returns both the time of day an raw
+ * monotonic time in a timespec format
+ * @ts_mono_raw:	pointer to the timespec to be set to raw
+ * 			monotonic time
+ * @ts_real:		pointer to the timespec to be set to the time
+ * 			of day
+ */
+void getnstime_raw_and_real(struct timespec *ts_mono_raw, struct timespec *ts_real)
+{
+	cycle_t cycle_now, cycle_delta;
+	unsigned long seq;
+	s64 nsecs_mono_raw, nsecs_real;
+
+	WARN_ON(timekeeping_suspended);
+
+	do {
+		seq = read_seqbegin(&xtime_lock);
+
+		*ts_real = xtime;
+
+		/* read clocksource: */
+		cycle_now = clocksource_read(clock);
+		/* calculate the delta since the last update_wall_time: */
+		cycle_delta = (cycle_now - clock->cycle_last) & clock->mask;
+		/* convert to nanoseconds: */
+		nsecs_mono_raw = ((s64)cycle_delta * clock->mult_orig) >> clock->shift;
+		nsecs_real = cyc2ns(clock, cycle_delta);
+
+		*ts_mono_raw = clock->raw_time;
+
+	} while (read_seqretry(&xtime_lock, seq));
+
+	timespec_add_ns(ts_mono_raw, nsecs_mono_raw);
+	timespec_add_ns(ts_real, nsecs_real);
+}
+
+EXPORT_SYMBOL(getnstime_raw_and_real);
+
 ktime_t ktime_get(void)
 {
 	cycle_t cycle_now, cycle_delta;
-- 
1.6.3.3




More information about the LinuxPPS mailing list