Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1193670 > unrolled thread
| Started by | Christopher Hall <christopher.s.hall@intel.com> |
|---|---|
| First post | 2015-07-28 02:50 +0200 |
| Last post | 2015-07-29 12:20 +0200 |
| Articles | 4 — 3 participants |
Back to article view | Back to linux.kernel
This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by
below is the oldest one visible, not the original post.
[PATCH 1/5] Add functions producing system time given a backing counter value Christopher Hall <christopher.s.hall@intel.com> - 2015-07-28 02:50 +0200
RE: [PATCH 1/5] Add functions producing system time given a backing counter value "Hall, Christopher S" <christopher.s.hall@intel.com> - 2015-07-29 03:50 +0200
RE: [PATCH 1/5] Add functions producing system time given a backing counter value Thomas Gleixner <tglx@linutronix.de> - 2015-07-29 16:10 +0200
Re: [PATCH 1/5] Add functions producing system time given a backing counter value Thomas Gleixner <tglx@linutronix.de> - 2015-07-29 12:20 +0200
| From | Christopher Hall <christopher.s.hall@intel.com> |
|---|---|
| Date | 2015-07-28 02:50 +0200 |
| Subject | [PATCH 1/5] Add functions producing system time given a backing counter value |
| Message-ID | <pR1fI-1AI-13@gated-at.bofh.it> |
* counter_to_rawmono64
* counter_to_mono64
* counter_to_realtime64
Enables drivers to translate a captured system clock counter to system
time. This is useful for network and audio devices that capture timestamps
in terms of both the system clock and device clock.
Signed-off-by: Christopher Hall <christopher.s.hall@intel.com>
---
include/linux/timekeeping.h | 8 ++
kernel/time/timekeeping.c | 211 +++++++++++++++++++++++++++++++++++++++-----
2 files changed, 199 insertions(+), 20 deletions(-)
diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h
index 6e191e4..04e6455 100644
--- a/include/linux/timekeeping.h
+++ b/include/linux/timekeeping.h
@@ -1,6 +1,8 @@
#ifndef _LINUX_TIMEKEEPING_H
#define _LINUX_TIMEKEEPING_H
+struct clocksource; /* Defined in clocksource.h */
+
/* Included from linux/ktime.h */
void timekeeping_init(void);
@@ -27,6 +29,12 @@ struct timespec __current_kernel_time(void);
*/
struct timespec64 get_monotonic_coarse64(void);
extern void getrawmonotonic64(struct timespec64 *ts);
+extern int counter_to_rawmono64
+(struct timespec64 *rawmono, cycle_t counterval, struct clocksource *cs);
+extern int counter_to_mono64
+(struct timespec64 *mono, cycle_t counterval, struct clocksource *cs);
+extern int counter_to_realtime64
+(struct timespec64 *realtime, cycle_t counterval, struct clocksource *cs);
extern void ktime_get_ts64(struct timespec64 *ts);
extern time64_t ktime_get_seconds(void);
extern time64_t ktime_get_real_seconds(void);
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index bca3667..1ca4fe0 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -116,6 +116,8 @@ static inline void tk_update_sleep_time(struct timekeeper *tk, ktime_t delta)
tk->offs_boot = ktime_add(tk->offs_boot, delta);
}
+#define ROLLOVER_THRESHOLD (2ULL << 39)
+
#ifdef CONFIG_DEBUG_TIMEKEEPING
#define WARNING_FREQ (HZ*300) /* 5 minute rate-limiting */
@@ -158,10 +160,11 @@ static void timekeeping_check_update(struct timekeeper *tk, cycle_t offset)
}
}
-static inline cycle_t timekeeping_get_delta(struct tk_read_base *tkr)
+static inline int timekeeping_get_delta
+(cycle_t *delta, struct tk_read_base *tkr, cycle_t *counterval)
{
struct timekeeper *tk = &tk_core.timekeeper;
- cycle_t now, last, mask, max, delta;
+ cycle_t now = *counterval, last, mask, max, delta;
unsigned int seq;
/*
@@ -173,46 +176,61 @@ static inline cycle_t timekeeping_get_delta(struct tk_read_base *tkr)
*/
do {
seq = read_seqcount_begin(&tk_core.seq);
- now = tkr->read(tkr->clock);
+ if (counterval == NULL) {
+ now = tkr->read(tkr->clock);
+ } else {
+ if (now < tkr->cycle_last &&
+ tkr->cycle_last - now < ROLLOVER_THRESHOLD)
+ return -EAGAIN;
+ }
last = tkr->cycle_last;
mask = tkr->mask;
max = tkr->clock->max_cycles;
} while (read_seqcount_retry(&tk_core.seq, seq));
- delta = clocksource_delta(now, last, mask);
+ *delta = clocksource_delta(now, last, mask);
/*
* Try to catch underflows by checking if we are seeing small
* mask-relative negative values.
*/
- if (unlikely((~delta & mask) < (mask >> 3))) {
+ if (unlikely((~*delta & mask) < (mask >> 3))) {
tk->underflow_seen = 1;
- delta = 0;
+ *delta = 0;
}
/* Cap delta value to the max_cycles values to avoid mult overflows */
- if (unlikely(delta > max)) {
- tk->overflow_seen = 1;
- delta = tkr->clock->max_cycles;
+ if (unlikely(*delta > max)) {
+ tk->underflow_seen = 1;
+ *delta = tkr->clock->max_cycles;
}
- return delta;
+ return 0;
}
#else
-static inline void timekeeping_check_update(struct timekeeper *tk, cycle_t offset)
+static inline void timekeeping_check_update
+(struct timekeeper *tk, cycle_t offset)
{
}
-static inline cycle_t timekeeping_get_delta(struct tk_read_base *tkr)
+static inline cycle_t timekeeping_get_delta
+(cycle_t *delta, struct tk_read_base *tkr, cycle_t *counterval)
{
- cycle_t cycle_now, delta;
+ cycle_t cycle_now;
/* read clocksource */
- cycle_now = tkr->read(tkr->clock);
+ if (counterval == NULL) {
+ cycle_now = tkr->read(tkr->clock);
+ } else {
+ cycle_now = *counterval;
+ if (cycle_now < tkr->cycle_last &&
+ tkr->cycle_last - cycle_now < ROLLOVER_THRESHOLD)
+ return -EAGAIN;
+ }
/* calculate the delta since the last update_wall_time */
- delta = clocksource_delta(cycle_now, tkr->cycle_last, tkr->mask);
+ *delta = clocksource_delta(cycle_now, tkr->cycle_last, tkr->mask);
- return delta;
+ return 0;
}
#endif
@@ -298,13 +316,11 @@ u32 (*arch_gettimeoffset)(void) = default_arch_gettimeoffset;
static inline u32 arch_gettimeoffset(void) { return 0; }
#endif
-static inline s64 timekeeping_get_ns(struct tk_read_base *tkr)
+static inline s64 timekeeping_delta_to_ns
+(struct tk_read_base *tkr, cycle_t delta)
{
- cycle_t delta;
s64 nsec;
- delta = timekeeping_get_delta(tkr);
-
nsec = delta * tkr->mult + tkr->xtime_nsec;
nsec >>= tkr->shift;
@@ -312,6 +328,28 @@ static inline s64 timekeeping_get_ns(struct tk_read_base *tkr)
return nsec + arch_gettimeoffset();
}
+static inline s64 timekeeping_get_ns(struct tk_read_base *tkr)
+{
+ cycle_t delta;
+
+ timekeeping_get_delta(&delta, tkr, NULL);
+ return timekeeping_delta_to_ns(tkr, delta);
+}
+
+static inline int timekeeping_counter_to_ns
+(s64 *nsec, struct tk_read_base *tkr, cycle_t counterval)
+{
+ cycle_t delta;
+ int err;
+
+ err = timekeeping_get_delta(&delta, tkr, &counterval);
+ if (err != 0)
+ return err;
+
+ *nsec = timekeeping_delta_to_ns(tkr, delta);
+ return 0;
+}
+
/**
* update_fast_timekeeper - Update the fast and NMI safe monotonic timekeeper.
* @tkr: Timekeeping readout base from which we take the update
@@ -1117,6 +1155,139 @@ void getrawmonotonic64(struct timespec64 *ts)
EXPORT_SYMBOL(getrawmonotonic64);
+ /**
+ * counterval_to_rawmono64 - Returns the raw monotonic time given a counter
+ * value
+ * @counterval: counter value (input)
+ * @rawmono: raw monotonic clock (output)
+ * @cs: clocksource from which clockvalue is derived
+ *
+ * Returns:
+ * 0 - Success
+ * -EAGAIN - Clock value is in the past, try again
+ * -ENXIO - Clocksource 'cs' doesn't match the current clocksource
+ *
+ */
+int counter_to_rawmono64(struct timespec64 *rawmono, cycle_t counterval,
+ struct clocksource *cs)
+{
+ struct timekeeper *tk = &tk_core.timekeeper;
+ struct clocksource *curr_clock;
+ struct timespec64 ts64;
+ unsigned long seq;
+ s64 nsecs;
+ int err;
+
+ do {
+ seq = read_seqcount_begin(&tk_core.seq);
+ err = timekeeping_counter_to_ns
+ (&nsecs, &tk->tkr_raw, counterval);
+ if (err != 0)
+ return err;
+ ts64 = tk->raw_time;
+ curr_clock = tk->tkr_raw.clock;
+ } while (read_seqcount_retry(&tk_core.seq, seq));
+
+ if (curr_clock != cs)
+ return -ENXIO;
+
+ timespec64_add_ns(&ts64, nsecs);
+ *rawmono = ts64;
+
+ return 0;
+}
+EXPORT_SYMBOL(counter_to_rawmono64);
+
+ /**
+ * counterval_to_realtime64 - Returns the real time given a counter
+ * value
+ * @counterval: counter value (input)
+ * @realtime: realtime clock (output)
+ * @cs: clocksource from which clockvalue is derived
+ *
+ * Returns:
+ * 0 - Success
+ * -EAGAIN - Clock value is in the past, try again
+ * -ENXIO - Clocksource 'cs' doesn't match the current clocksource
+ *
+ */
+int counter_to_realtime64(struct timespec64 *realtime, cycle_t counterval,
+ struct clocksource *cs)
+{
+ struct timekeeper *tk = &tk_core.timekeeper;
+ struct clocksource *curr_clock;
+ struct timespec64 ts64;
+ unsigned long seq;
+ s64 nsecs;
+ int err;
+
+ do {
+ seq = read_seqcount_begin(&tk_core.seq);
+ err = timekeeping_counter_to_ns
+ (&nsecs, &tk->tkr_mono, counterval);
+ if (err != 0)
+ return err;
+ ts64.tv_sec = tk->xtime_sec;
+ curr_clock = tk->tkr_mono.clock;
+ } while (read_seqcount_retry(&tk_core.seq, seq));
+
+ if (curr_clock != cs)
+ return -ENXIO;
+
+ ts64.tv_nsec = 0;
+ timespec64_add_ns(&ts64, nsecs);
+ *realtime = ts64;
+
+ return 0;
+}
+EXPORT_SYMBOL(counter_to_realtime64);
+
+ /**
+ * counterval_to_mono64 - Returns the real time given a counter
+ * value
+ * @counterval: counter value (input)
+ * @realtime: realtime clock (output)
+ * @cs: clocksource from which clockvalue is derived
+ *
+ * Returns:
+ * 0 - Success
+ * -EAGAIN - Clock value is in the past, try again
+ * -ENXIO - Clocksource 'cs' doesn't match the current clocksource
+ *
+ */
+int counter_to_mono64(struct timespec64 *mono, cycle_t counterval,
+ struct clocksource *cs)
+{
+ struct timekeeper *tk = &tk_core.timekeeper;
+ struct clocksource *curr_clock;
+ struct timespec64 tomono;
+ struct timespec64 ts64;
+ unsigned long seq;
+ s64 nsecs;
+ int err;
+
+ do {
+ seq = read_seqcount_begin(&tk_core.seq);
+ err = timekeeping_counter_to_ns
+ (&nsecs, &tk->tkr_mono, counterval);
+ if (err != 0)
+ return err;
+ ts64.tv_sec = tk->xtime_sec;
+ tomono = tk->wall_to_monotonic;
+ curr_clock = tk->tkr_mono.clock;
+ } while (read_seqcount_retry(&tk_core.seq, seq));
+
+ if (curr_clock != cs)
+ return -ENXIO;
+
+ ts64.tv_nsec = 0;
+ timespec64_add_ns(&ts64, nsecs);
+ *mono = timespec64_add(ts64, tomono);
+
+ return 0;
+}
+EXPORT_SYMBOL(counter_to_mono64);
+
/**
* timekeeping_valid_for_hres - Check if timekeeping is suitable for hres
*/
--
1.9.1
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [next] | [standalone]
| From | "Hall, Christopher S" <christopher.s.hall@intel.com> |
|---|---|
| Date | 2015-07-29 03:50 +0200 |
| Subject | RE: [PATCH 1/5] Add functions producing system time given a backing counter value |
| Message-ID | <pRoFj-1X0-5@gated-at.bofh.it> |
| In reply to | #1193670 |
DQo+IC0tLS0tT3JpZ2luYWwgTWVzc2FnZS0tLS0tDQo+IEZyb206IEpvaG4gU3R1bHR6IFttYWls dG86am9obi5zdHVsdHpAbGluYXJvLm9yZ10NCj4gU2VudDogTW9uZGF5LCBKdWx5IDI3LCAyMDE1 IDg6NDQgUE0NCj4gVG86IEhhbGwsIENocmlzdG9waGVyIFMNCj4gQ2M6IFRob21hcyBHbGVpeG5l cjsgUmljaGFyZCBDb2NocmFuOyBJbmdvIE1vbG5hcjsgS2lyc2hlciwgSmVmZnJleSBUOw0KPiBS b25jaWFrLCBKb2huOyBILiBQZXRlciBBbnZpbjsgeDg2QGtlcm5lbC5vcmc7IGxrbWw7DQo+IG5l dGRldkB2Z2VyLmtlcm5lbC5vcmcNCj4gU3ViamVjdDogUmU6IFtQQVRDSCAxLzVdIEFkZCBmdW5j dGlvbnMgcHJvZHVjaW5nIHN5c3RlbSB0aW1lIGdpdmVuIGENCj4gYmFja2luZyBjb3VudGVyIHZh bHVlDQo+IA0KPiBPbiBNb24sIEp1bCAyNywgMjAxNSBhdCA1OjQ2IFBNLCBDaHJpc3RvcGhlciBI YWxsDQo+IDxjaHJpc3RvcGhlci5zLmhhbGxAaW50ZWwuY29tPiB3cm90ZToNCj4gPiAqIGNvdW50 ZXJfdG9fcmF3bW9ubzY0DQo+ID4gKiBjb3VudGVyX3RvX21vbm82NA0KPiA+ICogY291bnRlcl90 b19yZWFsdGltZTY0DQo+ID4NCj4gPiBFbmFibGVzIGRyaXZlcnMgdG8gdHJhbnNsYXRlIGEgY2Fw dHVyZWQgc3lzdGVtIGNsb2NrIGNvdW50ZXIgdG8gc3lzdGVtDQo+ID4gdGltZS4gVGhpcyBpcyB1 c2VmdWwgZm9yIG5ldHdvcmsgYW5kIGF1ZGlvIGRldmljZXMgdGhhdCBjYXB0dXJlDQo+IHRpbWVz dGFtcHMNCj4gPiBpbiB0ZXJtcyBvZiBib3RoIHRoZSBzeXN0ZW0gY2xvY2sgYW5kIGRldmljZSBj bG9jay4NCj4gDQo+IEh1aC4gIFNvIGZvciBjb3VudGVyX3RvX3JlYWx0aW1lNjQgJiBtb25vNjQs IHRoaXMgc2VlbXMgdG8gaWdub3JlIHRoZQ0KPiBmYWN0IHRoYXQgdGhlIG11bHRpcGxpZXIgaXMg Y29uc3RhbnRseSBhZGp1c3RlZCBhbmQgY29ycmVjdGVkLiBTbyB0aGF0DQo+IGNhbGxpbmcgdGhl IGZ1bmN0aW9uIHR3aWNlIHdpdGggdGhlIHNhbWUgY291bnRlciB2YWx1ZSBtYXkgcmVzdWx0IGlu DQo+IGRpZmZlcmVudCByZXR1cm5lZCB2YWx1ZXMuDQo+IA0KPiBJJ3ZlIG5vdCB5ZXQgZ3Jva2Vk IHRoZSB3aG9sZSBwYXRjaHNldCwgYnV0IGl0IHNlZW1zIGxpa2UgdGhlcmUgbmVlZHMNCj4gdG8g YmUgc29tZSBtZWNoYW5pc20gdGhhdCBlbnN1cmVzIHRoZSBjb3VudGVyIHZhbHVlIGlzIGNhcHR1 cmVkIGFuZA0KPiB1c2VkIGluIHRoZSBzYW1lIChvciBhdCBsZWFzdCBjbG9zZSkgaW50ZXJ2YWwg dGhhdCB0aGUgdGltZWtlZXBlciBkYXRhDQo+IGlzIHZhbGlkIGZvci4NCg0KVGhlIEFSVCAoYW5k IGRlcml2ZWQgVFNDKSB2YWx1ZXMgYXJlIGFsd2F5cyBpbiB0aGUgcGFzdC4gIFRoZXJlJ3Mgbm8N CmNoYW5jZSB0aGF0IHdlIGNvdWxkIGV4Y2VlZCB0aGUgaW50ZXJ2YWwuICBJIGRvbid0IHRoaW5r IGFueSBzaW1pbGFyDQp1c2FnZSB3b3VsZCBiZSBhIHByb2JsZW0gZWl0aGVyLg0KDQpBcmUgeW91 IHN1Z2dlc3RpbmcgdGhhdCwgZm9yIGNvbXBsZXRlbmVzcywgdGhpcyBiZSBlbmZvcmNlZCBieSB0 aGUNCmNvbnZlcnNpb24gZnVuY3Rpb24/DQoNCkkgZG8gYSBjaGVjayBoZXJlIHRvIG1ha2Ugc3Vy ZSB0aGF0IHRoZSBjdXJyZW50IGNvdW50ZXIgdmFsdWUgaXNuJ3QgYmVmb3JlDQp0aGUgYmVnaW5u aW5nIG9mIHRoZSBjdXJyZW50IGludGVydmFsOg0KDQp0aW1la2VlcGluZ19nZXRfZGVsdGEoKQ0K Li4uDQogICAgICAgICAgICAgICBpZiAoY3ljbGVfbm93IDwgdGtyLT5jeWNsZV9sYXN0ICYmDQog ICAgICAgICAgICAgICAgICAgdGtyLT5jeWNsZV9sYXN0IC0gY3ljbGVfbm93IDwgUk9MTE9WRVJf VEhSRVNIT0xEKQ0KICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIC1FQUdBSU47DQoNCklm IHRrci0+Y3ljbGVfbGFzdCAtIGN5Y2xlX25vdyBpcyBsYXJnZSwgdGhlIGFzc3VtcHRpb24gaXMg dGhhdA0Kcm9sbG92ZXIgb2NjdXJyZWQuICBPdGhlcndpc2UsIHRoZSBjYWxsZXIgc2hvdWxkIHJl LXJlYWQgdGhlIGNvdW50ZXINCnNvIHRoYXQgaXQgZmFsbHMgd2l0aGluIHRoZSBjdXJyZW50IGlu dGVydmFsLiAgSW4gbXkgIm5vcm1hbCB1c2UiDQp0ZXN0aW5nLCByZS1yZWFkIG5ldmVyIG9jY3Vy cmVkLg0KDQpUaGFua3MgZm9yIHlvdXIgaW5wdXQuDQoNCkNocmlzDQoNCj4gDQo+IHRoYW5rcw0K PiAtam9obg0K -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Thomas Gleixner <tglx@linutronix.de> |
|---|---|
| Date | 2015-07-29 16:10 +0200 |
| Subject | RE: [PATCH 1/5] Add functions producing system time given a backing counter value |
| Message-ID | <pRAds-1Te-33@gated-at.bofh.it> |
| In reply to | #1194767 |
On Wed, 29 Jul 2015, Hall, Christopher S wrote: > > > -----Original Message----- > > From: John Stultz [mailto:john.stultz@linaro.org] > > Sent: Monday, July 27, 2015 8:44 PM > > To: Hall, Christopher S > > Cc: Thomas Gleixner; Richard Cochran; Ingo Molnar; Kirsher, Jeffrey T; > > Ronciak, John; H. Peter Anvin; x86@kernel.org; lkml; > > netdev@vger.kernel.org > > Subject: Re: [PATCH 1/5] Add functions producing system time given a > > backing counter value > > > > On Mon, Jul 27, 2015 at 5:46 PM, Christopher Hall > > <christopher.s.hall@intel.com> wrote: > > > * counter_to_rawmono64 > > > * counter_to_mono64 > > > * counter_to_realtime64 > > > > > > Enables drivers to translate a captured system clock counter to system > > > time. This is useful for network and audio devices that capture > > timestamps > > > in terms of both the system clock and device clock. > > > > Huh. So for counter_to_realtime64 & mono64, this seems to ignore the > > fact that the multiplier is constantly adjusted and corrected. So that > > calling the function twice with the same counter value may result in > > different returned values. > > > > I've not yet groked the whole patchset, but it seems like there needs > > to be some mechanism that ensures the counter value is captured and > > used in the same (or at least close) interval that the timekeeper data > > is valid for. > > The ART (and derived TSC) values are always in the past. There's no > chance that we could exceed the interval. I don't think any similar > usage would be a problem either. > > Are you suggesting that, for completeness, this be enforced by the > conversion function? > > I do a check here to make sure that the current counter value isn't before > the beginning of the current interval: > > timekeeping_get_delta() > ... > if (cycle_now < tkr->cycle_last && > tkr->cycle_last - cycle_now < ROLLOVER_THRESHOLD) > return -EAGAIN; > > If tkr->cycle_last - cycle_now is large, the assumption is that > rollover occurred. Otherwise, the caller should re-read the counter > so that it falls within the current interval. In my "normal use" > testing, re-read never occurred. Sure that never happens, because your rollover value is 2 << 39 for whatever reasons. So on a 1GHz machine that is (2 << 39) / 1e9 ~= 1099.51 seconds. Oh well. -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Thomas Gleixner <tglx@linutronix.de> |
|---|---|
| Date | 2015-07-29 12:20 +0200 |
| Subject | Re: [PATCH 1/5] Add functions producing system time given a backing counter value |
| Message-ID | <pRwCS-59z-23@gated-at.bofh.it> |
| In reply to | #1193670 |
On Mon, 27 Jul 2015, John Stultz wrote:
> On Mon, Jul 27, 2015 at 8:44 PM, John Stultz <john.stultz@linaro.org> wrote:
> > On Mon, Jul 27, 2015 at 5:46 PM, Christopher Hall
> > <christopher.s.hall@intel.com> wrote:
> >> * counter_to_rawmono64
> >> * counter_to_mono64
> >> * counter_to_realtime64
> >>
> >> Enables drivers to translate a captured system clock counter to system
> >> time. This is useful for network and audio devices that capture timestamps
> >> in terms of both the system clock and device clock.
> >
> > Huh. So for counter_to_realtime64 & mono64, this seems to ignore the
> > fact that the multiplier is constantly adjusted and corrected. So that
> > calling the function twice with the same counter value may result in
> > different returned values.
> >
> > I've not yet groked the whole patchset, but it seems like there needs
> > to be some mechanism that ensures the counter value is captured and
> > used in the same (or at least close) interval that the timekeeper data
> > is valid for.
>
>
> So reading through. It looks like you only use art_to_realtime(), right?
>
> So again, since CLOCK_MONOTONIC and CLOCK_REALTIME are constaly being
> frequency adjusted, it might be best to construct this delta in the
> following way.
>
>
> Add counter_to_rawmono64(), which should be able to safely calculate
> the corresponding CLOCK_MONOTONIC_RAW time from any given cycle value.
>
> Use getnstime_raw_and_real() to get a immediate snapshot of current
> MONOTONIC_RAW and REALTIME clocks.
>
> Then calculate the delta between the snapshotted counter raw time, and
> the current raw time. Then apply that offset to the current realtime.
>
> The larger the raw-time delta, the larger the possible realtime error.
> But I think that will be as good as it gets.
I think that's still not the right approach. The whole purpose of this
is to get a precise snapshot of
- PTP time from the ETH device
and
- current system time
Right now this does
ktime_get_real();
read_ptp_time();
which is obviously not precise.
The new hardware allows you to latch PTP time and ART time atomically
in the ETH device and read them out.
ART is the base clock of the TSC where
TSC = K + (ART * n) / d;
So for this to work proper, we need a function which converts ART to
TSC. This is obviously x86/TSC specific code.
Now on the PTP side we need a callback provided by the device driver
to get the snapshot of the PTP and the ART.
So the proposed implementation merily calls that callback from the PTP
ioctl and then tries to do a magic CLOCK_REALTIME conversion of the
ART value. But that's just wrong as it does not guarantee a proper
correlation to the core timekeeping.
So what we really need is a function in the timekeeper core which gets
the PTP/ART timestamps from the device under the timekeeper sequence
counter and converts to clock realtime and raw monotonic.
That function is then called from the PTP ioctl.
Anything else is just 'lets hope it works and is precise enough'
voodoo.
Something like the below untested patch should be all we need for PTP
to be as precise as possible.
I don't know whether we need functionality to convert arbitrary
timestamps at all, but if we really need them then they are going to
be pretty simple and explicitely not precise for anything else than
clock monotonic raw. But that's a different story.
Lets concentrate on PTP first and talk about the other stuff once we
settled the use case which actually has a precision requirement.
Thanks,
tglx
----------------------------------------->
Subject: ptp: Get sync timestamps
From: Thomas Gleixner <tglx@linutronix.de>
Date: Wed, 29 Jul 2015 10:52:06 +0200
The ART stuff wants to be splitted out.
Not-Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
arch/x86/kernel/tsc.c | 27 ++++++++++++++++++
include/linux/clocksource.h | 30 ++++++++++++++++++++
include/linux/timekeeping.h | 4 ++
kernel/time/timekeeping.c | 63 ++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 124 insertions(+)
Index: linux/arch/x86/kernel/tsc.c
===================================================================
--- linux.orig/arch/x86/kernel/tsc.c
+++ linux/arch/x86/kernel/tsc.c
@@ -1059,6 +1059,23 @@ int unsynchronized_tsc(void)
return 0;
}
+static u32 tsc_numerator;
+static u32 tsc_denominator;
+/*
+ * CHECKME: Do we need the adjust value? It should be 0, but if we run
+ * in a VM this might be a different story.
+ */
+static u64 tsc_adjust;
+
+static u64 art_to_tsc(u64 cycles)
+{
+ /* FIXME: This needs 128bit math to work proper */
+ return tsc_adjust + (cycles * tsc_numerator) / tsc_denominator;
+}
+
+struct correlated_cs art_timestamper = {
+ .convert = art_to_tsc,
+};
static void tsc_refine_calibration_work(struct work_struct *work);
static DECLARE_DELAYED_WORK(tsc_irqwork, tsc_refine_calibration_work);
@@ -1129,6 +1146,16 @@ static void tsc_refine_calibration_work(
(unsigned long)tsc_khz / 1000,
(unsigned long)tsc_khz % 1000);
+ /*
+ * TODO:
+ *
+ * If the system has ART, initialize the art_to_tsc conversion
+ * and set: art_timestamp.related_cs = &tsc_clocksource.
+ *
+ * Before that point a call to get_correlated_timestamp will
+ * fail the clocksource match check and return -ENODEV
+ */
+
out:
clocksource_register_khz(&clocksource_tsc, tsc_khz);
}
Index: linux/include/linux/clocksource.h
===================================================================
--- linux.orig/include/linux/clocksource.h
+++ linux/include/linux/clocksource.h
@@ -258,4 +258,34 @@ void acpi_generic_timer_init(void);
static inline void acpi_generic_timer_init(void) { }
#endif
+/**
+ * struct correlated_cs - Descriptor for a clocksource correlated to another clocksource
+ * @related_cs: Pointer to the related timekeeping clocksource
+ * @convert: Conversion function to convert a timestamp from
+ * the correlated clocksource to cycles of the related
+ * timekeeping clocksource
+ */
+struct correlated_cs {
+ struct clocksource *related_cs;
+ u64 (*convert)(u64 cycles);
+};
+
+struct correlated_ts;
+
+/**
+ * struct correlated_ts - Descriptor for taking a correlated time stamp
+ * @get_ts: Function to read out a synced system and device
+ * timestamp
+ * @system_ts: The raw system clock timestamp
+ * @device_ts: The raw device timestamp
+ * @system_real: @system_ts converted to CLOCK_REALTIME
+ * @system_raw: @system_ts converted to CLOCK_MONOTONIC_RAW
+ */
+struct correlated_ts {
+ int (*get_ts)(struct correlated_ts *ts);
+ u64 system_ts;
+ u64 device_ts;
+ u64 system_real;
+ u64 system_raw;
+};
#endif /* _LINUX_CLOCKSOURCE_H */
Index: linux/include/linux/timekeeping.h
===================================================================
--- linux.orig/include/linux/timekeeping.h
+++ linux/include/linux/timekeeping.h
@@ -258,6 +258,10 @@ extern void timekeeping_inject_sleeptime
*/
extern void getnstime_raw_and_real(struct timespec *ts_raw,
struct timespec *ts_real);
+struct correlated_ts;
+struct correlated_cs;
+extern int get_correlated_timestamp(struct correlated_ts *crt,
+ struct correlated_cs *crs);
/*
* Persistent clock related interfaces
Index: linux/kernel/time/timekeeping.c
===================================================================
--- linux.orig/kernel/time/timekeeping.c
+++ linux/kernel/time/timekeeping.c
@@ -312,6 +312,19 @@ static inline s64 timekeeping_get_ns(str
return nsec + arch_gettimeoffset();
}
+static inline s64 timekeeping_convert_to_ns(struct tk_read_base *tkr,
+ cycle_t cycles)
+{
+ cycle_t delta;
+ s64 nsec;
+
+ /* calculate the delta since the last update_wall_time */
+ delta = clocksource_delta(cycles, tkr->cycle_last, tkr->mask);
+
+ nsec = delta * tkr->mult + tkr->xtime_nsec;
+ return nsec >> tkr->shift;
+}
+
/**
* update_fast_timekeeper - Update the fast and NMI safe monotonic timekeeper.
* @tkr: Timekeeping readout base from which we take the update
@@ -885,6 +898,56 @@ EXPORT_SYMBOL(getnstime_raw_and_real);
#endif /* CONFIG_NTP_PPS */
/**
+ * get_correlated_timestamp - Get a correlated timestamp
+ *
+ * Reads a timestamp from a device and correlates it to system time
+ */
+int get_correlated_timestamp(struct correlated_ts *crt,
+ struct correlated_cs *crs)
+{
+ struct timekeeper *tk = &tk_core.timekeeper;
+ unsigned long seq;
+ cycles_t cycles;
+ ktime_t base;
+ s64 nsecs;
+ int ret;
+
+ do {
+ seq = read_seqcount_begin(&tk_core.seq);
+ /*
+ * Verify that the correlated clocksoure is related to
+ * the currently installed timekeeper clocksoure
+ */
+ if (tk->tkr_mono.clock != crs->related_cs)
+ return -ENODEV;
+
+ /*
+ * Try to get a timestamp from the device.
+ */
+ ret = crt->get_ts(crt);
+ if (ret)
+ return ret;
+
+ /*
+ * Convert the timestamp to timekeeper clock cycles
+ */
+ cycles = crs->convert(crs, crt->system_ts);
+
+ /* Convert to clock realtime */
+ base = ktime_add(tk->tkr_mono.base, tk_core.timekeeper.offs_real);
+ nsecs = timekeeping_convert_to_ns(&tk->tkr_mono, cycles);
+ crt->system_real = ktime_add_ns(base, nsecs);
+
+ /* Convert to clock raw monotonic */
+ base = tk->tkr_raw.base;
+ nsecs = timekeeping_convert_to_ns(&tk->tkr_raw, cycles);
+ crt->system_raw = ktime_add_ns(base, nsecs);
+
+ } while (read_seqcount_retry(&tk_core.seq, seq));
+ return 0;
+}
+
+/**
* do_gettimeofday - Returns the time of day in a timeval
* @tv: pointer to the timeval to be set
*
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web