[patch V3 04/11] timekeeping: Provide time setter for auxiliary clocks

Thomas Gleixner posted 11 patches 3 months, 2 weeks ago
[patch V3 04/11] timekeeping: Provide time setter for auxiliary clocks
Posted by Thomas Gleixner 3 months, 2 weeks ago
Add clock_settime(2) support for auxiliary clocks. The function affects the
AUX offset which is added to the "monotonic" clock readout of these clocks.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
 kernel/time/timekeeping.c |   39 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)
---

--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -2757,9 +2757,48 @@ static int aux_get_timespec(clockid_t id
 	return ktime_get_aux_ts64(id, tp) ? 0 : -ENODEV;
 }
 
+static int aux_clock_set(const clockid_t id, const struct timespec64 *tnew)
+{
+	struct tk_data *tkd = aux_get_tk_data(id);
+	struct timekeeper *tks;
+	ktime_t tnow, nsecs;
+
+	if (!timespec64_valid_settod(tnew))
+		return -EINVAL;
+	if (!tkd)
+		return -ENODEV;
+
+	tks = &tkd->shadow_timekeeper;
+
+	guard(raw_spinlock_irq)(&tkd->lock);
+	if (!tks->clock_valid)
+		return -ENODEV;
+
+	/* Forward the timekeeper base time */
+	timekeeping_forward_now(tks);
+	/*
+	 * Get the updated base time. tkr_mono.base has not been
+	 * updated yet, so do that first. That makes the update
+	 * in timekeeping_update_from_shadow() redundant, but
+	 * that's harmless. After that @tnow can be calculated
+	 * by using tkr_mono::cycle_last, which has been set
+	 * by timekeeping_forward_now().
+	 */
+	tk_update_ktime_data(tks);
+	nsecs = timekeeping_cycles_to_ns(&tks->tkr_mono, tks->tkr_mono.cycle_last);
+	tnow = ktime_add(tks->tkr_mono.base, nsecs);
+
+	/* Calculate the new AUX offset */
+	tks->offs_aux = ktime_sub(timespec64_to_ktime(*tnew), tnow);
+
+	timekeeping_update_from_shadow(tkd, TK_UPDATE_ALL);
+	return 0;
+}
+
 const struct k_clock clock_aux = {
 	.clock_getres		= aux_get_res,
 	.clock_get_timespec	= aux_get_timespec,
+	.clock_set		= aux_clock_set,
 };
 
 static __init void tk_aux_setup(void)
Re: [patch V3 04/11] timekeeping: Provide time setter for auxiliary clocks
Posted by John Stultz 3 months, 1 week ago
On Wed, Jun 25, 2025 at 11:38 AM Thomas Gleixner <tglx@linutronix.de> wrote:
>
> Add clock_settime(2) support for auxiliary clocks. The function affects the
> AUX offset which is added to the "monotonic" clock readout of these clocks.
>
> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
> ---

Minor fretting: I worry a little that the difference here between the
default timekeeper where set adjusts the REALTIME offset from
MONOTONIC, and here where it directly adjusts "mono" might confuse
later readers?
Would something about that be useful to have in a comment?

Either way,
Acked-by: John Stultz <jstultz@google.com>
Re: [patch V3 04/11] timekeeping: Provide time setter for auxiliary clocks
Posted by Thomas Gleixner 3 months, 1 week ago
On Thu, Jun 26 2025 at 21:23, John Stultz wrote:
> On Wed, Jun 25, 2025 at 11:38 AM Thomas Gleixner <tglx@linutronix.de> wrote:
>>
>> Add clock_settime(2) support for auxiliary clocks. The function affects the
>> AUX offset which is added to the "monotonic" clock readout of these clocks.
>>
>> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
>> ---
>
> Minor fretting: I worry a little that the difference here between the
> default timekeeper where set adjusts the REALTIME offset from
> MONOTONIC, and here where it directly adjusts "mono" might confuse
> later readers?

Actually it's not really that different.

In both cases the new offset to the monotonic clock is calculated and
stored in the relevant tk::offs_* member.

The difference is that the core timekeeper operates on xtime, but for
simplicity I chose to calculate the resulting tk::offs_aux directly from
the monotonic base. That's valid with the aux clocks as they don't
need any of the xtime parts.

I added some blurb to it.
Re: [patch V3 04/11] timekeeping: Provide time setter for auxiliary clocks
Posted by Thomas Gleixner 3 months, 1 week ago
On Fri, Jun 27 2025 at 16:18, Thomas Gleixner wrote:

