Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Christopher S. Hall and committed by
John Stultz
8006c245 ba26621e

+91
+35
include/linux/timekeeping.h
··· 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 + */ 289 + struct 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 + */ 302 + struct 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 + */ 310 + extern 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 318 * Simultaneously snapshot realtime and monotonic raw clocks 284 319 */ 285 320 extern void ktime_get_snapshot(struct system_time_snapshot *systime_snapshot);
+56
kernel/time/timekeeping.c
··· 908 908 EXPORT_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 + */ 918 + int 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 + } 964 + EXPORT_SYMBOL_GPL(get_device_system_crosststamp); 965 + 966 + /** 911 967 * do_gettimeofday - Returns the time of day in a timeval 912 968 * @tv: pointer to the timeval to be set 913 969 *