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

timekeeping: Provide infrastructure for converting to/from a base clock

Hardware time stamps like provided by PTP clock implementations are based
on a clock which feeds both the PCIe device and the system clock. For
further processing the underlying hardwarre clock timestamp must be
converted to the system clock.

Right now this requires drivers to invoke an architecture specific
conversion function, e.g. to convert the ART (Always Running Timer)
timestamp to a TSC timestamp.

As the system clock is aware of the underlying base clock, this can be
moved to the core code by providing a base clock property for the system
clock which contains the conversion factors and assigning a clocksource ID
to the base clock.

Add the required data structures and the conversion infrastructure in the
core code to prepare for converting X86 and the related PTP drivers over.

[ tglx: Added a missing READ_ONCE(). Massaged change log ]

Co-developed-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Co-developed-by: Christopher S. Hall <christopher.s.hall@intel.com>
Signed-off-by: Christopher S. Hall <christopher.s.hall@intel.com>
Signed-off-by: Lakshmi Sowjanya D <lakshmi.sowjanya.d@intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/r/20240513103813.5666-2-lakshmi.sowjanya.d@intel.com

authored by

Lakshmi Sowjanya D and committed by
Thomas Gleixner
6b2e2997 7cbf3b13

+70 -1
+27
include/linux/clocksource.h
··· 21 21 #include <asm/div64.h> 22 22 #include <asm/io.h> 23 23 24 + struct clocksource_base; 24 25 struct clocksource; 25 26 struct module; 26 27 ··· 51 50 * multiplication 52 51 * @name: Pointer to clocksource name 53 52 * @list: List head for registration (internal) 53 + * @freq_khz: Clocksource frequency in khz. 54 54 * @rating: Rating value for selection (higher is better) 55 55 * To avoid rating inflation the following 56 56 * list should give you a guide as to how ··· 72 70 * validate the clocksource from which the snapshot was 73 71 * taken. 74 72 * @flags: Flags describing special properties 73 + * @base: Hardware abstraction for clock on which a clocksource 74 + * is based 75 75 * @enable: Optional function to enable the clocksource 76 76 * @disable: Optional function to disable the clocksource 77 77 * @suspend: Optional suspend function for the clocksource ··· 111 107 u64 max_cycles; 112 108 const char *name; 113 109 struct list_head list; 110 + u32 freq_khz; 114 111 int rating; 115 112 enum clocksource_ids id; 116 113 enum vdso_clock_mode vdso_clock_mode; 117 114 unsigned long flags; 115 + struct clocksource_base *base; 118 116 119 117 int (*enable)(struct clocksource *cs); 120 118 void (*disable)(struct clocksource *cs); ··· 311 305 } 312 306 313 307 void clocksource_verify_percpu(struct clocksource *cs); 308 + 309 + /** 310 + * struct clocksource_base - hardware abstraction for clock on which a clocksource 311 + * is based 312 + * @id: Defaults to CSID_GENERIC. The id value is used for conversion 313 + * functions which require that the current clocksource is based 314 + * on a clocksource_base with a particular ID in certain snapshot 315 + * functions to allow callers to validate the clocksource from 316 + * which the snapshot was taken. 317 + * @freq_khz: Nominal frequency of the base clock in kHz 318 + * @offset: Offset between the base clock and the clocksource 319 + * @numerator: Numerator of the clock ratio between base clock and the clocksource 320 + * @denominator: Denominator of the clock ratio between base clock and the clocksource 321 + */ 322 + struct clocksource_base { 323 + enum clocksource_ids id; 324 + u32 freq_khz; 325 + u64 offset; 326 + u32 numerator; 327 + u32 denominator; 328 + }; 314 329 315 330 #endif /* _LINUX_CLOCKSOURCE_H */
+2
include/linux/timekeeping.h
··· 310 310 * timekeeping code to verify comparability of two cycle values. 311 311 * The default ID, CSID_GENERIC, does not identify a specific 312 312 * clocksource. 313 + * @use_nsecs: @cycles is in nanoseconds. 313 314 */ 314 315 struct system_counterval_t { 315 316 u64 cycles; 316 317 enum clocksource_ids cs_id; 318 + bool use_nsecs; 317 319 }; 318 320 319 321 /*
+41 -1
kernel/time/timekeeping.c
··· 1195 1195 return false; 1196 1196 } 1197 1197 1198 + static bool convert_clock(u64 *val, u32 numerator, u32 denominator) 1199 + { 1200 + u64 rem, res; 1201 + 1202 + if (!numerator || !denominator) 1203 + return false; 1204 + 1205 + res = div64_u64_rem(*val, denominator, &rem) * numerator; 1206 + *val = res + div_u64(rem * numerator, denominator); 1207 + return true; 1208 + } 1209 + 1210 + static bool convert_base_to_cs(struct system_counterval_t *scv) 1211 + { 1212 + struct clocksource *cs = tk_core.timekeeper.tkr_mono.clock; 1213 + struct clocksource_base *base; 1214 + u32 num, den; 1215 + 1216 + /* The timestamp was taken from the time keeper clock source */ 1217 + if (cs->id == scv->cs_id) 1218 + return true; 1219 + 1220 + /* 1221 + * Check whether cs_id matches the base clock. Prevent the compiler from 1222 + * re-evaluating @base as the clocksource might change concurrently. 1223 + */ 1224 + base = READ_ONCE(cs->base); 1225 + if (!base || base->id != scv->cs_id) 1226 + return false; 1227 + 1228 + num = scv->use_nsecs ? cs->freq_khz : base->numerator; 1229 + den = scv->use_nsecs ? USEC_PER_SEC : base->denominator; 1230 + 1231 + if (!convert_clock(&scv->cycles, num, den)) 1232 + return false; 1233 + 1234 + scv->cycles += base->offset; 1235 + return true; 1236 + } 1237 + 1198 1238 /** 1199 1239 * get_device_system_crosststamp - Synchronously capture system/device timestamp 1200 1240 * @get_time_fn: Callback to get simultaneous device time and ··· 1281 1241 * installed timekeeper clocksource 1282 1242 */ 1283 1243 if (system_counterval.cs_id == CSID_GENERIC || 1284 - tk->tkr_mono.clock->id != system_counterval.cs_id) 1244 + !convert_base_to_cs(&system_counterval)) 1285 1245 return -ENODEV; 1286 1246 cycles = system_counterval.cycles; 1287 1247