Re: [PATCH v5 02/11] timekeeping: Add function to convert realtime to base clock
From: Thomas Gleixner <hidden>
Date: 2024-03-21 14:53:24
Also in:
intel-wired-lan, linux-doc, linux-sound, lkml
On Tue, Mar 19 2024 at 18:35, lakshmi.sowjanya.d@intel.com wrote:
+bool ktime_real_to_base_clock(ktime_t treal, enum clocksource_ids base_id, u64 *cycles)
+{
+ struct timekeeper *tk = &tk_core.timekeeper;
+ unsigned int seq;
+ u64 delta;
+
+ do {
+ seq = read_seqcount_begin(&tk_core.seq);
+ delta = (u64)treal - tk->tkr_mono.base_real;
+ if (delta > tk->tkr_mono.clock->max_idle_ns)
+ return false;
I don't think this cutoff is valid. There is no guarantee that this is
linear unless:
Treal[last timekeeper update] <= treal < Treal[next timekeeper update]
Look at the dance in get_device_system_crosststamp() and
adjust_historical_crosststamp() to see why.
+ *cycles = tk->tkr_mono.cycle_last + convert_ns_to_cs(delta); + if (!convert_cs_to_base(cycles, base_id)) + return false; + } while (read_seqcount_retry(&tk_core.seq, seq)); + + return true; +} +EXPORT_SYMBOL_GPL(ktime_real_to_base_clock);
Looking at the usage site:
+static bool pps_generate_next_pulse(struct pps_tio *tio, ktime_t expires)
+{
+ u64 art;
+
+ if (!ktime_real_to_base_clock(expires, CSID_X86_ART, &art)) {
+ pps_tio_disable(tio);I'm pretty sure this can happen when there is sufficient delay between the check for (now - expires < SAFE_TIME_NS) and the delta computation in ktime_real_to_base_clock() if there is a timerkeeper update interleaving which brings tkr_mono.base_real in front of expires. Is that intentional and correct? If so, then it's inconsistent with the behaviour of the hrtimer callback:
+ return false;
+ }
+
+ pps_compv_write(tio, art - ART_HW_DELAY_CYCLES);
+ return true;
+}
+
+static enum hrtimer_restart hrtimer_callback(struct hrtimer *timer)
+{
+ struct pps_tio *tio = container_of(timer, struct pps_tio, timer);
+ ktime_t expires, now;
+
+ guard(spinlock)(&tio->lock);
+
+ expires = hrtimer_get_expires(timer);
+ now = ktime_get_real();
+
+ if (now - expires < SAFE_TIME_NS) {
+ if (!pps_generate_next_pulse(tio, expires + SAFE_TIME_NS))
+ return HRTIMER_NORESTART;
+ }
This safe guard does not care about time being set. I'm not familiar
with the PPS logic, but is it expected that the pulse pattern will be
like this:
---|-----|-----|-----|----------------->
P P ^ P
|
clock_settime(CLOCK_REALTIME, now - 2 seconds)
Obviously the pulse gap will be as big as the time is set
backwards, which might be way more than 2 seconds.
---|-----|-----|-----|----------------->
P P ^ P P
|
clock_settime(CLOCK_REALTIME, now + 2 seconds)
I don't see anything in this code which cares about CLOCK_REALTIME being
set via clock_settime() or adjtimex().
Aside of that I have a question about how the TIO hardware treats this
case:
ktime_real_to_base_clock(expires, &art);
-> GAP which makes @art get into the past
pps_compv_write(tio, art - ART_HW_DELAY_CYCLES);
Will the hardware ignore that already expired value or just emit a pulse
immediately? In the latter case the pulse will be at a random point in
time, which does not sound correct.
Thanks,
tglx