> On Thu, Jun 26 2025 at 21:23, John Stultz wrote:
>> On Wed, Jun 25, 2025 at 11:38 AM Thomas Gleixner <tglx@linutronix.de> wrote:
>>>
>>> Add clock_settime(2) support for auxiliary clocks. The function affects the
>>> AUX offset which is added to the "monotonic" clock readout of these clocks.
>>>
>>> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
>>> ---
>>
>> Minor fretting: I worry a little that the difference here between the
>> default timekeeper where set adjusts the REALTIME offset from
>> MONOTONIC, and here where it directly adjusts "mono" might confuse
>> later readers?
>
> Actually it's not really that different.
>
> In both cases the new offset to the monotonic clock is calculated and
> stored in the relevant tk::offs_* member.
>
> The difference is that the core timekeeper operates on xtime, but for
> simplicity I chose to calculate the resulting tk::offs_aux directly from
> the monotonic base. That's valid with the aux clocks as they don't
> need any of the xtime parts.

Actually using xtime and adjusting it and xtime_to_mono does not work
for those auxiliary clocks because the offset to clock "monotonic" is
allowed to be negative, unless it would result in a overall negative
time readout.

This is required for those clocks because the TSN/PTP zoo out there
especially in automation/automotive uses clockmasters starting at the
epoch for their specialized networks. So if the clockmaster starts up
_after_ the client, then the pile of xtime sanity checks would prevent
setting the clock back to the epoch.

I tried to work around that, but the result was more horrible and
fragile than the current approach with the aux specific offset.

Don't ask :)

Thanks,

        tglx
[tip: timers/ptp] timekeeping: Provide time setter for auxiliary clocks
Posted by tip-bot2 for Thomas Gleixner 3 months, 1 week ago
The following commit has been merged into the timers/ptp branch of tip:

Commit-ID:     60ecc26ec5af567a55f362ad92c0cac8b894541c
Gitweb:        https://git.kernel.org/tip/60ecc26ec5af567a55f362ad92c0cac8b894541c
Author:        Thomas Gleixner <tglx@linutronix.de>
AuthorDate:    Wed, 25 Jun 2025 20:38:34 +02:00
Committer:     Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Fri, 27 Jun 2025 20:13:12 +02:00

timekeeping: Provide time setter for auxiliary clocks

Add clock_settime(2) support for auxiliary clocks. The function affects the
AUX offset which is added to the "monotonic" clock readout of these clocks.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: John Stultz <jstultz@google.com>
Link: https://lore.kernel.org/all/20250625183757.995688714@linutronix.de

---
 kernel/time/timekeeping.c | 44 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 44 insertions(+)

diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 10c6e37..b6ac784 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -2765,9 +2765,53 @@ static int aux_get_timespec(clockid_t id, struct timespec64 *tp)
 	return ktime_get_aux_ts64(id, tp) ? 0 : -ENODEV;
 }
 
+static int aux_clock_set(const clockid_t id, const struct timespec64 *tnew)
+{
+	struct tk_data *aux_tkd = aux_get_tk_data(id);
+	struct timekeeper *aux_tks;
+	ktime_t tnow, nsecs;
+
+	if (!timespec64_valid_settod(tnew))
+		return -EINVAL;
+	if (!aux_tkd)
+		return -ENODEV;
+
+	aux_tks = &aux_tkd->shadow_timekeeper;
+
+	guard(raw_spinlock_irq)(&aux_tkd->lock);
+	if (!aux_tks->clock_valid)
+		return -ENODEV;
+
+	/* Forward the timekeeper base time */
+	timekeeping_forward_now(aux_tks);
+	/*
+	 * Get the updated base time. tkr_mono.base has not been
+	 * updated yet, so do that first. That makes the update
+	 * in timekeeping_update_from_shadow() redundant, but
+	 * that's harmless. After that @tnow can be calculated
+	 * by using tkr_mono::cycle_last, which has been set
+	 * by timekeeping_forward_now().
+	 */
+	tk_update_ktime_data(aux_tks);
+	nsecs = timekeeping_cycles_to_ns(&aux_tks->tkr_mono, aux_tks->tkr_mono.cycle_last);
+	tnow = ktime_add(aux_tks->tkr_mono.base, nsecs);
+
+	/*
+	 * Calculate the new AUX offset as delta to @tnow ("monotonic").
+	 * That avoids all the tk::xtime back and forth conversions as
+	 * xtime ("realtime") is not applicable for auxiliary clocks and
+	 * kept in sync with "monotonic".
+	 */
+	aux_tks->offs_aux = ktime_sub(timespec64_to_ktime(*tnew), tnow);
+
+	timekeeping_update_from_shadow(aux_tkd, TK_UPDATE_ALL);
+	return 0;
+}
+
 const struct k_clock clock_aux = {
 	.clock_getres		= aux_get_res,
 	.clock_get_timespec	= aux_get_timespec,
+	.clock_set		= aux_clock_set,
 };
 
 static __init void tk_aux_setup(void)