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

time: Add history to cross timestamp interface supporting slower devices

Another representative use case of time sync and the correlated
clocksource (in addition to PTP noted above) is PTP synchronized
audio.

In a streaming application, as an example, samples will be sent and/or
received by multiple devices with a presentation time that is in terms
of the PTP master clock. Synchronizing the audio output on these
devices requires correlating the audio clock with the PTP master
clock. The more precise this correlation is, the better the audio
quality (i.e. out of sync audio sounds bad).

From an application standpoint, to correlate the PTP master clock with
the audio device clock, the system clock is used as a intermediate
timebase. The transforms such an application would perform are:

System Clock <-> Audio clock
System Clock <-> Network Device Clock [<-> PTP Master Clock]

Modern Intel platforms can perform a more accurate cross timestamp in
hardware (ART,audio device clock). The audio driver requires
ART->system time transforms -- the same as required for the network
driver. These platforms offload audio processing (including
cross-timestamps) to a DSP which to ensure uninterrupted audio
processing, communicates and response to the host only once every
millsecond. As a result is takes up to a millisecond for the DSP to
receive a request, the request is processed by the DSP, the audio
output hardware is polled for completion, the result is copied into
shared memory, and the host is notified. All of these operation occur
on a millisecond cadence. This transaction requires about 2 ms, but
under heavier workloads it may take up to 4 ms.

Adding a history allows these slow devices the option of providing an
ART value outside of the current interval. In this case, the callback
provided is an accessor function for the previously obtained counter
value. If get_system_device_crosststamp() receives a counter value
previous to cycle_last, it consults the history provided as an
argument in history_ref and interpolates the realtime and monotonic
raw system time using the provided counter value. If there are any
clock discontinuities, e.g. from calling settimeofday(), the monotonic
raw time is interpolated in the usual way, but the realtime clock time
is adjusted by scaling the monotonic raw adjustment.

When an accessor function is used a history argument *must* be
provided. The history is initialized using ktime_get_snapshot() and
must be called before the counter values are read.

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: Fixed up cycles_t/cycle_t type confusion]
Signed-off-by: John Stultz <john.stultz@linaro.org>

authored by

Christopher S. Hall and committed by
John Stultz
2c756feb 8006c245

