* [patch 01/26] timekeeping: Remove hardcoded access to tk_core
2025-05-13 15:12 [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Thomas Gleixner
@ 2025-05-13 15:12 ` Thomas Gleixner
2025-05-13 15:12 ` [patch 02/26] timekeeping: Cleanup kernel doc of __ktime_get_real_seconds() Thomas Gleixner
` (25 subsequent siblings)
26 siblings, 0 replies; 32+ messages in thread
From: Thomas Gleixner @ 2025-05-13 15:12 UTC (permalink / raw)
To: LKML
Cc: netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen,
Miroslav Lichvar, Werner Abt, David Woodhouse, Stephen Boyd,
Thomas Weißschuh, Kurt Kanzenbach, Nam Cao, Alex Gieringer
This was overlooked in the initial conversion. Use the provided pointer to
access the shadow timekeeper.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
kernel/time/timekeeping.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
---
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -663,7 +663,7 @@ static void timekeeping_restore_shadow(s
static void timekeeping_update_from_shadow(struct tk_data *tkd, unsigned int action)
{
- struct timekeeper *tk = &tk_core.shadow_timekeeper;
+ struct timekeeper *tk = &tkd->shadow_timekeeper;
lockdep_assert_held(&tkd->lock);
^ permalink raw reply [flat|nested] 32+ messages in thread* [patch 02/26] timekeeping: Cleanup kernel doc of __ktime_get_real_seconds()
2025-05-13 15:12 [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Thomas Gleixner
2025-05-13 15:12 ` [patch 01/26] timekeeping: Remove hardcoded access to tk_core Thomas Gleixner
@ 2025-05-13 15:12 ` Thomas Gleixner
2025-05-13 15:13 ` [patch 03/26] timekeeping: Avoid double notification in do_adjtimex() Thomas Gleixner
` (24 subsequent siblings)
26 siblings, 0 replies; 32+ messages in thread
From: Thomas Gleixner @ 2025-05-13 15:12 UTC (permalink / raw)
To: LKML
Cc: netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen,
Miroslav Lichvar, Werner Abt, David Woodhouse, Stephen Boyd,
Thomas Weißschuh, Kurt Kanzenbach, Nam Cao, Alex Gieringer
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
kernel/time/timekeeping.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -975,9 +975,14 @@ time64_t ktime_get_real_seconds(void)
EXPORT_SYMBOL_GPL(ktime_get_real_seconds);
/**
- * __ktime_get_real_seconds - The same as ktime_get_real_seconds
- * but without the sequence counter protect. This internal function
- * is called just when timekeeping lock is already held.
+ * __ktime_get_real_seconds - Unprotected access to CLOCK_REALTIME seconds
+ *
+ * The same as ktime_get_real_seconds() but without the sequence counter
+ * protection. This function is used in restricted contexts like the x86 MCE
+ * handler and in KGDB. It's unprotected on 32-bit vs. concurrent half
+ * completed modification and only to be used for such critical contexts.
+ *
+ * Returns: Racy snapshot of the CLOCK_REALTIME seconds value
*/
noinstr time64_t __ktime_get_real_seconds(void)
{
^ permalink raw reply [flat|nested] 32+ messages in thread* [patch 03/26] timekeeping: Avoid double notification in do_adjtimex()
2025-05-13 15:12 [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Thomas Gleixner
2025-05-13 15:12 ` [patch 01/26] timekeeping: Remove hardcoded access to tk_core Thomas Gleixner
2025-05-13 15:12 ` [patch 02/26] timekeeping: Cleanup kernel doc of __ktime_get_real_seconds() Thomas Gleixner
@ 2025-05-13 15:13 ` Thomas Gleixner
2025-05-13 15:13 ` [patch 04/26] timekeeping: Introduce timekeeper ID Thomas Gleixner
` (23 subsequent siblings)
26 siblings, 0 replies; 32+ messages in thread
From: Thomas Gleixner @ 2025-05-13 15:13 UTC (permalink / raw)
To: LKML
Cc: netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen,
Miroslav Lichvar, Werner Abt, David Woodhouse, Stephen Boyd,
Thomas Weißschuh, Kurt Kanzenbach, Nam Cao, Alex Gieringer
Consolidate do_adjtimex() so that it does not notify about clock changes
twice.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
kernel/time/timekeeping.c | 98 ++++++++++++++++++++++++++--------------------
1 file changed, 56 insertions(+), 42 deletions(-)
---
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -1413,40 +1413,49 @@ int do_settimeofday64(const struct times
EXPORT_SYMBOL(do_settimeofday64);
/**
- * timekeeping_inject_offset - Adds or subtracts from the current time.
+ * __timekeeping_inject_offset - Adds or subtracts from the current time.
* @ts: Pointer to the timespec variable containing the offset
*
* Adds or subtracts an offset value from the current time.
*/
-static int timekeeping_inject_offset(const struct timespec64 *ts)
+static int __timekeeping_inject_offset(const struct timespec64 *ts)
{
+ struct timekeeper *tks = &tk_core.shadow_timekeeper;
+ struct timespec64 tmp;
+
if (ts->tv_nsec < 0 || ts->tv_nsec >= NSEC_PER_SEC)
return -EINVAL;
- scoped_guard (raw_spinlock_irqsave, &tk_core.lock) {
- struct timekeeper *tks = &tk_core.shadow_timekeeper;
- struct timespec64 tmp;
-
- timekeeping_forward_now(tks);
- /* Make sure the proposed value is valid */
- tmp = timespec64_add(tk_xtime(tks), *ts);
- if (timespec64_compare(&tks->wall_to_monotonic, ts) > 0 ||
- !timespec64_valid_settod(&tmp)) {
- timekeeping_restore_shadow(&tk_core);
- return -EINVAL;
- }
+ timekeeping_forward_now(tks);
- tk_xtime_add(tks, ts);
- tk_set_wall_to_mono(tks, timespec64_sub(tks->wall_to_monotonic, *ts));
- timekeeping_update_from_shadow(&tk_core, TK_UPDATE_ALL);
+ /* Make sure the proposed value is valid */
+ tmp = timespec64_add(tk_xtime(tks), *ts);
+ if (timespec64_compare(&tks->wall_to_monotonic, ts) > 0 ||
+ !timespec64_valid_settod(&tmp)) {
+ timekeeping_restore_shadow(&tk_core);
+ return -EINVAL;
}
- /* Signal hrtimers about time change */
- clock_was_set(CLOCK_SET_WALL);
+ tk_xtime_add(tks, ts);
+ tk_set_wall_to_mono(tks, timespec64_sub(tks->wall_to_monotonic, *ts));
+ timekeeping_update_from_shadow(&tk_core, TK_UPDATE_ALL);
return 0;
}
+static int timekeeping_inject_offset(const struct timespec64 *ts)
+{
+ int ret;
+
+ scoped_guard (raw_spinlock_irqsave, &tk_core.lock)
+ ret = __timekeeping_inject_offset(ts);
+
+ /* Signal hrtimers about time change */
+ if (!ret)
+ clock_was_set(CLOCK_SET_WALL);
+ return ret;
+}
+
/*
* Indicates if there is an offset between the system clock and the hardware
* clock/persistent clock/rtc.
@@ -2181,7 +2190,7 @@ static u64 logarithmic_accumulation(stru
* timekeeping_advance - Updates the timekeeper to the current time and
* current NTP tick length
*/
-static bool timekeeping_advance(enum timekeeping_adv_mode mode)
+static bool __timekeeping_advance(enum timekeeping_adv_mode mode)
{
struct timekeeper *tk = &tk_core.shadow_timekeeper;
struct timekeeper *real_tk = &tk_core.timekeeper;
@@ -2189,8 +2198,6 @@ static bool timekeeping_advance(enum tim
int shift = 0, maxshift;
u64 offset, orig_offset;
- guard(raw_spinlock_irqsave)(&tk_core.lock);
-
/* Make sure we're fully resumed: */
if (unlikely(timekeeping_suspended))
return false;
@@ -2244,6 +2251,12 @@ static bool timekeeping_advance(enum tim
return !!clock_set;
}
+static bool timekeeping_advance(enum timekeeping_adv_mode mode)
+{
+ guard(raw_spinlock_irqsave)(&tk_core.lock);
+ return __timekeeping_advance(mode);
+}
+
/**
* update_wall_time - Uses the current clocksource to increment the wall time
*
@@ -2532,10 +2545,10 @@ EXPORT_SYMBOL_GPL(random_get_entropy_fal
*/
int do_adjtimex(struct __kernel_timex *txc)
{
+ struct timespec64 delta, ts;
struct audit_ntp_data ad;
bool offset_set = false;
bool clock_set = false;
- struct timespec64 ts;
int ret;
/* Validate the data before disabling interrupts */
@@ -2544,21 +2557,6 @@ int do_adjtimex(struct __kernel_timex *t
return ret;
add_device_randomness(txc, sizeof(*txc));
- if (txc->modes & ADJ_SETOFFSET) {
- struct timespec64 delta;
-
- delta.tv_sec = txc->time.tv_sec;
- delta.tv_nsec = txc->time.tv_usec;
- if (!(txc->modes & ADJ_NANO))
- delta.tv_nsec *= 1000;
- ret = timekeeping_inject_offset(&delta);
- if (ret)
- return ret;
-
- offset_set = delta.tv_sec != 0;
- audit_tk_injoffset(delta);
- }
-
audit_ntp_init(&ad);
ktime_get_real_ts64(&ts);
@@ -2568,6 +2566,19 @@ int do_adjtimex(struct __kernel_timex *t
struct timekeeper *tks = &tk_core.shadow_timekeeper;
s32 orig_tai, tai;
+ if (txc->modes & ADJ_SETOFFSET) {
+ delta.tv_sec = txc->time.tv_sec;
+ delta.tv_nsec = txc->time.tv_usec;
+ if (!(txc->modes & ADJ_NANO))
+ delta.tv_nsec *= 1000;
+ ret = __timekeeping_inject_offset(&delta);
+ if (ret)
+ return ret;
+
+ offset_set = delta.tv_sec != 0;
+ clock_set = true;
+ }
+
orig_tai = tai = tks->tai_offset;
ret = __do_adjtimex(txc, &ts, &tai, &ad);
@@ -2578,13 +2589,16 @@ int do_adjtimex(struct __kernel_timex *t
} else {
tk_update_leap_state_all(&tk_core);
}
+
+ /* Update the multiplier immediately if frequency was set directly */
+ if (txc->modes & (ADJ_FREQUENCY | ADJ_TICK))
+ clock_set |= __timekeeping_advance(TK_ADV_FREQ);
}
- audit_ntp_log(&ad);
+ if (txc->modes & ADJ_SETOFFSET)
+ audit_tk_injoffset(delta);
- /* Update the multiplier immediately if frequency was set directly */
- if (txc->modes & (ADJ_FREQUENCY | ADJ_TICK))
- clock_set |= timekeeping_advance(TK_ADV_FREQ);
+ audit_ntp_log(&ad);
if (clock_set)
clock_was_set(CLOCK_SET_WALL);
^ permalink raw reply [flat|nested] 32+ messages in thread* [patch 04/26] timekeeping: Introduce timekeeper ID
2025-05-13 15:12 [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Thomas Gleixner
` (2 preceding siblings ...)
2025-05-13 15:13 ` [patch 03/26] timekeeping: Avoid double notification in do_adjtimex() Thomas Gleixner
@ 2025-05-13 15:13 ` Thomas Gleixner
2025-05-13 15:13 ` [patch 05/26] time: Introduce PTP clocks Thomas Gleixner
` (22 subsequent siblings)
26 siblings, 0 replies; 32+ messages in thread
From: Thomas Gleixner @ 2025-05-13 15:13 UTC (permalink / raw)
To: LKML
Cc: netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen,
Miroslav Lichvar, Werner Abt, David Woodhouse, Stephen Boyd,
Thomas Weißschuh, Kurt Kanzenbach, Nam Cao, Alex Gieringer
From: Anna-Maria Behnsen <anna-maria@linutronix.de>
As long as there is only a single timekeeper, there is no need to clarify
which timekeeper is used. But with the upcoming reusage of the timekeeper
infrastructure for per PTP clock timekeepers, an ID is required to
differentiate.
Introduce an enum for timekeeper IDs, introduce a field in struct tk_data
to store this timekeeper id and add also initialization. The id struct
field is added at the end of the second cachline, as there is a 4 byte hole
anyway.
Signed-off-by: Anna-Maria Behnsen <anna-maria@linutronix.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
include/linux/timekeeper_internal.h | 14 +++++++++++++-
kernel/time/timekeeping.c | 5 +++--
2 files changed, 16 insertions(+), 3 deletions(-)
---
--- a/include/linux/timekeeper_internal.h
+++ b/include/linux/timekeeper_internal.h
@@ -12,6 +12,16 @@
#include <linux/time.h>
/**
+ * timekeeper_ids - IDs for various time keepers in the kernel
+ * @TIMEKEEPER_CORE: The central core timekeeper managing system time
+ * @TIMEKEEPERS_MAX: The maximum number of timekeepers managed
+ */
+enum timekeeper_ids {
+ TIMEKEEPER_CORE,
+ TIMEKEEPERS_MAX,
+};
+
+/**
* struct tk_read_base - base structure for timekeeping readout
* @clock: Current clocksource used for timekeeping.
* @mask: Bitmask for two's complement subtraction of non 64bit clocks
@@ -52,6 +62,7 @@ struct tk_read_base {
* @offs_boot: Offset clock monotonic -> clock boottime
* @offs_tai: Offset clock monotonic -> clock tai
* @coarse_nsec: The nanoseconds part for coarse time getters
+ * @id: The timekeeper ID
* @tkr_raw: The readout base structure for CLOCK_MONOTONIC_RAW
* @raw_sec: CLOCK_MONOTONIC_RAW time in seconds
* @clock_was_set_seq: The sequence number of clock was set events
@@ -101,7 +112,7 @@ struct tk_read_base {
* which results in the following cacheline layout:
*
* 0: seqcount, tkr_mono
- * 1: xtime_sec ... coarse_nsec
+ * 1: xtime_sec ... id
* 2: tkr_raw, raw_sec
* 3,4: Internal variables
*
@@ -123,6 +134,7 @@ struct timekeeper {
ktime_t offs_boot;
ktime_t offs_tai;
u32 coarse_nsec;
+ enum timekeeper_ids id;
/* Cacheline 2: */
struct tk_read_base tkr_raw;
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -1658,10 +1658,11 @@ read_persistent_wall_and_boot_offset(str
*boot_offset = ns_to_timespec64(local_clock());
}
-static __init void tkd_basic_setup(struct tk_data *tkd)
+static __init void tkd_basic_setup(struct tk_data *tkd, enum timekeeper_ids tk_id)
{
raw_spin_lock_init(&tkd->lock);
seqcount_raw_spinlock_init(&tkd->seq, &tkd->lock);
+ tkd->timekeeper.id = tkd->shadow_timekeeper.id = tk_id;
}
/*
@@ -1691,7 +1692,7 @@ void __init timekeeping_init(void)
struct timekeeper *tks = &tk_core.shadow_timekeeper;
struct clocksource *clock;
- tkd_basic_setup(&tk_core);
+ tkd_basic_setup(&tk_core, TIMEKEEPER_CORE);
read_persistent_wall_and_boot_offset(&wall_time, &boot_offset);
if (timespec64_valid_settod(&wall_time) &&
^ permalink raw reply [flat|nested] 32+ messages in thread* [patch 05/26] time: Introduce PTP clocks
2025-05-13 15:12 [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Thomas Gleixner
` (3 preceding siblings ...)
2025-05-13 15:13 ` [patch 04/26] timekeeping: Introduce timekeeper ID Thomas Gleixner
@ 2025-05-13 15:13 ` Thomas Gleixner
2025-05-13 15:13 ` [patch 06/26] ntp: Add support for PTP timekeepers Thomas Gleixner
` (21 subsequent siblings)
26 siblings, 0 replies; 32+ messages in thread
From: Thomas Gleixner @ 2025-05-13 15:13 UTC (permalink / raw)
To: LKML
Cc: netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen,
Miroslav Lichvar, Werner Abt, David Woodhouse, Stephen Boyd,
Thomas Weißschuh, Kurt Kanzenbach, Nam Cao, Alex Gieringer
From: Anna-Maria Behnsen <anna-maria@linutronix.de>
To support per PTP clock timekeeping and the related user space interfaces,
it's required to define a clock ID range for them.
Reserve 8 PTP clock IDs after the regular timekeeping clock ID space.
This is the maximum number of PTP clocks the kernel can support. The actual
number of supported clocks depends obviously on the presence of PTP devices
and might be constraint by the available VDSO space.
Add the corresponding timekeeper IDs as well.
Signed-off-by: Anna-Maria Behnsen <anna-maria@linutronix.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
include/linux/timekeeper_internal.h | 6 ++++++
include/uapi/linux/time.h | 10 ++++++++++
kernel/time/Kconfig | 3 +++
3 files changed, 19 insertions(+)
---
--- a/include/linux/timekeeper_internal.h
+++ b/include/linux/timekeeper_internal.h
@@ -14,10 +14,16 @@
/**
* timekeeper_ids - IDs for various time keepers in the kernel
* @TIMEKEEPER_CORE: The central core timekeeper managing system time
+ * @TIMEKEEPER_PTP: The first PTP timekeeper
+ * @TIMEKEEPER_PTP_LAST:The last PTP timekeeper
* @TIMEKEEPERS_MAX: The maximum number of timekeepers managed
*/
enum timekeeper_ids {
TIMEKEEPER_CORE,
+#ifdef CONFIG_POSIX_PTP_CLOCKS
+ TIMEKEEPER_PTP,
+ TIMEKEEPER_PTP_LAST = TIMEKEEPER_PTP + MAX_PTP_CLOCKS - 1,
+#endif
TIMEKEEPERS_MAX,
};
--- a/include/uapi/linux/time.h
+++ b/include/uapi/linux/time.h
@@ -64,6 +64,16 @@ struct timezone {
#define CLOCK_TAI 11
#define MAX_CLOCKS 16
+
+/*
+ * PTP clock support. PTP clocks are dynamically configured by associating
+ * a clock ID to a PTP device. The kernel can support up to 16 PTP clocks,
+ * but the actual limit depends on architecture constraints vs. VDSO.
+ */
+#define CLOCK_PTP MAX_CLOCKS
+#define MAX_PTP_CLOCKS 8
+#define CLOCK_PTP_LAST (CLOCK_PTP + MAX_PTP_CLOCKS - 1)
+
#define CLOCKS_MASK (CLOCK_REALTIME | CLOCK_MONOTONIC)
#define CLOCKS_MONO CLOCK_MONOTONIC
--- a/kernel/time/Kconfig
+++ b/kernel/time/Kconfig
@@ -57,6 +57,9 @@ config POSIX_CPU_TIMERS_TASK_WORK
bool
default y if POSIX_TIMERS && HAVE_POSIX_CPU_TIMERS_TASK_WORK
+config POSIX_PTP_CLOCKS
+ def_bool POSIX_TIMERS && PTP_1588_CLOCK
+
config LEGACY_TIMER_TICK
bool
help
^ permalink raw reply [flat|nested] 32+ messages in thread* [patch 06/26] ntp: Add support for PTP timekeepers
2025-05-13 15:12 [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Thomas Gleixner
` (4 preceding siblings ...)
2025-05-13 15:13 ` [patch 05/26] time: Introduce PTP clocks Thomas Gleixner
@ 2025-05-13 15:13 ` Thomas Gleixner
2025-05-13 15:13 ` [patch 07/26] ntp: Add timekeeper ID arguments to public functions Thomas Gleixner
` (20 subsequent siblings)
26 siblings, 0 replies; 32+ messages in thread
From: Thomas Gleixner @ 2025-05-13 15:13 UTC (permalink / raw)
To: LKML
Cc: netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen,
Miroslav Lichvar, Werner Abt, David Woodhouse, Stephen Boyd,
Thomas Weißschuh, Kurt Kanzenbach, Nam Cao, Alex Gieringer
If PTP clocks are enabled, provide an array of NTP data so that independent
PTP clock timekeepers can be steered accordingly.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
kernel/time/ntp.c | 41 ++++++++++++++++++++++-------------------
1 file changed, 22 insertions(+), 19 deletions(-)
---
--- a/kernel/time/ntp.c
+++ b/kernel/time/ntp.c
@@ -18,6 +18,7 @@
#include <linux/module.h>
#include <linux/rtc.h>
#include <linux/audit.h>
+#include <linux/timekeeper_internal.h>
#include "ntp_internal.h"
#include "timekeeping_internal.h"
@@ -86,14 +87,16 @@ struct ntp_data {
#endif
};
-static struct ntp_data tk_ntp_data = {
- .tick_usec = USER_TICK_USEC,
- .time_state = TIME_OK,
- .time_status = STA_UNSYNC,
- .time_constant = 2,
- .time_maxerror = NTP_PHASE_LIMIT,
- .time_esterror = NTP_PHASE_LIMIT,
- .ntp_next_leap_sec = TIME64_MAX,
+static struct ntp_data tk_ntp_data[TIMEKEEPERS_MAX] = {
+ [ 0 ... TIMEKEEPERS_MAX - 1 ] = {
+ .tick_usec = USER_TICK_USEC,
+ .time_state = TIME_OK,
+ .time_status = STA_UNSYNC,
+ .time_constant = 2,
+ .time_maxerror = NTP_PHASE_LIMIT,
+ .time_esterror = NTP_PHASE_LIMIT,
+ .ntp_next_leap_sec = TIME64_MAX,
+ },
};
#define SECS_PER_DAY 86400
@@ -351,13 +354,13 @@ static void __ntp_clear(struct ntp_data
*/
void ntp_clear(void)
{
- __ntp_clear(&tk_ntp_data);
+ __ntp_clear(&tk_ntp_data[TIMEKEEPER_CORE]);
}
u64 ntp_tick_length(void)
{
- return tk_ntp_data.tick_length;
+ return tk_ntp_data[TIMEKEEPER_CORE].tick_length;
}
/**
@@ -368,7 +371,7 @@ u64 ntp_tick_length(void)
*/
ktime_t ntp_get_next_leap(void)
{
- struct ntp_data *ntpdata = &tk_ntp_data;
+ struct ntp_data *ntpdata = &tk_ntp_data[TIMEKEEPER_CORE];
ktime_t ret;
if ((ntpdata->time_state == TIME_INS) && (ntpdata->time_status & STA_INS))
@@ -389,7 +392,7 @@ ktime_t ntp_get_next_leap(void)
*/
int second_overflow(time64_t secs)
{
- struct ntp_data *ntpdata = &tk_ntp_data;
+ struct ntp_data *ntpdata = &tk_ntp_data[TIMEKEEPER_CORE];
s64 delta;
int leap = 0;
s32 rem;
@@ -605,7 +608,7 @@ static inline int update_rtc(struct time
*/
static inline bool ntp_synced(void)
{
- return !(tk_ntp_data.time_status & STA_UNSYNC);
+ return !(tk_ntp_data[TIMEKEEPER_CORE].time_status & STA_UNSYNC);
}
/*
@@ -762,7 +765,7 @@ static inline void process_adjtimex_mode
int __do_adjtimex(struct __kernel_timex *txc, const struct timespec64 *ts,
s32 *time_tai, struct audit_ntp_data *ad)
{
- struct ntp_data *ntpdata = &tk_ntp_data;
+ struct ntp_data *ntpdata = &tk_ntp_data[TIMEKEEPER_CORE];
int result;
if (txc->modes & ADJ_ADJTIME) {
@@ -1031,8 +1034,8 @@ static void hardpps_update_phase(struct
*/
void __hardpps(const struct timespec64 *phase_ts, const struct timespec64 *raw_ts)
{
+ struct ntp_data *ntpdata = &tk_ntp_data[TIMEKEEPER_CORE];
struct pps_normtime pts_norm, freq_norm;
- struct ntp_data *ntpdata = &tk_ntp_data;
pts_norm = pps_normalize_ts(*phase_ts);
@@ -1083,18 +1086,18 @@ void __hardpps(const struct timespec64 *
static int __init ntp_tick_adj_setup(char *str)
{
- int rc = kstrtos64(str, 0, &tk_ntp_data.ntp_tick_adj);
+ int rc = kstrtos64(str, 0, &tk_ntp_data[TIMEKEEPER_CORE].ntp_tick_adj);
if (rc)
return rc;
- tk_ntp_data.ntp_tick_adj <<= NTP_SCALE_SHIFT;
+ tk_ntp_data[TIMEKEEPER_CORE].ntp_tick_adj <<= NTP_SCALE_SHIFT;
return 1;
}
-
__setup("ntp_tick_adj=", ntp_tick_adj_setup);
void __init ntp_init(void)
{
- ntp_clear();
+ for (int id = 0; id < TIMEKEEPERS_MAX; id++)
+ __ntp_clear(tk_ntp_data + id);
ntp_init_cmos_sync();
}
^ permalink raw reply [flat|nested] 32+ messages in thread* [patch 07/26] ntp: Add timekeeper ID arguments to public functions
2025-05-13 15:12 [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Thomas Gleixner
` (5 preceding siblings ...)
2025-05-13 15:13 ` [patch 06/26] ntp: Add support for PTP timekeepers Thomas Gleixner
@ 2025-05-13 15:13 ` Thomas Gleixner
2025-05-13 15:13 ` [patch 08/26] ntp: Rename __do_adjtimex() to ntp_adjtimex() Thomas Gleixner
` (19 subsequent siblings)
26 siblings, 0 replies; 32+ messages in thread
From: Thomas Gleixner @ 2025-05-13 15:13 UTC (permalink / raw)
To: LKML
Cc: netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen,
Miroslav Lichvar, Werner Abt, David Woodhouse, Stephen Boyd,
Thomas Weißschuh, Kurt Kanzenbach, Nam Cao, Alex Gieringer
In preparation for supporting independent PTP clocks, add a time keeper ID
to the relevant functions.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
kernel/time/ntp.c | 33 +++++++++++++++++++--------------
kernel/time/ntp_internal.h | 11 +++++------
kernel/time/timekeeping.c | 12 ++++++------
3 files changed, 30 insertions(+), 26 deletions(-)
---
--- a/kernel/time/ntp.c
+++ b/kernel/time/ntp.c
@@ -351,33 +351,38 @@ static void __ntp_clear(struct ntp_data
/**
* ntp_clear - Clears the NTP state variables
+ * @tkid: Timekeeper ID to be able to select proper ntp data array member
*/
-void ntp_clear(void)
+void ntp_clear(unsigned int tkid)
{
- __ntp_clear(&tk_ntp_data[TIMEKEEPER_CORE]);
+ __ntp_clear(&tk_ntp_data[tkid]);
}
-u64 ntp_tick_length(void)
+u64 ntp_tick_length(unsigned int tkid)
{
- return tk_ntp_data[TIMEKEEPER_CORE].tick_length;
+ return tk_ntp_data[tkid].tick_length;
}
/**
* ntp_get_next_leap - Returns the next leapsecond in CLOCK_REALTIME ktime_t
+ * @tkid: Timekeeper ID
*
- * Provides the time of the next leapsecond against CLOCK_REALTIME in
- * a ktime_t format. Returns KTIME_MAX if no leapsecond is pending.
+ * Returns: For @tkid == TIMEKEEPER_CORE this provides the time of the next
+ * leap second against CLOCK_REALTIME in a ktime_t format if a
+ * leap second is pending. KTIME_MAX otherwise.
*/
-ktime_t ntp_get_next_leap(void)
+ktime_t ntp_get_next_leap(unsigned int tkid)
{
struct ntp_data *ntpdata = &tk_ntp_data[TIMEKEEPER_CORE];
- ktime_t ret;
+
+ if (tkid != TIMEKEEPER_CORE)
+ return KTIME_MAX;
if ((ntpdata->time_state == TIME_INS) && (ntpdata->time_status & STA_INS))
return ktime_set(ntpdata->ntp_next_leap_sec, 0);
- ret = KTIME_MAX;
- return ret;
+
+ return KTIME_MAX;
}
/*
@@ -390,9 +395,9 @@ ktime_t ntp_get_next_leap(void)
*
* Also handles leap second processing, and returns leap offset
*/
-int second_overflow(time64_t secs)
+int second_overflow(unsigned int tkid, time64_t secs)
{
- struct ntp_data *ntpdata = &tk_ntp_data[TIMEKEEPER_CORE];
+ struct ntp_data *ntpdata = &tk_ntp_data[tkid];
s64 delta;
int leap = 0;
s32 rem;
@@ -762,10 +767,10 @@ static inline void process_adjtimex_mode
* adjtimex() mainly allows reading (and writing, if superuser) of
* kernel time-keeping variables. used by xntpd.
*/
-int __do_adjtimex(struct __kernel_timex *txc, const struct timespec64 *ts,
+int __do_adjtimex(unsigned int tkid, struct __kernel_timex *txc, const struct timespec64 *ts,
s32 *time_tai, struct audit_ntp_data *ad)
{
- struct ntp_data *ntpdata = &tk_ntp_data[TIMEKEEPER_CORE];
+ struct ntp_data *ntpdata = &tk_ntp_data[tkid];
int result;
if (txc->modes & ADJ_ADJTIME) {
--- a/kernel/time/ntp_internal.h
+++ b/kernel/time/ntp_internal.h
@@ -3,13 +3,12 @@
#define _LINUX_NTP_INTERNAL_H
extern void ntp_init(void);
-extern void ntp_clear(void);
+extern void ntp_clear(unsigned int tkid);
/* Returns how long ticks are at present, in ns / 2^NTP_SCALE_SHIFT. */
-extern u64 ntp_tick_length(void);
-extern ktime_t ntp_get_next_leap(void);
-extern int second_overflow(time64_t secs);
-extern int __do_adjtimex(struct __kernel_timex *txc,
- const struct timespec64 *ts,
+extern u64 ntp_tick_length(unsigned int tkid);
+extern ktime_t ntp_get_next_leap(unsigned int tkid);
+extern int second_overflow(unsigned int tkid, time64_t secs);
+extern int __do_adjtimex(unsigned int tkid, struct __kernel_timex *txc, const struct timespec64 *ts,
s32 *time_tai, struct audit_ntp_data *ad);
extern void __hardpps(const struct timespec64 *phase_ts, const struct timespec64 *raw_ts);
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -601,7 +601,7 @@ EXPORT_SYMBOL_GPL(pvclock_gtod_unregiste
*/
static inline void tk_update_leap_state(struct timekeeper *tk)
{
- tk->next_leap_ktime = ntp_get_next_leap();
+ tk->next_leap_ktime = ntp_get_next_leap(tk->id);
if (tk->next_leap_ktime != KTIME_MAX)
/* Convert to monotonic time */
tk->next_leap_ktime = ktime_sub(tk->next_leap_ktime, tk->offs_real);
@@ -678,7 +678,7 @@ static void timekeeping_update_from_shad
if (action & TK_CLEAR_NTP) {
tk->ntp_error = 0;
- ntp_clear();
+ ntp_clear(tk->id);
}
tk_update_leap_state(tk);
@@ -2044,7 +2044,7 @@ static __always_inline void timekeeping_
*/
static void timekeeping_adjust(struct timekeeper *tk, s64 offset)
{
- u64 ntp_tl = ntp_tick_length();
+ u64 ntp_tl = ntp_tick_length(tk->id);
u32 mult;
/*
@@ -2125,7 +2125,7 @@ static inline unsigned int accumulate_ns
}
/* Figure out if its a leap sec and apply if needed */
- leap = second_overflow(tk->xtime_sec);
+ leap = second_overflow(tk->id, tk->xtime_sec);
if (unlikely(leap)) {
struct timespec64 ts;
@@ -2222,7 +2222,7 @@ static bool __timekeeping_advance(enum t
shift = ilog2(offset) - ilog2(tk->cycle_interval);
shift = max(0, shift);
/* Bound shift to one less than what overflows tick_length */
- maxshift = (64 - (ilog2(ntp_tick_length())+1)) - 1;
+ maxshift = (64 - (ilog2(ntp_tick_length(tk->id)) + 1)) - 1;
shift = min(shift, maxshift);
while (offset >= tk->cycle_interval) {
offset = logarithmic_accumulation(tk, offset, shift, &clock_set);
@@ -2581,7 +2581,7 @@ int do_adjtimex(struct __kernel_timex *t
}
orig_tai = tai = tks->tai_offset;
- ret = __do_adjtimex(txc, &ts, &tai, &ad);
+ ret = __do_adjtimex(tks->id, txc, &ts, &tai, &ad);
if (tai != orig_tai) {
__timekeeping_set_tai_offset(tks, tai);
^ permalink raw reply [flat|nested] 32+ messages in thread* [patch 08/26] ntp: Rename __do_adjtimex() to ntp_adjtimex()
2025-05-13 15:12 [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Thomas Gleixner
` (6 preceding siblings ...)
2025-05-13 15:13 ` [patch 07/26] ntp: Add timekeeper ID arguments to public functions Thomas Gleixner
@ 2025-05-13 15:13 ` Thomas Gleixner
2025-05-13 15:13 ` [patch 09/26] timekeeping: Make __timekeeping_advance() reusable Thomas Gleixner
` (18 subsequent siblings)
26 siblings, 0 replies; 32+ messages in thread
From: Thomas Gleixner @ 2025-05-13 15:13 UTC (permalink / raw)
To: LKML
Cc: netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen,
Miroslav Lichvar, Werner Abt, David Woodhouse, Stephen Boyd,
Thomas Weißschuh, Kurt Kanzenbach, Nam Cao, Alex Gieringer
Clean up the name space. No functional change.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
kernel/time/ntp.c | 4 ++--
kernel/time/ntp_internal.h | 4 ++--
kernel/time/timekeeping.c | 2 +-
3 files changed, 5 insertions(+), 5 deletions(-)
---
--- a/kernel/time/ntp.c
+++ b/kernel/time/ntp.c
@@ -767,8 +767,8 @@ static inline void process_adjtimex_mode
* adjtimex() mainly allows reading (and writing, if superuser) of
* kernel time-keeping variables. used by xntpd.
*/
-int __do_adjtimex(unsigned int tkid, struct __kernel_timex *txc, const struct timespec64 *ts,
- s32 *time_tai, struct audit_ntp_data *ad)
+int ntp_adjtimex(unsigned int tkid, struct __kernel_timex *txc, const struct timespec64 *ts,
+ s32 *time_tai, struct audit_ntp_data *ad)
{
struct ntp_data *ntpdata = &tk_ntp_data[tkid];
int result;
--- a/kernel/time/ntp_internal.h
+++ b/kernel/time/ntp_internal.h
@@ -8,8 +8,8 @@ extern void ntp_clear(unsigned int tkid)
extern u64 ntp_tick_length(unsigned int tkid);
extern ktime_t ntp_get_next_leap(unsigned int tkid);
extern int second_overflow(unsigned int tkid, time64_t secs);
-extern int __do_adjtimex(unsigned int tkid, struct __kernel_timex *txc, const struct timespec64 *ts,
- s32 *time_tai, struct audit_ntp_data *ad);
+extern int ntp_adjtimex(unsigned int tkid, struct __kernel_timex *txc, const struct timespec64 *ts,
+ s32 *time_tai, struct audit_ntp_data *ad);
extern void __hardpps(const struct timespec64 *phase_ts, const struct timespec64 *raw_ts);
#if defined(CONFIG_GENERIC_CMOS_UPDATE) || defined(CONFIG_RTC_SYSTOHC)
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -2581,7 +2581,7 @@ int do_adjtimex(struct __kernel_timex *t
}
orig_tai = tai = tks->tai_offset;
- ret = __do_adjtimex(tks->id, txc, &ts, &tai, &ad);
+ ret = ntp_adjtimex(tks->id, txc, &ts, &tai, &ad);
if (tai != orig_tai) {
__timekeeping_set_tai_offset(tks, tai);
^ permalink raw reply [flat|nested] 32+ messages in thread* [patch 09/26] timekeeping: Make __timekeeping_advance() reusable
2025-05-13 15:12 [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Thomas Gleixner
` (7 preceding siblings ...)
2025-05-13 15:13 ` [patch 08/26] ntp: Rename __do_adjtimex() to ntp_adjtimex() Thomas Gleixner
@ 2025-05-13 15:13 ` Thomas Gleixner
2025-05-13 15:13 ` [patch 10/26] timekeeping: Prepare timekeeping_update_from_shadow() Thomas Gleixner
` (17 subsequent siblings)
26 siblings, 0 replies; 32+ messages in thread
From: Thomas Gleixner @ 2025-05-13 15:13 UTC (permalink / raw)
To: LKML
Cc: netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen,
Miroslav Lichvar, Werner Abt, David Woodhouse, Stephen Boyd,
Thomas Weißschuh, Kurt Kanzenbach, Nam Cao, Alex Gieringer
From: Anna-Maria Behnsen <anna-maria@linutronix.de>
In __timekeeping_advance() the pointer to struct tk_data is hardcoded by the
use of &tk_core. As long as there is only a single timekeeper (tk_core),
this is not a problem. But when __timekeeping_advance() will be reused for
per ptp clock timekeepers, __timekeeping_advance() needs to be generalized.
Add a pointer to struct tk_data as function argument of
__timekeeping_advance() and adapt all call sites.
Signed-off-by: Anna-Maria Behnsen <anna-maria@linutronix.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
kernel/time/timekeeping.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
---
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -2191,10 +2191,10 @@ static u64 logarithmic_accumulation(stru
* timekeeping_advance - Updates the timekeeper to the current time and
* current NTP tick length
*/
-static bool __timekeeping_advance(enum timekeeping_adv_mode mode)
+static bool __timekeeping_advance(struct tk_data *tkd, enum timekeeping_adv_mode mode)
{
- struct timekeeper *tk = &tk_core.shadow_timekeeper;
- struct timekeeper *real_tk = &tk_core.timekeeper;
+ struct timekeeper *tk = &tkd->shadow_timekeeper;
+ struct timekeeper *real_tk = &tkd->timekeeper;
unsigned int clock_set = 0;
int shift = 0, maxshift;
u64 offset, orig_offset;
@@ -2247,7 +2247,7 @@ static bool __timekeeping_advance(enum t
if (orig_offset != offset)
tk_update_coarse_nsecs(tk);
- timekeeping_update_from_shadow(&tk_core, clock_set);
+ timekeeping_update_from_shadow(tkd, clock_set);
return !!clock_set;
}
@@ -2255,7 +2255,7 @@ static bool __timekeeping_advance(enum t
static bool timekeeping_advance(enum timekeeping_adv_mode mode)
{
guard(raw_spinlock_irqsave)(&tk_core.lock);
- return __timekeeping_advance(mode);
+ return __timekeeping_advance(&tk_core, mode);
}
/**
@@ -2593,7 +2593,7 @@ int do_adjtimex(struct __kernel_timex *t
/* Update the multiplier immediately if frequency was set directly */
if (txc->modes & (ADJ_FREQUENCY | ADJ_TICK))
- clock_set |= __timekeeping_advance(TK_ADV_FREQ);
+ clock_set |= __timekeeping_advance(&tk_core, TK_ADV_FREQ);
}
if (txc->modes & ADJ_SETOFFSET)
^ permalink raw reply [flat|nested] 32+ messages in thread* [patch 10/26] timekeeping: Prepare timekeeping_update_from_shadow()
2025-05-13 15:12 [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Thomas Gleixner
` (8 preceding siblings ...)
2025-05-13 15:13 ` [patch 09/26] timekeeping: Make __timekeeping_advance() reusable Thomas Gleixner
@ 2025-05-13 15:13 ` Thomas Gleixner
2025-05-13 15:13 ` [patch 11/26] timekeeping: Add clock_valid flag to timekeeper Thomas Gleixner
` (16 subsequent siblings)
26 siblings, 0 replies; 32+ messages in thread
From: Thomas Gleixner @ 2025-05-13 15:13 UTC (permalink / raw)
To: LKML
Cc: netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen,
Miroslav Lichvar, Werner Abt, David Woodhouse, Stephen Boyd,
Thomas Weißschuh, Kurt Kanzenbach, Nam Cao, Alex Gieringer
From: Thomas Gleixner <tglx@linutronix.de>
Don't invoke the VDSO and paravirt updates when utilized for independent
PTP clocks. This is a temporary workaround until the VDSO and paravirt
interfaces have been worked out.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
kernel/time/timekeeping.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
---
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -683,13 +683,15 @@ static void timekeeping_update_from_shad
tk_update_leap_state(tk);
tk_update_ktime_data(tk);
+ tk->tkr_mono.base_real = tk->tkr_mono.base + tk->offs_real;
- update_vsyscall(tk);
- update_pvclock_gtod(tk, action & TK_CLOCK_WAS_SET);
+ if (tk->id == TIMEKEEPER_CORE) {
+ update_vsyscall(tk);
+ update_pvclock_gtod(tk, action & TK_CLOCK_WAS_SET);
- tk->tkr_mono.base_real = tk->tkr_mono.base + tk->offs_real;
- update_fast_timekeeper(&tk->tkr_mono, &tk_fast_mono);
- update_fast_timekeeper(&tk->tkr_raw, &tk_fast_raw);
+ update_fast_timekeeper(&tk->tkr_mono, &tk_fast_mono);
+ update_fast_timekeeper(&tk->tkr_raw, &tk_fast_raw);
+ }
if (action & TK_CLOCK_WAS_SET)
tk->clock_was_set_seq++;
^ permalink raw reply [flat|nested] 32+ messages in thread* [patch 11/26] timekeeping: Add clock_valid flag to timekeeper
2025-05-13 15:12 [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Thomas Gleixner
` (9 preceding siblings ...)
2025-05-13 15:13 ` [patch 10/26] timekeeping: Prepare timekeeping_update_from_shadow() Thomas Gleixner
@ 2025-05-13 15:13 ` Thomas Gleixner
2025-05-13 15:13 ` [patch 12/26] timekeeping: Introduce PTP time keepers Thomas Gleixner
` (15 subsequent siblings)
26 siblings, 0 replies; 32+ messages in thread
From: Thomas Gleixner @ 2025-05-13 15:13 UTC (permalink / raw)
To: LKML
Cc: netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen,
Miroslav Lichvar, Werner Abt, David Woodhouse, Stephen Boyd,
Thomas Weißschuh, Kurt Kanzenbach, Nam Cao, Alex Gieringer
From: Thomas Gleixner <tglx@linutronix.de>
In preparation for supporting independent PTP clock timekeepers, add a
clock valid field and set it to true for the system timekeeper.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
include/linux/timekeeper_internal.h | 2 ++
kernel/time/timekeeping.c | 5 +++--
2 files changed, 5 insertions(+), 2 deletions(-)
---
--- a/include/linux/timekeeper_internal.h
+++ b/include/linux/timekeeper_internal.h
@@ -73,6 +73,7 @@ struct tk_read_base {
* @raw_sec: CLOCK_MONOTONIC_RAW time in seconds
* @clock_was_set_seq: The sequence number of clock was set events
* @cs_was_changed_seq: The sequence number of clocksource change events
+ * @clock_valid: Indicator for valid clock
* @monotonic_to_boot: CLOCK_MONOTONIC to CLOCK_BOOTTIME offset
* @cycle_interval: Number of clock cycles in one NTP interval
* @xtime_interval: Number of clock shifted nano seconds in one NTP
@@ -149,6 +150,7 @@ struct timekeeper {
/* Cachline 3 and 4 (timekeeping internal variables): */
unsigned int clock_was_set_seq;
u8 cs_was_changed_seq;
+ u8 clock_valid;
struct timespec64 monotonic_to_boot;
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -1660,11 +1660,12 @@ read_persistent_wall_and_boot_offset(str
*boot_offset = ns_to_timespec64(local_clock());
}
-static __init void tkd_basic_setup(struct tk_data *tkd, enum timekeeper_ids tk_id)
+static __init void tkd_basic_setup(struct tk_data *tkd, enum timekeeper_ids tk_id, bool valid)
{
raw_spin_lock_init(&tkd->lock);
seqcount_raw_spinlock_init(&tkd->seq, &tkd->lock);
tkd->timekeeper.id = tkd->shadow_timekeeper.id = tk_id;
+ tkd->timekeeper.clock_valid = tkd->shadow_timekeeper.clock_valid = valid;
}
/*
@@ -1694,7 +1695,7 @@ void __init timekeeping_init(void)
struct timekeeper *tks = &tk_core.shadow_timekeeper;
struct clocksource *clock;
- tkd_basic_setup(&tk_core, TIMEKEEPER_CORE);
+ tkd_basic_setup(&tk_core, TIMEKEEPER_CORE, true);
read_persistent_wall_and_boot_offset(&wall_time, &boot_offset);
if (timespec64_valid_settod(&wall_time) &&
^ permalink raw reply [flat|nested] 32+ messages in thread* [patch 12/26] timekeeping: Introduce PTP time keepers
2025-05-13 15:12 [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Thomas Gleixner
` (10 preceding siblings ...)
2025-05-13 15:13 ` [patch 11/26] timekeeping: Add clock_valid flag to timekeeper Thomas Gleixner
@ 2025-05-13 15:13 ` Thomas Gleixner
2025-05-13 15:13 ` [patch 13/26] timekeeping: Provide ktime_get_ntp_seconds() Thomas Gleixner
` (14 subsequent siblings)
26 siblings, 0 replies; 32+ messages in thread
From: Thomas Gleixner @ 2025-05-13 15:13 UTC (permalink / raw)
To: LKML
Cc: netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen,
Miroslav Lichvar, Werner Abt, David Woodhouse, Stephen Boyd,
Thomas Weißschuh, Kurt Kanzenbach, Nam Cao, Alex Gieringer
From: Anna-Maria Behnsen <anna-maria@linutronix.de>
Provide time keepers for independent PTP clocks and initialize them during
boot.
Signed-off-by: Anna-Maria Behnsen <anna-maria@linutronix.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
kernel/time/timekeeping.c | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
---
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -53,7 +53,11 @@ struct tk_data {
raw_spinlock_t lock;
} ____cacheline_aligned;
-static struct tk_data tk_core;
+static struct tk_data timekeeper_data[TIMEKEEPERS_MAX];
+
+/* The core timekeeper */
+#define tk_core (timekeeper_data[TIMEKEEPER_CORE])
+
/* flag for if timekeeping is suspended */
int __read_mostly timekeeping_suspended;
@@ -113,6 +117,12 @@ static struct tk_fast tk_fast_raw ____c
.base[1] = FAST_TK_INIT,
};
+#ifdef CONFIG_POSIX_PTP_CLOCKS
+static __init void tk_ptp_setup(void);
+#else
+static inline void tk_ptp_setup(void) { }
+#endif
+
unsigned long timekeeper_lock_irqsave(void)
{
unsigned long flags;
@@ -1584,7 +1594,6 @@ void ktime_get_raw_ts64(struct timespec6
}
EXPORT_SYMBOL(ktime_get_raw_ts64);
-
/**
* timekeeping_valid_for_hres - Check if timekeeping is suitable for hres
*/
@@ -1696,6 +1705,7 @@ void __init timekeeping_init(void)
struct clocksource *clock;
tkd_basic_setup(&tk_core, TIMEKEEPER_CORE, true);
+ tk_ptp_setup();
read_persistent_wall_and_boot_offset(&wall_time, &boot_offset);
if (timespec64_valid_settod(&wall_time) &&
@@ -2625,3 +2635,11 @@ void hardpps(const struct timespec64 *ph
}
EXPORT_SYMBOL(hardpps);
#endif /* CONFIG_NTP_PPS */
+
+#ifdef CONFIG_POSIX_PTP_CLOCKS
+static __init void tk_ptp_setup(void)
+{
+ for (int i = TIMEKEEPER_PTP; i <= TIMEKEEPER_PTP_LAST; i++)
+ tkd_basic_setup(&timekeeper_data[i], i, false);
+}
+#endif /* CONFIG_POSIX_PTP_CLOCKS */
^ permalink raw reply [flat|nested] 32+ messages in thread* [patch 13/26] timekeeping: Provide ktime_get_ntp_seconds()
2025-05-13 15:12 [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Thomas Gleixner
` (11 preceding siblings ...)
2025-05-13 15:13 ` [patch 12/26] timekeeping: Introduce PTP time keepers Thomas Gleixner
@ 2025-05-13 15:13 ` Thomas Gleixner
2025-05-13 15:13 ` [patch 14/26] ntp: Use ktime_get_ntp_seconds() Thomas Gleixner
` (13 subsequent siblings)
26 siblings, 0 replies; 32+ messages in thread
From: Thomas Gleixner @ 2025-05-13 15:13 UTC (permalink / raw)
To: LKML
Cc: netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen,
Miroslav Lichvar, Werner Abt, David Woodhouse, Stephen Boyd,
Thomas Weißschuh, Kurt Kanzenbach, Nam Cao, Alex Gieringer
ntp_adjtimex() requires access to the actual time keeper per timekeeper
ID. Provide an interface.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
kernel/time/timekeeping.c | 9 +++++++++
kernel/time/timekeeping_internal.h | 3 +++
2 files changed, 12 insertions(+)
---
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -2622,6 +2622,15 @@ int do_adjtimex(struct __kernel_timex *t
return ret;
}
+/*
+ * Invoked from NTP with the time keeper lock held, so lockless access is
+ * fine.
+ */
+long ktime_get_ntp_seconds(unsigned int id)
+{
+ return timekeeper_data[id].timekeeper.xtime_sec;
+}
+
#ifdef CONFIG_NTP_PPS
/**
* hardpps() - Accessor function to NTP __hardpps function
--- a/kernel/time/timekeeping_internal.h
+++ b/kernel/time/timekeeping_internal.h
@@ -45,4 +45,7 @@ static inline u64 clocksource_delta(u64
unsigned long timekeeper_lock_irqsave(void);
void timekeeper_unlock_irqrestore(unsigned long flags);
+/* NTP specific interface to access the current seconds value */
+long ktime_get_ntp_seconds(unsigned int id);
+
#endif /* _TIMEKEEPING_INTERNAL_H */
^ permalink raw reply [flat|nested] 32+ messages in thread* [patch 14/26] ntp: Use ktime_get_ntp_seconds()
2025-05-13 15:12 [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Thomas Gleixner
` (12 preceding siblings ...)
2025-05-13 15:13 ` [patch 13/26] timekeeping: Provide ktime_get_ntp_seconds() Thomas Gleixner
@ 2025-05-13 15:13 ` Thomas Gleixner
2025-05-13 15:13 ` [patch 15/26] timekeeping: Add PTP offset to timekeeper Thomas Gleixner
` (12 subsequent siblings)
26 siblings, 0 replies; 32+ messages in thread
From: Thomas Gleixner @ 2025-05-13 15:13 UTC (permalink / raw)
To: LKML
Cc: netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen,
Miroslav Lichvar, Werner Abt, David Woodhouse, Stephen Boyd,
Thomas Weißschuh, Kurt Kanzenbach, Nam Cao, Alex Gieringer
Use ktime_get_ntp_seconds() to prepare for independent PTP clocks so that
the readout becomes per timekeeper.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
kernel/time/ntp.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
---
--- a/kernel/time/ntp.c
+++ b/kernel/time/ntp.c
@@ -303,7 +303,7 @@ static void ntp_update_offset(struct ntp
* Select how the frequency is to be controlled
* and in which mode (PLL or FLL).
*/
- real_secs = __ktime_get_real_seconds();
+ real_secs = ktime_get_ntp_seconds(ntpdata - tk_ntp_data);
secs = (long)(real_secs - ntpdata->time_reftime);
if (unlikely(ntpdata->time_status & STA_FREQHOLD))
secs = 0;
@@ -710,7 +710,7 @@ static inline void process_adj_status(st
* reference time to current time.
*/
if (!(ntpdata->time_status & STA_PLL) && (txc->status & STA_PLL))
- ntpdata->time_reftime = __ktime_get_real_seconds();
+ ntpdata->time_reftime = ktime_get_ntp_seconds(ntpdata - tk_ntp_data);
/* only set allowed bits */
ntpdata->time_status &= STA_RONLY;
^ permalink raw reply [flat|nested] 32+ messages in thread* [patch 15/26] timekeeping: Add PTP offset to timekeeper
2025-05-13 15:12 [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Thomas Gleixner
` (13 preceding siblings ...)
2025-05-13 15:13 ` [patch 14/26] ntp: Use ktime_get_ntp_seconds() Thomas Gleixner
@ 2025-05-13 15:13 ` Thomas Gleixner
2025-05-13 15:13 ` [patch 16/26] timekeeping: Update PTP timekeepers on clocksource change Thomas Gleixner
` (11 subsequent siblings)
26 siblings, 0 replies; 32+ messages in thread
From: Thomas Gleixner @ 2025-05-13 15:13 UTC (permalink / raw)
To: LKML
Cc: netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen,
Miroslav Lichvar, Werner Abt, David Woodhouse, Stephen Boyd,
Thomas Weißschuh, Kurt Kanzenbach, Nam Cao, Alex Gieringer
This offset will be used in the time getters of independent PTP clocks. It
is added to the "monotonic" clock readout.
As independent PTP clocks do not utilize the offset fields of the core time
keeper, this is just an alias for offs_tai, so that the cache line layout
stays the same.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
include/linux/timekeeper_internal.h | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
---
--- a/include/linux/timekeeper_internal.h
+++ b/include/linux/timekeeper_internal.h
@@ -67,6 +67,7 @@ struct tk_read_base {
* @offs_real: Offset clock monotonic -> clock realtime
* @offs_boot: Offset clock monotonic -> clock boottime
* @offs_tai: Offset clock monotonic -> clock tai
+ * @offs_ptp: Offset clock monotonic -> clock PTP
* @coarse_nsec: The nanoseconds part for coarse time getters
* @id: The timekeeper ID
* @tkr_raw: The readout base structure for CLOCK_MONOTONIC_RAW
@@ -139,7 +140,10 @@ struct timekeeper {
struct timespec64 wall_to_monotonic;
ktime_t offs_real;
ktime_t offs_boot;
- ktime_t offs_tai;
+ union {
+ ktime_t offs_tai;
+ ktime_t offs_ptp;
+ };
u32 coarse_nsec;
enum timekeeper_ids id;
^ permalink raw reply [flat|nested] 32+ messages in thread* [patch 16/26] timekeeping: Update PTP timekeepers on clocksource change
2025-05-13 15:12 [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Thomas Gleixner
` (14 preceding siblings ...)
2025-05-13 15:13 ` [patch 15/26] timekeeping: Add PTP offset to timekeeper Thomas Gleixner
@ 2025-05-13 15:13 ` Thomas Gleixner
2025-05-13 15:13 ` [patch 17/26] timekeeping: Provide time getters for PTP clocks Thomas Gleixner
` (10 subsequent siblings)
26 siblings, 0 replies; 32+ messages in thread
From: Thomas Gleixner @ 2025-05-13 15:13 UTC (permalink / raw)
To: LKML
Cc: netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen,
Miroslav Lichvar, Werner Abt, David Woodhouse, Stephen Boyd,
Thomas Weißschuh, Kurt Kanzenbach, Nam Cao, Alex Gieringer
Propagate a system clocksource change to the PTP timekeepers.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
kernel/time/timekeeping.c | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
---
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -119,8 +119,10 @@ static struct tk_fast tk_fast_raw ____c
#ifdef CONFIG_POSIX_PTP_CLOCKS
static __init void tk_ptp_setup(void);
+static void tk_ptp_update_clocksource(void);
#else
static inline void tk_ptp_setup(void) { }
+static inline void tk_ptp_update_clocksource(void) { }
#endif
unsigned long timekeeper_lock_irqsave(void)
@@ -1548,6 +1550,8 @@ static int change_clocksource(void *data
timekeeping_update_from_shadow(&tk_core, TK_UPDATE_ALL);
}
+ tk_ptp_update_clocksource();
+
if (old) {
if (old->disable)
old->disable(old);
@@ -2653,6 +2657,30 @@ EXPORT_SYMBOL(hardpps);
#endif /* CONFIG_NTP_PPS */
#ifdef CONFIG_POSIX_PTP_CLOCKS
+
+/* Bitmap for the activated PTP timekeepers */
+static unsigned long ptp_timekeepers;
+
+/* Invoked from timekeeping after a clocksource change */
+static void tk_ptp_update_clocksource(void)
+{
+ unsigned long active = READ_ONCE(ptp_timekeepers);
+ unsigned int id;
+
+ for_each_set_bit(id, &active, BITS_PER_LONG) {
+ struct tk_data *tkd = &timekeeper_data[id + TIMEKEEPER_PTP];
+ struct timekeeper *tks = &tkd->shadow_timekeeper;
+
+ guard(raw_spinlock_irqsave)(&tkd->lock);
+ if (!tks->clock_valid)
+ continue;
+
+ timekeeping_forward_now(tks);
+ tk_setup_internals(tks, tk_core.timekeeper.tkr_mono.clock);
+ timekeeping_update_from_shadow(tkd, TK_UPDATE_ALL);
+ }
+}
+
static __init void tk_ptp_setup(void)
{
for (int i = TIMEKEEPER_PTP; i <= TIMEKEEPER_PTP_LAST; i++)
^ permalink raw reply [flat|nested] 32+ messages in thread* [patch 17/26] timekeeping: Provide time getters for PTP clocks
2025-05-13 15:12 [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Thomas Gleixner
` (15 preceding siblings ...)
2025-05-13 15:13 ` [patch 16/26] timekeeping: Update PTP timekeepers on clocksource change Thomas Gleixner
@ 2025-05-13 15:13 ` Thomas Gleixner
2025-05-13 15:13 ` [patch 18/26] timekeeping: Add minimal posix-timers support " Thomas Gleixner
` (9 subsequent siblings)
26 siblings, 0 replies; 32+ messages in thread
From: Thomas Gleixner @ 2025-05-13 15:13 UTC (permalink / raw)
To: LKML
Cc: netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen,
Miroslav Lichvar, Werner Abt, David Woodhouse, Stephen Boyd,
Thomas Weißschuh, Kurt Kanzenbach, Nam Cao, Alex Gieringer
Provide interfaces similar to the ktime_get*() family which provide access
to the independent PTP clocks.
These interfaces have a boolean return value, which indicates whether the
accessed clock is valid or not.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
include/linux/timekeeping.h | 17 ++++++++++++
kernel/time/timekeeping.c | 62 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 79 insertions(+)
---
--- a/include/linux/timekeeping.h
+++ b/include/linux/timekeeping.h
@@ -60,6 +60,17 @@ extern time64_t __ktime_get_real_seconds
extern time64_t ktime_get_real_seconds(void);
/*
+ * PTP clock interfaces
+ */
+#ifdef CONFIG_POSIX_PTP_CLOCKS
+extern bool ktime_get_ptp(clockid_t id, ktime_t *kt);
+extern bool ktime_get_ptp_ts64(clockid_t id, struct timespec64 *kt);
+#else
+static inline bool ktime_get_ptp(clockid_t id, ktime_t *kt) { return false; }
+static inline bool ktime_get_ptp_ts64(clockid_t id, struct timespec64 *kt) { return false; }
+#endif
+
+/*
* ktime_t based interfaces
*/
@@ -263,6 +274,12 @@ extern bool timekeeping_rtc_skipresume(v
extern void timekeeping_inject_sleeptime64(const struct timespec64 *delta);
+/*
+ * PTP clocks
+ */
+bool ktime_get_ptp(clockid_t ptp_clock_id, ktime_t *ts);
+bool ktime_get_ptp_ts64(clockid_t ptp_clock_id, struct timespec64 *ts);
+
/**
* struct system_time_snapshot - simultaneous raw/real time capture with
* counter value
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -2661,6 +2661,23 @@ EXPORT_SYMBOL(hardpps);
/* Bitmap for the activated PTP timekeepers */
static unsigned long ptp_timekeepers;
+static inline bool ptp_valid_clockid(clockid_t id)
+{
+ return id >= CLOCK_PTP && id <= CLOCK_PTP_LAST;
+}
+
+static inline unsigned int clockid_to_tkid(unsigned int id)
+{
+ return TIMEKEEPER_PTP + id - CLOCK_PTP;
+}
+
+static inline struct tk_data *ptp_get_tk_data(clockid_t id)
+{
+ if (!ptp_valid_clockid(id))
+ return NULL;
+ return &timekeeper_data[clockid_to_tkid(id)];
+}
+
/* Invoked from timekeeping after a clocksource change */
static void tk_ptp_update_clocksource(void)
{
@@ -2681,6 +2698,51 @@ static void tk_ptp_update_clocksource(vo
}
}
+/**
+ * ktime_get_ptp - Get TAI time for a PTP clock
+ * @id: ID of the clock to read (CLOCK_PTP...)
+ * @kt: Pointer to ktime_t to store the time stamp
+ *
+ * Returns: True if the timestamp is valid, false otherwise
+ */
+bool ktime_get_ptp(clockid_t id, ktime_t *kt)
+{
+ struct tk_data *tkd = ptp_get_tk_data(id);
+ struct timekeeper *tk;
+ unsigned int seq;
+ ktime_t base;
+ u64 nsecs;
+
+ WARN_ON(timekeeping_suspended);
+
+ if (!tkd)
+ return false;
+
+ tk = &tkd->timekeeper;
+ do {
+ seq = read_seqcount_begin(&tkd->seq);
+ if (!tk->clock_valid)
+ return false;
+
+ base = ktime_add(tk->tkr_mono.base, tk->offs_ptp);
+ nsecs = timekeeping_get_ns(&tk->tkr_mono);
+ } while (read_seqcount_retry(&tkd->seq, seq));
+
+ *kt = ktime_add_ns(base, nsecs);
+ return true;
+}
+EXPORT_SYMBOL_GPL(ktime_get_ptp);
+
+bool ktime_get_ptp_ts64(clockid_t id, struct timespec64 *ts)
+{
+ ktime_t now;
+
+ if (!ktime_get_ptp(id, &now))
+ return false;
+ *ts = ktime_to_timespec64(now);
+ return true;
+}
+
static __init void tk_ptp_setup(void)
{
for (int i = TIMEKEEPER_PTP; i <= TIMEKEEPER_PTP_LAST; i++)
^ permalink raw reply [flat|nested] 32+ messages in thread* [patch 18/26] timekeeping: Add minimal posix-timers support for PTP clocks
2025-05-13 15:12 [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Thomas Gleixner
` (16 preceding siblings ...)
2025-05-13 15:13 ` [patch 17/26] timekeeping: Provide time getters for PTP clocks Thomas Gleixner
@ 2025-05-13 15:13 ` Thomas Gleixner
2025-05-13 15:13 ` [patch 19/26] timekeeping: Provide time setter " Thomas Gleixner
` (8 subsequent siblings)
26 siblings, 0 replies; 32+ messages in thread
From: Thomas Gleixner @ 2025-05-13 15:13 UTC (permalink / raw)
To: LKML
Cc: netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen,
Miroslav Lichvar, Werner Abt, David Woodhouse, Stephen Boyd,
Thomas Weißschuh, Kurt Kanzenbach, Nam Cao, Alex Gieringer
Provide clock_getres(2) and clock_gettime(2) for PTP clocks.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
kernel/time/posix-timers.c | 3 +++
kernel/time/posix-timers.h | 1 +
kernel/time/timekeeping.c | 21 +++++++++++++++++++++
3 files changed, 25 insertions(+)
---
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -1526,6 +1526,9 @@ static const struct k_clock * const posi
[CLOCK_REALTIME_ALARM] = &alarm_clock,
[CLOCK_BOOTTIME_ALARM] = &alarm_clock,
[CLOCK_TAI] = &clock_tai,
+#ifdef CONFIG_POSIX_PTP_CLOCKS
+ [CLOCK_PTP ... CLOCK_PTP_LAST] = &clock_ptp,
+#endif
};
static const struct k_clock *clockid_to_kclock(const clockid_t id)
--- a/kernel/time/posix-timers.h
+++ b/kernel/time/posix-timers.h
@@ -41,6 +41,7 @@ extern const struct k_clock clock_posix_
extern const struct k_clock clock_process;
extern const struct k_clock clock_thread;
extern const struct k_clock alarm_clock;
+extern const struct k_clock clock_ptp;
void posix_timer_queue_signal(struct k_itimer *timr);
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -2655,6 +2655,7 @@ EXPORT_SYMBOL(hardpps);
#endif /* CONFIG_NTP_PPS */
#ifdef CONFIG_POSIX_PTP_CLOCKS
+#include "posix-timers.h"
/* Bitmap for the activated PTP timekeepers */
static unsigned long ptp_timekeepers;
@@ -2741,6 +2742,26 @@ bool ktime_get_ptp_ts64(clockid_t id, st
return true;
}
+static int ptp_get_res(clockid_t id, struct timespec64 *tp)
+{
+ if (!ptp_valid_clockid(id))
+ return -ENODEV;
+
+ tp->tv_sec = 0;
+ tp->tv_nsec = 1;
+ return 0;
+}
+
+static int ptp_get_timespec(clockid_t id, struct timespec64 *tp)
+{
+ return ktime_get_ptp_ts64(id, tp) ? 0 : -ENODEV;
+}
+
+const struct k_clock clock_ptp = {
+ .clock_getres = ptp_get_res,
+ .clock_get_timespec = ptp_get_timespec,
+};
+
static __init void tk_ptp_setup(void)
{
for (int i = TIMEKEEPER_PTP; i <= TIMEKEEPER_PTP_LAST; i++)
^ permalink raw reply [flat|nested] 32+ messages in thread* [patch 19/26] timekeeping: Provide time setter for PTP clocks
2025-05-13 15:12 [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Thomas Gleixner
` (17 preceding siblings ...)
2025-05-13 15:13 ` [patch 18/26] timekeeping: Add minimal posix-timers support " Thomas Gleixner
@ 2025-05-13 15:13 ` Thomas Gleixner
2025-05-13 15:13 ` [patch 20/26] timekeeping: Make timekeeping_inject_offset() reusable Thomas Gleixner
` (7 subsequent siblings)
26 siblings, 0 replies; 32+ messages in thread
From: Thomas Gleixner @ 2025-05-13 15:13 UTC (permalink / raw)
To: LKML
Cc: netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen,
Miroslav Lichvar, Werner Abt, David Woodhouse, Stephen Boyd,
Thomas Weißschuh, Kurt Kanzenbach, Nam Cao, Alex Gieringer
Add clocK_settime(2) support for PTP clocks. The function affects the PTP
offset which is added to the "monotonic" clock readout.
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
@@ -2759,9 +2759,48 @@ static int ptp_get_timespec(clockid_t id
return ktime_get_ptp_ts64(id, tp) ? 0 : -ENODEV;
}
+static int ptp_clock_set(const clockid_t id, const struct timespec64 *tnew)
+{
+ struct tk_data *tkd = ptp_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 PTP offset */
+ tks->offs_ptp = ktime_sub(timespec64_to_ktime(*tnew), tnow);
+
+ timekeeping_update_from_shadow(tkd, TK_UPDATE_ALL);
+ return 0;
+}
+
const struct k_clock clock_ptp = {
.clock_getres = ptp_get_res,
.clock_get_timespec = ptp_get_timespec,
+ .clock_set = ptp_clock_set,
};
static __init void tk_ptp_setup(void)
^ permalink raw reply [flat|nested] 32+ messages in thread* [patch 20/26] timekeeping: Make timekeeping_inject_offset() reusable
2025-05-13 15:12 [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Thomas Gleixner
` (18 preceding siblings ...)
2025-05-13 15:13 ` [patch 19/26] timekeeping: Provide time setter " Thomas Gleixner
@ 2025-05-13 15:13 ` Thomas Gleixner
2025-05-13 15:13 ` [patch 21/26] timekeeping: Add PTP clock support to __timekeeping_inject_offset() Thomas Gleixner
` (6 subsequent siblings)
26 siblings, 0 replies; 32+ messages in thread
From: Thomas Gleixner @ 2025-05-13 15:13 UTC (permalink / raw)
To: LKML
Cc: netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen,
Miroslav Lichvar, Werner Abt, David Woodhouse, Stephen Boyd,
Thomas Weißschuh, Kurt Kanzenbach, Nam Cao, Alex Gieringer
Split out the inner workings for PTP clock support and feed the core time
keeper into it.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
kernel/time/timekeeping.c | 26 +++++++++++++++-----------
1 file changed, 15 insertions(+), 11 deletions(-)
---
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -1433,32 +1433,32 @@ EXPORT_SYMBOL(do_settimeofday64);
/**
* __timekeeping_inject_offset - Adds or subtracts from the current time.
+ * @tkd: Pointer to the timekeeper to modify
* @ts: Pointer to the timespec variable containing the offset
*
* Adds or subtracts an offset value from the current time.
*/
-static int __timekeeping_inject_offset(const struct timespec64 *ts)
+static int __timekeeping_inject_offset(struct tk_data *tkd, const struct timespec64 *ts)
{
- struct timekeeper *tks = &tk_core.shadow_timekeeper;
+ struct timekeeper *tks = &tkd->shadow_timekeeper;
struct timespec64 tmp;
if (ts->tv_nsec < 0 || ts->tv_nsec >= NSEC_PER_SEC)
return -EINVAL;
-
timekeeping_forward_now(tks);
/* Make sure the proposed value is valid */
tmp = timespec64_add(tk_xtime(tks), *ts);
if (timespec64_compare(&tks->wall_to_monotonic, ts) > 0 ||
!timespec64_valid_settod(&tmp)) {
- timekeeping_restore_shadow(&tk_core);
+ timekeeping_restore_shadow(tkd);
return -EINVAL;
}
tk_xtime_add(tks, ts);
tk_set_wall_to_mono(tks, timespec64_sub(tks->wall_to_monotonic, *ts));
- timekeeping_update_from_shadow(&tk_core, TK_UPDATE_ALL);
+ timekeeping_update_from_shadow(tkd, TK_UPDATE_ALL);
return 0;
}
@@ -1467,7 +1467,7 @@ static int timekeeping_inject_offset(con
int ret;
scoped_guard (raw_spinlock_irqsave, &tk_core.lock)
- ret = __timekeeping_inject_offset(ts);
+ ret = __timekeeping_inject_offset(&tk_core, ts);
/* Signal hrtimers about time change */
if (!ret)
@@ -2568,6 +2568,7 @@ EXPORT_SYMBOL_GPL(random_get_entropy_fal
*/
int do_adjtimex(struct __kernel_timex *txc)
{
+ struct tk_data *tkd = &tk_core;
struct timespec64 delta, ts;
struct audit_ntp_data ad;
bool offset_set = false;
@@ -2585,16 +2586,19 @@ int do_adjtimex(struct __kernel_timex *t
ktime_get_real_ts64(&ts);
add_device_randomness(&ts, sizeof(ts));
- scoped_guard (raw_spinlock_irqsave, &tk_core.lock) {
- struct timekeeper *tks = &tk_core.shadow_timekeeper;
+ scoped_guard (raw_spinlock_irqsave, &tkd->lock) {
+ struct timekeeper *tks = &tkd->shadow_timekeeper;
s32 orig_tai, tai;
+ if (!tks->clock_valid)
+ return -ENODEV;
+
if (txc->modes & ADJ_SETOFFSET) {
delta.tv_sec = txc->time.tv_sec;
delta.tv_nsec = txc->time.tv_usec;
if (!(txc->modes & ADJ_NANO))
delta.tv_nsec *= 1000;
- ret = __timekeeping_inject_offset(&delta);
+ ret = __timekeeping_inject_offset(tkd, &delta);
if (ret)
return ret;
@@ -2607,7 +2611,7 @@ int do_adjtimex(struct __kernel_timex *t
if (tai != orig_tai) {
__timekeeping_set_tai_offset(tks, tai);
- timekeeping_update_from_shadow(&tk_core, TK_CLOCK_WAS_SET);
+ timekeeping_update_from_shadow(tkd, TK_CLOCK_WAS_SET);
clock_set = true;
} else {
tk_update_leap_state_all(&tk_core);
@@ -2615,7 +2619,7 @@ int do_adjtimex(struct __kernel_timex *t
/* Update the multiplier immediately if frequency was set directly */
if (txc->modes & (ADJ_FREQUENCY | ADJ_TICK))
- clock_set |= __timekeeping_advance(&tk_core, TK_ADV_FREQ);
+ clock_set |= __timekeeping_advance(tkd, TK_ADV_FREQ);
}
if (txc->modes & ADJ_SETOFFSET)
^ permalink raw reply [flat|nested] 32+ messages in thread* [patch 21/26] timekeeping: Add PTP clock support to __timekeeping_inject_offset()
2025-05-13 15:12 [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Thomas Gleixner
` (19 preceding siblings ...)
2025-05-13 15:13 ` [patch 20/26] timekeeping: Make timekeeping_inject_offset() reusable Thomas Gleixner
@ 2025-05-13 15:13 ` Thomas Gleixner
2025-05-13 15:13 ` [patch 22/26] timekeeping: Make do_adjtimex() reusable Thomas Gleixner
` (5 subsequent siblings)
26 siblings, 0 replies; 32+ messages in thread
From: Thomas Gleixner @ 2025-05-13 15:13 UTC (permalink / raw)
To: LKML
Cc: netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen,
Miroslav Lichvar, Werner Abt, David Woodhouse, Stephen Boyd,
Thomas Weißschuh, Kurt Kanzenbach, Nam Cao, Alex Gieringer
Redirect the relative offset adjustment to the PTP clock offset instead of
modifying CLOCK_REALTIME, which has no meaning in context of these clocks.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
kernel/time/timekeeping.c | 34 ++++++++++++++++++++++++++--------
1 file changed, 26 insertions(+), 8 deletions(-)
---
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -1448,16 +1448,34 @@ static int __timekeeping_inject_offset(s
timekeeping_forward_now(tks);
- /* Make sure the proposed value is valid */
- tmp = timespec64_add(tk_xtime(tks), *ts);
- if (timespec64_compare(&tks->wall_to_monotonic, ts) > 0 ||
- !timespec64_valid_settod(&tmp)) {
- timekeeping_restore_shadow(tkd);
- return -EINVAL;
+ if (!IS_ENABLED(CONFIG_POSIX_PTP_CLOCKS) || tks->id == TIMEKEEPER_CORE) {
+ /* Make sure the proposed value is valid */
+ tmp = timespec64_add(tk_xtime(tks), *ts);
+ if (timespec64_compare(&tks->wall_to_monotonic, ts) > 0 ||
+ !timespec64_valid_settod(&tmp)) {
+ timekeeping_restore_shadow(tkd);
+ return -EINVAL;
+ }
+
+ tk_xtime_add(tks, ts);
+ tk_set_wall_to_mono(tks, timespec64_sub(tks->wall_to_monotonic, *ts));
+ } else {
+ struct tk_read_base *tkr_mono = &tks->tkr_mono;
+ ktime_t now, offs;
+
+ /* Get the current time */
+ now = ktime_add_ns(tkr_mono->base, timekeeping_get_ns(tkr_mono));
+ /* Add the relative offset change */
+ offs = ktime_add(tks->offs_ptp, timespec64_to_ktime(*ts));
+
+ /* Prevent that the resulting time becomes negative */
+ if (ktime_add(now, offs) < 0) {
+ timekeeping_restore_shadow(tkd);
+ return -EINVAL;
+ }
+ tks->offs_ptp = offs;
}
- tk_xtime_add(tks, ts);
- tk_set_wall_to_mono(tks, timespec64_sub(tks->wall_to_monotonic, *ts));
timekeeping_update_from_shadow(tkd, TK_UPDATE_ALL);
return 0;
}
^ permalink raw reply [flat|nested] 32+ messages in thread* [patch 22/26] timekeeping: Make do_adjtimex() reusable
2025-05-13 15:12 [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Thomas Gleixner
` (20 preceding siblings ...)
2025-05-13 15:13 ` [patch 21/26] timekeeping: Add PTP clock support to __timekeeping_inject_offset() Thomas Gleixner
@ 2025-05-13 15:13 ` Thomas Gleixner
2025-05-13 15:13 ` [patch 23/26] timekeeping: Prepare do_adtimex() for PTP clocks Thomas Gleixner
` (4 subsequent siblings)
26 siblings, 0 replies; 32+ messages in thread
From: Thomas Gleixner @ 2025-05-13 15:13 UTC (permalink / raw)
To: LKML
Cc: netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen,
Miroslav Lichvar, Werner Abt, David Woodhouse, Stephen Boyd,
Thomas Weißschuh, Kurt Kanzenbach, Nam Cao, Alex Gieringer
Split out the actual functionality of adjtimex() and make do_adjtimex() a
wrapper which feeds the core timekeeper into it and handles the result
including audit at the call site.
This allows to reuse the actual functionality for independent PTP clocks.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
kernel/time/timekeeping.c | 110 +++++++++++++++++++++++++---------------------
1 file changed, 60 insertions(+), 50 deletions(-)
---
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -2580,17 +2580,18 @@ unsigned long random_get_entropy_fallbac
}
EXPORT_SYMBOL_GPL(random_get_entropy_fallback);
-/**
- * do_adjtimex() - Accessor function to NTP __do_adjtimex function
- * @txc: Pointer to kernel_timex structure containing NTP parameters
- */
-int do_adjtimex(struct __kernel_timex *txc)
+struct adjtimex_result {
+ struct audit_ntp_data ad;
+ struct timespec64 delta;
+ bool clock_set;
+};
+
+static int __do_adjtimex(struct tk_data *tkd, struct __kernel_timex *txc,
+ struct adjtimex_result *result)
{
- struct tk_data *tkd = &tk_core;
- struct timespec64 delta, ts;
- struct audit_ntp_data ad;
- bool offset_set = false;
- bool clock_set = false;
+ struct timekeeper *tks = &tkd->shadow_timekeeper;
+ struct timespec64 ts;
+ s32 orig_tai, tai;
int ret;
/* Validate the data before disabling interrupts */
@@ -2599,56 +2600,65 @@ int do_adjtimex(struct __kernel_timex *t
return ret;
add_device_randomness(txc, sizeof(*txc));
- audit_ntp_init(&ad);
-
ktime_get_real_ts64(&ts);
add_device_randomness(&ts, sizeof(ts));
- scoped_guard (raw_spinlock_irqsave, &tkd->lock) {
- struct timekeeper *tks = &tkd->shadow_timekeeper;
- s32 orig_tai, tai;
-
- if (!tks->clock_valid)
- return -ENODEV;
-
- if (txc->modes & ADJ_SETOFFSET) {
- delta.tv_sec = txc->time.tv_sec;
- delta.tv_nsec = txc->time.tv_usec;
- if (!(txc->modes & ADJ_NANO))
- delta.tv_nsec *= 1000;
- ret = __timekeeping_inject_offset(tkd, &delta);
- if (ret)
- return ret;
-
- offset_set = delta.tv_sec != 0;
- clock_set = true;
- }
-
- orig_tai = tai = tks->tai_offset;
- ret = ntp_adjtimex(tks->id, txc, &ts, &tai, &ad);
-
- if (tai != orig_tai) {
- __timekeeping_set_tai_offset(tks, tai);
- timekeeping_update_from_shadow(tkd, TK_CLOCK_WAS_SET);
- clock_set = true;
- } else {
- tk_update_leap_state_all(&tk_core);
- }
-
- /* Update the multiplier immediately if frequency was set directly */
- if (txc->modes & (ADJ_FREQUENCY | ADJ_TICK))
- clock_set |= __timekeeping_advance(tkd, TK_ADV_FREQ);
+ guard(raw_spinlock_irqsave)(&tkd->lock);
+
+ if (!tks->clock_valid)
+ return -ENODEV;
+
+ if (txc->modes & ADJ_SETOFFSET) {
+ result->delta.tv_sec = txc->time.tv_sec;
+ result->delta.tv_nsec = txc->time.tv_usec;
+ if (!(txc->modes & ADJ_NANO))
+ result->delta.tv_nsec *= 1000;
+ ret = __timekeeping_inject_offset(tkd, &result->delta);
+ if (ret)
+ return ret;
+ result->clock_set = true;
+ }
+
+ orig_tai = tai = tks->tai_offset;
+ ret = ntp_adjtimex(tks->id, txc, &ts, &tai, &result->ad);
+
+ if (tai != orig_tai) {
+ __timekeeping_set_tai_offset(tks, tai);
+ timekeeping_update_from_shadow(tkd, TK_CLOCK_WAS_SET);
+ result->clock_set = true;
+ } else {
+ tk_update_leap_state_all(&tk_core);
}
+ /* Update the multiplier immediately if frequency was set directly */
+ if (txc->modes & (ADJ_FREQUENCY | ADJ_TICK))
+ result->clock_set |= __timekeeping_advance(tkd, TK_ADV_FREQ);
+
+ return ret;
+}
+
+/**
+ * do_adjtimex() - Accessor function to NTP __do_adjtimex function
+ * @txc: Pointer to kernel_timex structure containing NTP parameters
+ */
+int do_adjtimex(struct __kernel_timex *txc)
+{
+ struct adjtimex_result result = { };
+ int ret;
+
+ ret = __do_adjtimex(&tk_core, txc, &result);
+ if (ret < 0)
+ return ret;
+
if (txc->modes & ADJ_SETOFFSET)
- audit_tk_injoffset(delta);
+ audit_tk_injoffset(result.delta);
- audit_ntp_log(&ad);
+ audit_ntp_log(&result.ad);
- if (clock_set)
+ if (result.clock_set)
clock_was_set(CLOCK_SET_WALL);
- ntp_notify_cmos_timer(offset_set);
+ ntp_notify_cmos_timer(result.delta.tv_sec != 0);
return ret;
}
^ permalink raw reply [flat|nested] 32+ messages in thread* [patch 23/26] timekeeping: Prepare do_adtimex() for PTP clocks
2025-05-13 15:12 [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Thomas Gleixner
` (21 preceding siblings ...)
2025-05-13 15:13 ` [patch 22/26] timekeeping: Make do_adjtimex() reusable Thomas Gleixner
@ 2025-05-13 15:13 ` Thomas Gleixner
2025-05-13 15:13 ` [patch 24/26] timekeeping: Provide adjtimex() " Thomas Gleixner
` (3 subsequent siblings)
26 siblings, 0 replies; 32+ messages in thread
From: Thomas Gleixner @ 2025-05-13 15:13 UTC (permalink / raw)
To: LKML
Cc: netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen,
Miroslav Lichvar, Werner Abt, David Woodhouse, Stephen Boyd,
Thomas Weißschuh, Kurt Kanzenbach, Nam Cao, Alex Gieringer
Exclude ADJ_TAI, leap seconds and PPS functionality and provide a time
stamp based on the actual clock.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
kernel/time/timekeeping.c | 39 ++++++++++++++++++++++++++++++++++++---
1 file changed, 36 insertions(+), 3 deletions(-)
---
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -58,6 +58,17 @@ static struct tk_data timekeeper_data[TI
/* The core timekeeper */
#define tk_core (timekeeper_data[TIMEKEEPER_CORE])
+#ifdef CONFIG_POSIX_PTP_CLOCKS
+static inline bool tk_get_ptp_ts64(unsigned int tkid, struct timespec64 *ts)
+{
+ return ktime_get_ptp_ts64(CLOCK_PTP + tkid - TIMEKEEPER_PTP, ts);
+}
+#else
+static inline bool tk_get_ptp_ts64(unsigned int tkid, struct timespec64 *ts)
+{
+ return false;
+}
+#endif
/* flag for if timekeeping is suspended */
int __read_mostly timekeeping_suspended;
@@ -2503,7 +2514,7 @@ ktime_t ktime_get_update_offsets_now(uns
/*
* timekeeping_validate_timex - Ensures the timex is ok for use in do_adjtimex
*/
-static int timekeeping_validate_timex(const struct __kernel_timex *txc)
+static int timekeeping_validate_timex(const struct __kernel_timex *txc, bool ptp_clock)
{
if (txc->modes & ADJ_ADJTIME) {
/* singleshot must not be used with any other mode bits */
@@ -2562,6 +2573,21 @@ static int timekeeping_validate_timex(co
return -EINVAL;
}
+ if (!ptp_clock)
+ return 0;
+
+ /* PTP clocks are TAI based and do not have leap seconds */
+ if (txc->status & (STA_INS | STA_DEL))
+ return -EINVAL;
+
+ /* No TAI offset setting */
+ if (txc->modes & ADJ_TAI)
+ return -EINVAL;
+
+ /* No PPS support either */
+ if (txc->status & (STA_PPSFREQ | STA_PPSTIME))
+ return -EINVAL;
+
return 0;
}
@@ -2592,15 +2618,22 @@ static int __do_adjtimex(struct tk_data
struct timekeeper *tks = &tkd->shadow_timekeeper;
struct timespec64 ts;
s32 orig_tai, tai;
+ bool ptp_clock;
int ret;
+ ptp_clock = IS_ENABLED(CONFIG_POSIX_PTP_CLOCKS) && tkd->timekeeper.id != TIMEKEEPER_CORE;
+
/* Validate the data before disabling interrupts */
- ret = timekeeping_validate_timex(txc);
+ ret = timekeeping_validate_timex(txc, ptp_clock);
if (ret)
return ret;
add_device_randomness(txc, sizeof(*txc));
- ktime_get_real_ts64(&ts);
+ if (!ptp_clock)
+ ktime_get_real_ts64(&ts);
+ else
+ tk_get_ptp_ts64(tkd->timekeeper.id, &ts);
+
add_device_randomness(&ts, sizeof(ts));
guard(raw_spinlock_irqsave)(&tkd->lock);
^ permalink raw reply [flat|nested] 32+ messages in thread* [patch 24/26] timekeeping: Provide adjtimex() for PTP clocks
2025-05-13 15:12 [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Thomas Gleixner
` (22 preceding siblings ...)
2025-05-13 15:13 ` [patch 23/26] timekeeping: Prepare do_adtimex() for PTP clocks Thomas Gleixner
@ 2025-05-13 15:13 ` Thomas Gleixner
2025-05-13 15:13 ` [patch 25/26] timekeeping: Provide update for PTP timekeepers Thomas Gleixner
` (2 subsequent siblings)
26 siblings, 0 replies; 32+ messages in thread
From: Thomas Gleixner @ 2025-05-13 15:13 UTC (permalink / raw)
To: LKML
Cc: netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen,
Miroslav Lichvar, Werner Abt, David Woodhouse, Stephen Boyd,
Thomas Weißschuh, Kurt Kanzenbach, Nam Cao, Alex Gieringer
The behaviour is close to clock_adtime(CLOCK_REALTIME) with the
following differences:
1) ADJ_SETOFFSET adjusts the PTP clock offset
2) ADJ_TAI is not supported
3) Leap seconds are not supported
4) PPS is not supported
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
kernel/time/timekeeping.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
---
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -2862,10 +2862,26 @@ static int ptp_clock_set(const clockid_t
return 0;
}
+static int ptp_clock_adj(const clockid_t id, struct __kernel_timex *txc)
+{
+ struct tk_data *tkd = ptp_get_tk_data(id);
+ struct adjtimex_result result = { };
+
+ if (!tkd)
+ return -ENODEV;
+
+ /*
+ * @result is ignored for now as there are neither hrtimers nor a
+ * RTC related to these PTP clocks.
+ */
+ return __do_adjtimex(tkd, txc, &result);
+}
+
const struct k_clock clock_ptp = {
.clock_getres = ptp_get_res,
.clock_get_timespec = ptp_get_timespec,
.clock_set = ptp_clock_set,
+ .clock_adj = ptp_clock_adj,
};
static __init void tk_ptp_setup(void)
^ permalink raw reply [flat|nested] 32+ messages in thread* [patch 25/26] timekeeping: Provide update for PTP timekeepers
2025-05-13 15:12 [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Thomas Gleixner
` (23 preceding siblings ...)
2025-05-13 15:13 ` [patch 24/26] timekeeping: Provide adjtimex() " Thomas Gleixner
@ 2025-05-13 15:13 ` Thomas Gleixner
2025-05-13 15:13 ` [patch 26/26] timekeeping: Provide interface to control independent PTP clocks Thomas Gleixner
2025-05-14 7:12 ` [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Miroslav Lichvar
26 siblings, 0 replies; 32+ messages in thread
From: Thomas Gleixner @ 2025-05-13 15:13 UTC (permalink / raw)
To: LKML
Cc: netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen,
Miroslav Lichvar, Werner Abt, David Woodhouse, Stephen Boyd,
Thomas Weißschuh, Kurt Kanzenbach, Nam Cao, Alex Gieringer
Update the PTP timekeepers periodically. For now this is tied to the system
timekeeper update from the tick. This might be revisited.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
kernel/time/timekeeping.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
---
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -130,9 +130,11 @@ static struct tk_fast tk_fast_raw ____c
#ifdef CONFIG_POSIX_PTP_CLOCKS
static __init void tk_ptp_setup(void);
+static void tk_ptp_advance(void);
static void tk_ptp_update_clocksource(void);
#else
static inline void tk_ptp_setup(void) { }
+static inline void tk_ptp_advance(void) { }
static inline void tk_ptp_update_clocksource(void) { }
#endif
@@ -2312,11 +2314,13 @@ static bool timekeeping_advance(enum tim
/**
* update_wall_time - Uses the current clocksource to increment the wall time
*
+ * It also updates eventually installed PTP clock timekeepers
*/
void update_wall_time(void)
{
if (timekeeping_advance(TK_ADV_TICK))
clock_was_set_delayed();
+ tk_ptp_advance();
}
/**
@@ -2762,6 +2766,20 @@ static void tk_ptp_update_clocksource(vo
}
}
+static void tk_ptp_advance(void)
+{
+ unsigned long active = READ_ONCE(ptp_timekeepers);
+ unsigned int id;
+
+ for_each_set_bit(id, &active, BITS_PER_LONG) {
+ struct tk_data *tkd = &timekeeper_data[id + TIMEKEEPER_PTP];
+
+ guard(raw_spinlock)(&tkd->lock);
+ if (tkd->shadow_timekeeper.clock_valid)
+ __timekeeping_advance(tkd, TK_ADV_TICK);
+ }
+}
+
/**
* ktime_get_ptp - Get TAI time for a PTP clock
* @id: ID of the clock to read (CLOCK_PTP...)
^ permalink raw reply [flat|nested] 32+ messages in thread* [patch 26/26] timekeeping: Provide interface to control independent PTP clocks
2025-05-13 15:12 [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Thomas Gleixner
` (24 preceding siblings ...)
2025-05-13 15:13 ` [patch 25/26] timekeeping: Provide update for PTP timekeepers Thomas Gleixner
@ 2025-05-13 15:13 ` Thomas Gleixner
2025-05-14 8:07 ` Antoine Tenart
2025-05-14 7:12 ` [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Miroslav Lichvar
26 siblings, 1 reply; 32+ messages in thread
From: Thomas Gleixner @ 2025-05-13 15:13 UTC (permalink / raw)
To: LKML
Cc: netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen,
Miroslav Lichvar, Werner Abt, David Woodhouse, Stephen Boyd,
Thomas Weißschuh, Kurt Kanzenbach, Nam Cao, Alex Gieringer
Independent PTP clocks are disabled by default and attempts to access them
fail.
Provide an interface to enable/disable them at run-time.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
Documentation/ABI/stable/sysfs-kernel-time-ptp | 6 +
kernel/time/timekeeping.c | 125 +++++++++++++++++++++++++
2 files changed, 131 insertions(+)
--- /dev/null
+++ b/Documentation/ABI/stable/sysfs-kernel-time-ptp
@@ -0,0 +1,6 @@
+What: /sys/kernel/time/ptp/<ID>/enable
+Date: May 2025
+Contact: Thomas Gleixner <tglx@linutronix.de>
+Description:
+ Controls the enablement of independent PTP clock
+ timekeepers.
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -14,6 +14,7 @@
#include <linux/sched/loadavg.h>
#include <linux/sched/clock.h>
#include <linux/syscore_ops.h>
+#include <linux/sysfs.h>
#include <linux/clocksource.h>
#include <linux/jiffies.h>
#include <linux/time.h>
@@ -2900,6 +2901,130 @@ const struct k_clock clock_ptp = {
.clock_adj = ptp_clock_adj,
};
+static void ptp_clock_enable(unsigned int id)
+{
+ struct tk_read_base *tkr_raw = &tk_core.timekeeper.tkr_raw;
+ struct tk_data *tkd = ptp_get_tk_data(id);
+ struct timekeeper *tks = &tkd->shadow_timekeeper;
+
+ /* Prevent the core timekeeper from changing. */
+ guard(raw_spinlock_irq)(&tk_core.lock);
+
+ /*
+ * Setup the PTP clock assuming that the raw core timekeeper clock
+ * frequency conversion is close enough. PTP userspace has to
+ * adjust for the deviation via clock_adjtime(2).
+ */
+ guard(raw_spinlock_nested)(&tkd->lock);
+
+ /* Remove leftovers of a previous registration */
+ memset(tks, 0, sizeof(*tks));
+ /* Restore the timekeeper id */
+ tks->id = tkd->timekeeper.id;
+ /* Setup the timekeeper based on the current system clocksource */
+ tk_setup_internals(tks, tkr_raw->clock);
+
+ /* Mark it valid and set it live */
+ tks->clock_valid = true;
+ timekeeping_update_from_shadow(tkd, TK_UPDATE_ALL);
+}
+
+static void ptp_clock_disable(unsigned int id)
+{
+ struct tk_data *tkd = ptp_get_tk_data(id);
+
+ guard(raw_spinlock_irq)(&tkd->lock);
+ tkd->shadow_timekeeper.clock_valid = false;
+ timekeeping_update_from_shadow(tkd, TK_UPDATE_ALL);
+}
+
+static DEFINE_MUTEX(ptp_clock_mutex);
+
+static ssize_t ptp_clock_enable_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ /* Lazy atoi() as name is "0..7" */
+ int id = kobj->name[0] & 0x7;
+ bool enable;
+
+ if (!capable(CAP_SYS_TIME))
+ return -EPERM;
+
+ if (kstrtobool(buf, &enable) < 0)
+ return -EINVAL;
+
+ guard(mutex)(&ptp_clock_mutex);
+ if (enable == test_bit(id, &ptp_timekeepers))
+ return count;
+
+ if (enable) {
+ ptp_clock_enable(CLOCK_PTP + id);
+ set_bit(id, &ptp_timekeepers);
+ } else {
+ ptp_clock_disable(CLOCK_PTP + id);
+ clear_bit(id, &ptp_timekeepers);
+ }
+ return count;
+}
+
+static ssize_t ptp_clock_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ unsigned long active = READ_ONCE(ptp_timekeepers);
+ /* Lazy atoi() as name is "0..7" */
+ int id = kobj->name[0] & 0x7;
+
+ return sysfs_emit(buf, "%d\n", test_bit(id, &active));
+}
+
+static struct kobj_attribute ptp_clock_enable_attr = __ATTR_RW(ptp_clock_enable);
+
+static struct attribute *ptp_clock_enable_attrs[] = {
+ &ptp_clock_enable_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ptp_clock_enable_attr_group = {
+ .attrs = ptp_clock_enable_attrs,
+};
+
+static int __init tk_ptp_sysfs_init(void)
+{
+ struct kobject *ptpo, *tko = kobject_create_and_add("time", kernel_kobj);
+
+ if (!tko) {
+ pr_warn("Unable to create /sys/kernel/time/. POSIX PTP clocks disabled.\n");
+ return -ENOMEM;
+ }
+
+ ptpo = kobject_create_and_add("ptp_clocks", tko);
+ if (!ptpo) {
+ pr_warn("Unable to create /sys/kernel/time/ptp_clocks. POSIX PTP clocks disabled.\n");
+ kobject_put(tko);
+ return -ENOMEM;
+ }
+
+ for (int i = TIMEKEEPER_PTP; i <= TIMEKEEPER_PTP_LAST; i++) {
+ char id[2] = { [0] = '0' + (i - TIMEKEEPER_PTP), };
+ struct kobject *clk = kobject_create_and_add(id, ptpo);
+
+ if (!clk) {
+ pr_warn("Unable to create /sys/kernel/time/ptp_clocks/%d\n",
+ i - TIMEKEEPER_PTP);
+ return -ENOMEM;
+ }
+
+ int ret = sysfs_create_group(clk, &ptp_clock_enable_attr_group);
+
+ if (ret) {
+ pr_warn("Unable to create /sys/kernel/time/ptp_clocks/%d/enable\n",
+ i - TIMEKEEPER_PTP);
+ return ret;
+ }
+ }
+ return 0;
+}
+late_initcall(tk_ptp_sysfs_init);
+
static __init void tk_ptp_setup(void)
{
for (int i = TIMEKEEPER_PTP; i <= TIMEKEEPER_PTP_LAST; i++)
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [patch 26/26] timekeeping: Provide interface to control independent PTP clocks
2025-05-13 15:13 ` [patch 26/26] timekeeping: Provide interface to control independent PTP clocks Thomas Gleixner
@ 2025-05-14 8:07 ` Antoine Tenart
2025-05-14 8:37 ` Thomas Gleixner
0 siblings, 1 reply; 32+ messages in thread
From: Antoine Tenart @ 2025-05-14 8:07 UTC (permalink / raw)
To: Thomas Gleixner
Cc: LKML, netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen,
Miroslav Lichvar, Werner Abt, David Woodhouse, Stephen Boyd,
Thomas Weißschuh, Kurt Kanzenbach, Nam Cao, Alex Gieringer
On Tue, May 13, 2025 at 05:13:44PM +0200, Thomas Gleixner wrote:
> Independent PTP clocks are disabled by default and attempts to access them
> fail.
>
> Provide an interface to enable/disable them at run-time.
>
> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
> ---
> Documentation/ABI/stable/sysfs-kernel-time-ptp | 6 +
> kernel/time/timekeeping.c | 125 +++++++++++++++++++++++++
> 2 files changed, 131 insertions(+)
>
> --- /dev/null
> +++ b/Documentation/ABI/stable/sysfs-kernel-time-ptp
> @@ -0,0 +1,6 @@
> +What: /sys/kernel/time/ptp/<ID>/enable
The path added below is /sys/kernel/time/ptp_clocks/<ID>/enable.
> +Date: May 2025
> +Contact: Thomas Gleixner <tglx@linutronix.de>
> +Description:
> + Controls the enablement of independent PTP clock
> + timekeepers.
> --- a/kernel/time/timekeeping.c
> +++ b/kernel/time/timekeeping.c
> @@ -14,6 +14,7 @@
> #include <linux/sched/loadavg.h>
> #include <linux/sched/clock.h>
> #include <linux/syscore_ops.h>
> +#include <linux/sysfs.h>
> #include <linux/clocksource.h>
> #include <linux/jiffies.h>
> #include <linux/time.h>
> @@ -2900,6 +2901,130 @@ const struct k_clock clock_ptp = {
> .clock_adj = ptp_clock_adj,
> };
>
> +static void ptp_clock_enable(unsigned int id)
> +{
> + struct tk_read_base *tkr_raw = &tk_core.timekeeper.tkr_raw;
> + struct tk_data *tkd = ptp_get_tk_data(id);
> + struct timekeeper *tks = &tkd->shadow_timekeeper;
> +
> + /* Prevent the core timekeeper from changing. */
> + guard(raw_spinlock_irq)(&tk_core.lock);
> +
> + /*
> + * Setup the PTP clock assuming that the raw core timekeeper clock
> + * frequency conversion is close enough. PTP userspace has to
> + * adjust for the deviation via clock_adjtime(2).
> + */
> + guard(raw_spinlock_nested)(&tkd->lock);
> +
> + /* Remove leftovers of a previous registration */
> + memset(tks, 0, sizeof(*tks));
> + /* Restore the timekeeper id */
> + tks->id = tkd->timekeeper.id;
> + /* Setup the timekeeper based on the current system clocksource */
> + tk_setup_internals(tks, tkr_raw->clock);
> +
> + /* Mark it valid and set it live */
> + tks->clock_valid = true;
> + timekeeping_update_from_shadow(tkd, TK_UPDATE_ALL);
> +}
> +
> +static void ptp_clock_disable(unsigned int id)
> +{
> + struct tk_data *tkd = ptp_get_tk_data(id);
> +
> + guard(raw_spinlock_irq)(&tkd->lock);
> + tkd->shadow_timekeeper.clock_valid = false;
> + timekeeping_update_from_shadow(tkd, TK_UPDATE_ALL);
> +}
> +
> +static DEFINE_MUTEX(ptp_clock_mutex);
> +
> +static ssize_t ptp_clock_enable_store(struct kobject *kobj, struct kobj_attribute *attr,
> + const char *buf, size_t count)
> +{
> + /* Lazy atoi() as name is "0..7" */
> + int id = kobj->name[0] & 0x7;
> + bool enable;
> +
> + if (!capable(CAP_SYS_TIME))
> + return -EPERM;
> +
> + if (kstrtobool(buf, &enable) < 0)
> + return -EINVAL;
> +
> + guard(mutex)(&ptp_clock_mutex);
> + if (enable == test_bit(id, &ptp_timekeepers))
> + return count;
> +
> + if (enable) {
> + ptp_clock_enable(CLOCK_PTP + id);
> + set_bit(id, &ptp_timekeepers);
> + } else {
> + ptp_clock_disable(CLOCK_PTP + id);
> + clear_bit(id, &ptp_timekeepers);
> + }
> + return count;
> +}
> +
> +static ssize_t ptp_clock_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
> +{
> + unsigned long active = READ_ONCE(ptp_timekeepers);
> + /* Lazy atoi() as name is "0..7" */
> + int id = kobj->name[0] & 0x7;
> +
> + return sysfs_emit(buf, "%d\n", test_bit(id, &active));
> +}
> +
> +static struct kobj_attribute ptp_clock_enable_attr = __ATTR_RW(ptp_clock_enable);
> +
> +static struct attribute *ptp_clock_enable_attrs[] = {
> + &ptp_clock_enable_attr.attr,
> + NULL,
> +};
> +
> +static const struct attribute_group ptp_clock_enable_attr_group = {
> + .attrs = ptp_clock_enable_attrs,
> +};
> +
> +static int __init tk_ptp_sysfs_init(void)
> +{
> + struct kobject *ptpo, *tko = kobject_create_and_add("time", kernel_kobj);
> +
> + if (!tko) {
> + pr_warn("Unable to create /sys/kernel/time/. POSIX PTP clocks disabled.\n");
> + return -ENOMEM;
> + }
> +
> + ptpo = kobject_create_and_add("ptp_clocks", tko);
> + if (!ptpo) {
> + pr_warn("Unable to create /sys/kernel/time/ptp_clocks. POSIX PTP clocks disabled.\n");
> + kobject_put(tko);
> + return -ENOMEM;
> + }
> +
> + for (int i = TIMEKEEPER_PTP; i <= TIMEKEEPER_PTP_LAST; i++) {
> + char id[2] = { [0] = '0' + (i - TIMEKEEPER_PTP), };
> + struct kobject *clk = kobject_create_and_add(id, ptpo);
> +
> + if (!clk) {
> + pr_warn("Unable to create /sys/kernel/time/ptp_clocks/%d\n",
> + i - TIMEKEEPER_PTP);
> + return -ENOMEM;
> + }
> +
> + int ret = sysfs_create_group(clk, &ptp_clock_enable_attr_group);
> +
> + if (ret) {
> + pr_warn("Unable to create /sys/kernel/time/ptp_clocks/%d/enable\n",
> + i - TIMEKEEPER_PTP);
> + return ret;
> + }
> + }
> + return 0;
> +}
> +late_initcall(tk_ptp_sysfs_init);
> +
> static __init void tk_ptp_setup(void)
> {
> for (int i = TIMEKEEPER_PTP; i <= TIMEKEEPER_PTP_LAST; i++)
>
>
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [patch 26/26] timekeeping: Provide interface to control independent PTP clocks
2025-05-14 8:07 ` Antoine Tenart
@ 2025-05-14 8:37 ` Thomas Gleixner
0 siblings, 0 replies; 32+ messages in thread
From: Thomas Gleixner @ 2025-05-14 8:37 UTC (permalink / raw)
To: Antoine Tenart
Cc: LKML, netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen,
Miroslav Lichvar, Werner Abt, David Woodhouse, Stephen Boyd,
Thomas Weißschuh, Kurt Kanzenbach, Nam Cao, Alex Gieringer
On Wed, May 14 2025 at 10:07, Antoine Tenart wrote:
> On Tue, May 13, 2025 at 05:13:44PM +0200, Thomas Gleixner wrote:
>> +++ b/Documentation/ABI/stable/sysfs-kernel-time-ptp
>> @@ -0,0 +1,6 @@
>> +What: /sys/kernel/time/ptp/<ID>/enable
>
> The path added below is /sys/kernel/time/ptp_clocks/<ID>/enable.
Ooops.
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [patch 00/26] timekeeping: Provide support for independent PTP timekeepers
2025-05-13 15:12 [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Thomas Gleixner
` (25 preceding siblings ...)
2025-05-13 15:13 ` [patch 26/26] timekeeping: Provide interface to control independent PTP clocks Thomas Gleixner
@ 2025-05-14 7:12 ` Miroslav Lichvar
2025-05-14 8:54 ` Thomas Gleixner
26 siblings, 1 reply; 32+ messages in thread
From: Miroslav Lichvar @ 2025-05-14 7:12 UTC (permalink / raw)
To: Thomas Gleixner
Cc: LKML, netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen, Werner Abt,
David Woodhouse, Stephen Boyd, Thomas Weißschuh,
Kurt Kanzenbach, Nam Cao, Alex Gieringer
On Tue, May 13, 2025 at 05:12:54PM +0200, Thomas Gleixner wrote:
> This series addresses the timekeeping part by utilizing the existing
> timekeeping and NTP infrastructure, which has been prepared for
> multi-instance in recent kernels.
This looks very interesting. I ran some quick tests and it seems to
work as expected from the user space point of view. I can enable the
clock and synchronize it to a PTP HW clock or the system REALTIME
clock. ADJ_TICK works too.
To get accuracy and stability comparable to CLOCK_REALTIME, there will
need to be some support for cross timestamping against CLOCK_REALTIME
and/or PTP HW clocks, e.g. a variant of the PTP_SYS_OFFSET_PRECISE and
PTP_SYS_OFFSET_EXTENDED ioctls where the target clock can be selected.
The "PTP" naming of these new clocks doesn't seem right to me though
and I suspect it would just create more confusion. I don't see
anything specific to PTP here. There is no timestamping of network
packets, no /dev/ptp device, no PTP ioctls. To me they look like
secondary or auxiliary system realtime clocks. I propose to rename
them from CLOCK_PTP0-7 to CLOCK_REALTIME2-9, CLOCK_AUXILIARY0-7, or
CLOCK_AUX0-7.
--
Miroslav Lichvar
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [patch 00/26] timekeeping: Provide support for independent PTP timekeepers
2025-05-14 7:12 ` [patch 00/26] timekeeping: Provide support for independent PTP timekeepers Miroslav Lichvar
@ 2025-05-14 8:54 ` Thomas Gleixner
2025-05-14 15:58 ` Richard Cochran
0 siblings, 1 reply; 32+ messages in thread
From: Thomas Gleixner @ 2025-05-14 8:54 UTC (permalink / raw)
To: Miroslav Lichvar
Cc: LKML, netdev, Richard Cochran, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen, Werner Abt,
David Woodhouse, Stephen Boyd, Thomas Weißschuh,
Kurt Kanzenbach, Nam Cao, Alex Gieringer
On Wed, May 14 2025 at 09:12, Miroslav Lichvar wrote:
> On Tue, May 13, 2025 at 05:12:54PM +0200, Thomas Gleixner wrote:
>> This series addresses the timekeeping part by utilizing the existing
>> timekeeping and NTP infrastructure, which has been prepared for
>> multi-instance in recent kernels.
>
> This looks very interesting. I ran some quick tests and it seems to
> work as expected from the user space point of view. I can enable the
> clock and synchronize it to a PTP HW clock or the system REALTIME
> clock. ADJ_TICK works too.
Cool.
> To get accuracy and stability comparable to CLOCK_REALTIME, there will
> need to be some support for cross timestamping against CLOCK_REALTIME
> and/or PTP HW clocks, e.g. a variant of the PTP_SYS_OFFSET_PRECISE and
> PTP_SYS_OFFSET_EXTENDED ioctls where the target clock can be selected.
Yes, that's required, but for that to implement we need the core muck
first :)
> The "PTP" naming of these new clocks doesn't seem right to me though
> and I suspect it would just create more confusion. I don't see
> anything specific to PTP here. There is no timestamping of network
> packets, no /dev/ptp device, no PTP ioctls. To me they look like
> secondary or auxiliary system realtime clocks. I propose to rename
> them from CLOCK_PTP0-7 to CLOCK_REALTIME2-9, CLOCK_AUXILIARY0-7, or
> CLOCK_AUX0-7.
CLOCK_REALTIME2-9 would be weird as those clocks have not necessarily a
relationship to CLOCK_REALTIME. They can have a seperate resulting
frequency and starting point when they are soleley used for application
specific purposes within a network (think automation, automotive, audio
etc.).
CLOCK_AUX0-7 sounds really good to me and makes sense. I picked PTP
because that's where I was coming from. I'll rework that accordingly and
make the config enablement independent of PTP as well:
config POSIX_CLOCKS_AUX
bool "Enable auxiliary POSIX clocks" if POSIX_TIMERS
help
Add blurb
and PTP can eventually select it (or not). Something like that.
Thanks,
tglx
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [patch 00/26] timekeeping: Provide support for independent PTP timekeepers
2025-05-14 8:54 ` Thomas Gleixner
@ 2025-05-14 15:58 ` Richard Cochran
0 siblings, 0 replies; 32+ messages in thread
From: Richard Cochran @ 2025-05-14 15:58 UTC (permalink / raw)
To: Thomas Gleixner
Cc: Miroslav Lichvar, LKML, netdev, Christopher Hall, David Zage,
John Stultz, Frederic Weisbecker, Anna-Maria Behnsen, Werner Abt,
David Woodhouse, Stephen Boyd, Thomas Weißschuh,
Kurt Kanzenbach, Nam Cao, Alex Gieringer
On Wed, May 14, 2025 at 10:54:33AM +0200, Thomas Gleixner wrote:
> CLOCK_AUX0-7 sounds really good to me and makes sense. I picked PTP
> because that's where I was coming from. I'll rework that accordingly and
> make the config enablement independent of PTP as well:
+1 for using AUX naming instead of PTP.
Thanks,
Richard
^ permalink raw reply [flat|nested] 32+ messages in thread