aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristopher S. Hall <christopher.s.hall@intel.com>2016-02-22 06:15:22 -0500
committerJohn Stultz <john.stultz@linaro.org>2016-03-02 20:13:10 -0500
commit8006c24595cab106bcb9da12d35e32e14ff492df (patch)
tree528cbddb843192ebc465d543fa35f1011f8cac42
parentba26621e63ce6dc481d90ab9f6902e058d4ea39a (diff)
time: Add driver cross timestamp interface for higher precision time synchronization
ACKNOWLEDGMENT: cross timestamp code was developed by Thomas Gleixner <tglx@linutronix.de>. It has changed considerably and any mistakes are mine. The precision with which events on multiple networked systems can be synchronized using, as an example, PTP (IEEE 1588, 802.1AS) is limited by the precision of the cross timestamps between the system clock and the device (timestamp) clock. Precision here is the degree of simultaneity when capturing the cross timestamp. Currently the PTP cross timestamp is captured in software using the PTP device driver ioctl PTP_SYS_OFFSET. Reads of the device clock are interleaved with reads of the realtime clock. At best, the precision of this cross timestamp is on the order of several microseconds due to software latencies. Sub-microsecond precision is required for industrial control and some media applications. To achieve this level of precision hardware supported cross timestamping is needed. The function get_device_system_crosstimestamp() allows device drivers to return a cross timestamp with system time properly scaled to nanoseconds. The realtime value is needed to discipline that clock using PTP and the monotonic raw value is used for applications that don't require a "real" time, but need an unadjusted clock time. The get_device_system_crosstimestamp() code calls back into the driver to ensure that the system counter is within the current timekeeping update interval. Modern Intel hardware provides an Always Running Timer (ART) which is exactly related to TSC through a known frequency ratio. The ART is routed to devices on the system and is used to precisely and simultaneously capture the device clock with the ART. Cc: Prarit Bhargava <prarit@redhat.com> Cc: Richard Cochran <richardcochran@gmail.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Ingo Molnar <mingo@kernel.org> Cc: Andy Lutomirski <luto@amacapital.net> Cc: kevin.b.stanton@intel.com Cc: kevin.j.clarke@intel.com Cc: hpa@zytor.com Cc: jeffrey.t.kirsher@intel.com Cc: netdev@vger.kernel.org Reviewed-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Christopher S. Hall <christopher.s.hall@intel.com> [jstultz: Reworked to remove extra structures and simplify calling] Signed-off-by: John Stultz <john.stultz@linaro.org>
-rw-r--r--include/linux/timekeeping.h35
-rw-r--r--kernel/time/timekeeping.c56
2 files changed, 91 insertions, 0 deletions
diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h
index 7817591af46f..4a2ca65fc778 100644
--- a/include/linux/timekeeping.h
+++ b/include/linux/timekeeping.h
@@ -280,6 +280,41 @@ struct system_time_snapshot {
280}; 280};
281 281
282/* 282/*
283 * struct system_device_crosststamp - system/device cross-timestamp
284 * (syncronized capture)
285 * @device: Device time
286 * @sys_realtime: Realtime simultaneous with device time
287 * @sys_monoraw: Monotonic raw simultaneous with device time
288 */
289struct system_device_crosststamp {
290 ktime_t device;
291 ktime_t sys_realtime;
292 ktime_t sys_monoraw;
293};
294
295/*
296 * struct system_counterval_t - system counter value with the pointer to the
297 * corresponding clocksource
298 * @cycles: System counter value
299 * @cs: Clocksource corresponding to system counter value. Used by
300 * timekeeping code to verify comparibility of two cycle values
301 */
302struct system_counterval_t {
303 cycle_t cycles;
304 struct clocksource *cs;
305};
306
307/*
308 * Get cross timestamp between system clock and device clock
309 */
310extern int get_device_system_crosststamp(
311 int (*get_time_fn)(ktime_t *device_time,
312 struct system_counterval_t *system_counterval,
313 void *ctx),
314 void *ctx,
315 struct system_device_crosststamp *xtstamp);
316
317/*
283 * Simultaneously snapshot realtime and monotonic raw clocks 318 * Simultaneously snapshot realtime and monotonic raw clocks
284 */ 319 */
285extern void ktime_get_snapshot(struct system_time_snapshot *systime_snapshot); 320extern void ktime_get_snapshot(struct system_time_snapshot *systime_snapshot);
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index af19a49d5223..dba595cdb200 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -908,6 +908,62 @@ void ktime_get_snapshot(struct system_time_snapshot *systime_snapshot)
908EXPORT_SYMBOL_GPL(ktime_get_snapshot); 908EXPORT_SYMBOL_GPL(ktime_get_snapshot);
909 909
910/** 910/**
911 * get_device_system_crosststamp - Synchronously capture system/device timestamp
912 * @sync_devicetime: Callback to get simultaneous device time and
913 * system counter from the device driver
914 * @xtstamp: Receives simultaneously captured system and device time
915 *
916 * Reads a timestamp from a device and correlates it to system time
917 */
918int get_device_system_crosststamp(int (*get_time_fn)
919 (ktime_t *device_time,
920 struct system_counterval_t *sys_counterval,
921 void *ctx),
922 void *ctx,
923 struct system_device_crosststamp *xtstamp)
924{
925 struct system_counterval_t system_counterval;
926 struct timekeeper *tk = &tk_core.timekeeper;
927 ktime_t base_real, base_raw;
928 s64 nsec_real, nsec_raw;
929 unsigned long seq;
930 int ret;
931
932 do {
933 seq = read_seqcount_begin(&tk_core.seq);
934 /*
935 * Try to synchronously capture device time and a system
936 * counter value calling back into the device driver
937 */
938 ret = get_time_fn(&xtstamp->device, &system_counterval, ctx);
939 if (ret)
940 return ret;
941
942 /*
943 * Verify that the clocksource associated with the captured
944 * system counter value is the same as the currently installed
945 * timekeeper clocksource
946 */
947 if (tk->tkr_mono.clock != system_counterval.cs)
948 return -ENODEV;
949
950 base_real = ktime_add(tk->tkr_mono.base,
951 tk_core.timekeeper.offs_real);
952 base_raw = tk->tkr_raw.base;
953
954 nsec_real = timekeeping_cycles_to_ns(&tk->tkr_mono,
955 system_counterval.cycles);
956 nsec_raw = timekeeping_cycles_to_ns(&tk->tkr_raw,
957 system_counterval.cycles);
958 } while (read_seqcount_retry(&tk_core.seq, seq));
959
960 xtstamp->sys_realtime = ktime_add_ns(base_real, nsec_real);
961 xtstamp->sys_monoraw = ktime_add_ns(base_raw, nsec_raw);
962 return 0;
963}
964EXPORT_SYMBOL_GPL(get_device_system_crosststamp);
965
966/**
911 * do_gettimeofday - Returns the time of day in a timeval 967 * do_gettimeofday - Returns the time of day in a timeval
912 * @tv: pointer to the timeval to be set 968 * @tv: pointer to the timeval to be set
913 * 969 *