+177 -1
+2
include/linux/timekeeper_internal.h
··· 50 50 * @offs_tai: Offset clock monotonic -> clock tai 51 51 * @tai_offset: The current UTC to TAI offset in seconds 52 52 * @clock_was_set_seq: The sequence number of clock was set events 53 + * @cs_was_changed_seq: The sequence number of clocksource change events 53 54 * @next_leap_ktime: CLOCK_MONOTONIC time value of a pending leap-second 54 55 * @raw_time: Monotonic raw base time in timespec64 format 55 56 * @cycle_interval: Number of clock cycles in one NTP interval ··· 92 91 ktime_t offs_tai; 93 92 s32 tai_offset; 94 93 unsigned int clock_was_set_seq; 94 + u8 cs_was_changed_seq; 95 95 ktime_t next_leap_ktime; 96 96 struct timespec64 raw_time; 97 97
+5
include/linux/timekeeping.h
··· 272 272 * @cycles: Clocksource counter value to produce the system times 273 273 * @real: Realtime system time 274 274 * @raw: Monotonic raw system time 275 + * @clock_was_set_seq: The sequence number of clock was set events 276 + * @cs_was_changed_seq: The sequence number of clocksource change events 275 277 */ 276 278 struct system_time_snapshot { 277 279 cycle_t cycles; 278 280 ktime_t real; 279 281 ktime_t raw; 282 + unsigned int clock_was_set_seq; 283 + u8 cs_was_changed_seq; 280 284 }; 281 285 282 286 /* ··· 316 312 struct system_counterval_t *system_counterval, 317 313 void *ctx), 318 314 void *ctx, 315 + struct system_time_snapshot *history, 319 316 struct system_device_crosststamp *xtstamp); 320 317 321 318 /*
+170 -1
kernel/time/timekeeping.c
··· 233 233 u64 tmp, ntpinterval; 234 234 struct clocksource *old_clock; 235 235 236 + ++tk->cs_was_changed_seq; 236 237 old_clock = tk->tkr_mono.clock; 237 238 tk->tkr_mono.clock = clock; 238 239 tk->tkr_mono.read = clock->read; ··· 895 894 seq = read_seqcount_begin(&tk_core.seq); 896 895 897 896 now = tk->tkr_mono.read(tk->tkr_mono.clock); 897 + systime_snapshot->cs_was_changed_seq = tk->cs_was_changed_seq; 898 + systime_snapshot->clock_was_set_seq = tk->clock_was_set_seq; 898 899 base_real = ktime_add(tk->tkr_mono.base, 899 900 tk_core.timekeeper.offs_real); 900 901 base_raw = tk->tkr_raw.base; ··· 910 907 } 911 908 EXPORT_SYMBOL_GPL(ktime_get_snapshot); 912 909 910 + /* Scale base by mult/div checking for overflow */ 911 + static int scale64_check_overflow(u64 mult, u64 div, u64 *base) 912 + { 913 + u64 tmp, rem; 914 + 915 + tmp = div64_u64_rem(*base, div, &rem); 916 + 917 + if (((int)sizeof(u64)*8 - fls64(mult) < fls64(tmp)) || 918 + ((int)sizeof(u64)*8 - fls64(mult) < fls64(rem))) 919 + return -EOVERFLOW; 920 + tmp *= mult; 921 + rem *= mult; 922 + 923 + do_div(rem, div); 924 + *base = tmp + rem; 925 + return 0; 926 + } 927 + 928 + /** 929 + * adjust_historical_crosststamp - adjust crosstimestamp previous to current interval 930 + * @history: Snapshot representing start of history 931 + * @partial_history_cycles: Cycle offset into history (fractional part) 932 + * @total_history_cycles: Total history length in cycles 933 + * @discontinuity: True indicates clock was set on history period 934 + * @ts: Cross timestamp that should be adjusted using 935 + * partial/total ratio 936 + * 937 + * Helper function used by get_device_system_crosststamp() to correct the 938 + * crosstimestamp corresponding to the start of the current interval to the 939 + * system counter value (timestamp point) provided by the driver. The 940 + * total_history_* quantities are the total history starting at the provided 941 + * reference point and ending at the start of the current interval. The cycle 942 + * count between the driver timestamp point and the start of the current 943 + * interval is partial_history_cycles. 944 + */ 945 + static int adjust_historical_crosststamp(struct system_time_snapshot *history, 946 + cycle_t partial_history_cycles, 947 + cycle_t total_history_cycles, 948 + bool discontinuity, 949 + struct system_device_crosststamp *ts) 950 + { 951 + struct timekeeper *tk = &tk_core.timekeeper; 952 + u64 corr_raw, corr_real; 953 + bool interp_forward; 954 + int ret; 955 + 956 + if (total_history_cycles == 0 || partial_history_cycles == 0) 957 + return 0; 958 + 959 + /* Interpolate shortest distance from beginning or end of history */ 960 + interp_forward = partial_history_cycles > total_history_cycles/2 ? 961 + true : false; 962 + partial_history_cycles = interp_forward ? 963 + total_history_cycles - partial_history_cycles : 964 + partial_history_cycles; 965 + 966 + /* 967 + * Scale the monotonic raw time delta by: 968 + * partial_history_cycles / total_history_cycles 969 + */ 970 + corr_raw = (u64)ktime_to_ns( 971 + ktime_sub(ts->sys_monoraw, history->raw)); 972 + ret = scale64_check_overflow(partial_history_cycles, 973 + total_history_cycles, &corr_raw); 974 + if (ret) 975 + return ret; 976 + 977 + /* 978 + * If there is a discontinuity in the history, scale monotonic raw 979 + * correction by: 980 + * mult(real)/mult(raw) yielding the realtime correction 981 + * Otherwise, calculate the realtime correction similar to monotonic 982 + * raw calculation 983 + */ 984 + if (discontinuity) { 985 + corr_real = mul_u64_u32_div 986 + (corr_raw, tk->tkr_mono.mult, tk->tkr_raw.mult); 987 + } else { 988 + corr_real = (u64)ktime_to_ns( 989 + ktime_sub(ts->sys_realtime, history->real)); 990 + ret = scale64_check_overflow(partial_history_cycles, 991 + total_history_cycles, &corr_real); 992 + if (ret) 993 + return ret; 994 + } 995 + 996 + /* Fixup monotonic raw and real time time values */ 997 + if (interp_forward) { 998 + ts->sys_monoraw = ktime_add_ns(history->raw, corr_raw); 999 + ts->sys_realtime = ktime_add_ns(history->real, corr_real); 1000 + } else { 1001 + ts->sys_monoraw = ktime_sub_ns(ts->sys_monoraw, corr_raw); 1002 + ts->sys_realtime = ktime_sub_ns(ts->sys_realtime, corr_real); 1003 + } 1004 + 1005 + return 0; 1006 + } 1007 + 1008 + /* 1009 + * cycle_between - true if test occurs chronologically between before and after 1010 + */ 1011 + static bool cycle_between(cycle_t before, cycle_t test, cycle_t after) 1012 + { 1013 + if (test > before && test < after) 1014 + return true; 1015 + if (test < before && before > after) 1016 + return true; 1017 + return false; 1018 + } 1019 + 913 1020 /** 914 1021 * get_device_system_crosststamp - Synchronously capture system/device timestamp 915 - * @sync_devicetime: Callback to get simultaneous device time and 1022 + * @get_time_fn: Callback to get simultaneous device time and 916 1023 * system counter from the device driver 1024 + * @ctx: Context passed to get_time_fn() 1025 + * @history_begin: Historical reference point used to interpolate system 1026 + * time when counter provided by the driver is before the current interval 917 1027 * @xtstamp: Receives simultaneously captured system and device time 918 1028 * 919 1029 * Reads a timestamp from a device and correlates it to system time ··· 1036 920 struct system_counterval_t *sys_counterval, 1037 921 void *ctx), 1038 922 void *ctx, 923 + struct system_time_snapshot *history_begin, 1039 924 struct system_device_crosststamp *xtstamp) 1040 925 { 1041 926 struct system_counterval_t system_counterval; 1042 927 struct timekeeper *tk = &tk_core.timekeeper; 928 + cycle_t cycles, now, interval_start; 929 + unsigned int clock_was_set_seq; 1043 930 ktime_t base_real, base_raw; 1044 931 s64 nsec_real, nsec_raw; 932 + u8 cs_was_changed_seq; 1045 933 unsigned long seq; 934 + bool do_interp; 1046 935 int ret; 1047 936 1048 937 do { ··· 1067 946 */ 1068 947 if (tk->tkr_mono.clock != system_counterval.cs) 1069 948 return -ENODEV; 949 + cycles = system_counterval.cycles; 950 + 951 + /* 952 + * Check whether the system counter value provided by the 953 + * device driver is on the current timekeeping interval. 954 + */ 955 + now = tk->tkr_mono.read(tk->tkr_mono.clock); 956 + interval_start = tk->tkr_mono.cycle_last; 957 + if (!cycle_between(interval_start, cycles, now)) { 958 + clock_was_set_seq = tk->clock_was_set_seq; 959 + cs_was_changed_seq = tk->cs_was_changed_seq; 960 + cycles = interval_start; 961 + do_interp = true; 962 + } else { 963 + do_interp = false; 964 + } 1070 965 1071 966 base_real = ktime_add(tk->tkr_mono.base, 1072 967 tk_core.timekeeper.offs_real); ··· 1096 959 1097 960 xtstamp->sys_realtime = ktime_add_ns(base_real, nsec_real); 1098 961 xtstamp->sys_monoraw = ktime_add_ns(base_raw, nsec_raw); 962 + 963 + /* 964 + * Interpolate if necessary, adjusting back from the start of the 965 + * current interval 966 + */ 967 + if (do_interp) { 968 + cycle_t partial_history_cycles, total_history_cycles; 969 + bool discontinuity; 970 + 971 + /* 972 + * Check that the counter value occurs after the provided 973 + * history reference and that the history doesn't cross a 974 + * clocksource change 975 + */ 976 + if (!history_begin || 977 + !cycle_between(history_begin->cycles, 978 + system_counterval.cycles, cycles) || 979 + history_begin->cs_was_changed_seq != cs_was_changed_seq) 980 + return -EINVAL; 981 + partial_history_cycles = cycles - system_counterval.cycles; 982 + total_history_cycles = cycles - history_begin->cycles; 983 + discontinuity = 984 + history_begin->clock_was_set_seq != clock_was_set_seq; 985 + 986 + ret = adjust_historical_crosststamp(history_begin, 987 + partial_history_cycles, 988 + total_history_cycles, 989 + discontinuity, xtstamp); 990 + if (ret) 991 + return ret; 992 + } 993 + 1099 994 return 0; 1100 995 } 1101 996 EXPORT_SYMBOL_GPL(get_device_system_crosststamp);