[PATCH net-next v2 1/6] timekeeping: add ktime_scale_raw_ns()
From: James Clark
Date: Sun Oct 18 2026 - 06:03:58 EST
Add ktime_scale_raw_ns(), which scales a duration in monotonic raw
time by the frequency of a given clock relative to monotonic raw.
A PTP driver that takes a ktime_get_snapshot_id() snapshot around a
hardware access may know how long after the snapshot, in monotonic raw
time, the hardware event happened. To shift the snapshot's time of the
selected clock by the same interval, the duration has to be scaled by
that clock's frequency relative to monotonic raw.
Scale by tkr_mono.mult / tkr_raw.mult of the clock's timekeeper, as
adjust_historical_crosststamp() does. For an AUX clock this is the AUX
clock's own timekeeper.
Signed-off-by: James Clark <jjc@xxxxxxxxxx>
Assisted-by: LLM
---
Changes in v2:
- New patch, used by the ptp helpers in the next patch
include/linux/timekeeping.h | 6 +++++
kernel/time/timekeeping.c | 54 +++++++++++++++++++++++++++++++++++++
2 files changed, 60 insertions(+)
diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h
index efa9442aaee..c3737aa844f 100644
--- a/include/linux/timekeeping.h
+++ b/include/linux/timekeeping.h
@@ -366,6 +366,12 @@ extern int get_device_system_crosststamp(
*/
extern void ktime_get_snapshot_id(clockid_t clock_id, struct system_time_snapshot *systime_snapshot);
+/*
+ * Scale a monotonic raw duration by the frequency of a given clock
+ * relative to monotonic raw.
+ */
+extern u64 ktime_scale_raw_ns(clockid_t clock_id, u64 ns);
+
/*
* Persistent clock related interfaces
*/
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index ea2e6e55f37..3f8592bac96 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -1332,6 +1332,60 @@ void ktime_get_snapshot_id(clockid_t clock_id, struct system_time_snapshot *syst
}
EXPORT_SYMBOL_GPL(ktime_get_snapshot_id);
+/**
+ * ktime_scale_raw_ns - Scale a monotonic raw duration by a clock's frequency
+ * @clock_id: The clock ID whose frequency to scale by
+ * @ns: Duration in nanoseconds of the monotonic raw clock related
+ * to @clock_id
+ *
+ * Multiply a duration measured in monotonic raw time by the current
+ * frequency of @clock_id relative to monotonic raw.
+ *
+ * Return: @ns scaled by the frequency of @clock_id relative to monotonic
+ * raw. @ns unchanged for CLOCK_MONOTONIC_RAW, or if @clock_id is an AUX
+ * clock that is not enabled.
+ */
+u64 ktime_scale_raw_ns(clockid_t clock_id, u64 ns)
+{
+ u32 mult_sys, mult_raw;
+ struct timekeeper *tk;
+ struct tk_data *tkd;
+ unsigned int seq;
+
+ switch (clock_id) {
+ case CLOCK_REALTIME:
+ case CLOCK_MONOTONIC:
+ case CLOCK_BOOTTIME:
+ tkd = &tk_core;
+ break;
+ case CLOCK_MONOTONIC_RAW:
+ return ns;
+ case CLOCK_AUX ... CLOCK_AUX_LAST:
+ tkd = aux_get_tk_data(clock_id);
+ if (!tkd)
+ return ns;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return ns;
+ }
+
+ tk = &tkd->timekeeper;
+
+ do {
+ seq = read_seqcount_begin(&tkd->seq);
+
+ if (!tk->clock_valid)
+ return ns;
+
+ mult_sys = tk->tkr_mono.mult;
+ mult_raw = tk->tkr_raw.mult;
+ } while (read_seqcount_retry(&tkd->seq, seq));
+
+ return mul_u64_u32_div(ns, mult_sys, mult_raw);
+}
+EXPORT_SYMBOL_GPL(ktime_scale_raw_ns);
+
/* Scale base by mult/div checking for overflow */
static int scale64_check_overflow(u64 mult, u64 div, u64 *base)
{
--
2.56.0