Skip to content

Commit 35b603f

Browse files
benjarobinKAGA-KOKO
authored andcommitted
ntp: Make sure RTC is synchronized when time goes backwards
sync_hw_clock() is normally called every 11 minutes when time is synchronized. This issue is that this periodic timer uses the REALTIME clock, so when time moves backwards (the NTP server jumps into the past), the timer expires late. If the timer expires late, which can be days later, the RTC will no longer be updated, which is an issue if the device is abruptly powered OFF during this period. When the device will restart (when powered ON), it will have the date prior to the ADJ_SETOFFSET call. A normal NTP server should not jump in the past like that, but it is possible... Another way of reproducing this issue is to use phc2sys to synchronize the REALTIME clock with, for example, an IRIG timecode with the source always starting at the same date (not synchronized). Also, if the time jump in the future by less than 11 minutes, the RTC may not be updated immediately (minor issue). Consider the following scenario: - Time is synchronized, and sync_hw_clock() was just called (the timer expires in 11 minutes). - A time jump is realized in the future by a couple of minutes. - The time is synchronized again. - Users may expect that RTC to be updated as soon as possible, and not after 11 minutes (for the same reason, if a power loss occurs in this period). Cancel periodic timer on any time jump (ADJ_SETOFFSET) greater than or equal to 1s. The timer will be relaunched at the end of do_adjtimex() if NTP is still considered synced. Otherwise the timer will be relaunched later when NTP is synced. This way, when the time is synchronized again, the RTC is updated after less than 2 seconds. Signed-off-by: Benjamin ROBIN <dev@benjarobin.fr> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Link: https://lore.kernel.org/all/20240908140836.203911-1-dev@benjarobin.fr
1 parent 2f7eedc commit 35b603f

File tree

3 files changed

+14
-4
lines changed

3 files changed

+14
-4
lines changed

kernel/time/ntp.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -660,8 +660,16 @@ static void sync_hw_clock(struct work_struct *work)
660660
sched_sync_hw_clock(offset_nsec, res != 0);
661661
}
662662

663-
void ntp_notify_cmos_timer(void)
663+
void ntp_notify_cmos_timer(bool offset_set)
664664
{
665+
/*
666+
* If the time jumped (using ADJ_SETOFFSET) cancels sync timer,
667+
* which may have been running if the time was synchronized
668+
* prior to the ADJ_SETOFFSET call.
669+
*/
670+
if (offset_set)
671+
hrtimer_cancel(&sync_hrtimer);
672+
665673
/*
666674
* When the work is currently executed but has not yet the timer
667675
* rearmed this queues the work immediately again. No big issue,

kernel/time/ntp_internal.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ extern int __do_adjtimex(struct __kernel_timex *txc,
1414
extern void __hardpps(const struct timespec64 *phase_ts, const struct timespec64 *raw_ts);
1515

1616
#if defined(CONFIG_GENERIC_CMOS_UPDATE) || defined(CONFIG_RTC_SYSTOHC)
17-
extern void ntp_notify_cmos_timer(void);
17+
extern void ntp_notify_cmos_timer(bool offset_set);
1818
#else
19-
static inline void ntp_notify_cmos_timer(void) { }
19+
static inline void ntp_notify_cmos_timer(bool offset_set) { }
2020
#endif
2121

2222
#endif /* _LINUX_NTP_INTERNAL_H */

kernel/time/timekeeping.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2553,6 +2553,7 @@ int do_adjtimex(struct __kernel_timex *txc)
25532553
{
25542554
struct timekeeper *tk = &tk_core.timekeeper;
25552555
struct audit_ntp_data ad;
2556+
bool offset_set = false;
25562557
bool clock_set = false;
25572558
struct timespec64 ts;
25582559
unsigned long flags;
@@ -2575,6 +2576,7 @@ int do_adjtimex(struct __kernel_timex *txc)
25752576
if (ret)
25762577
return ret;
25772578

2579+
offset_set = delta.tv_sec != 0;
25782580
audit_tk_injoffset(delta);
25792581
}
25802582

@@ -2608,7 +2610,7 @@ int do_adjtimex(struct __kernel_timex *txc)
26082610
if (clock_set)
26092611
clock_was_set(CLOCK_SET_WALL);
26102612

2611-
ntp_notify_cmos_timer();
2613+
ntp_notify_cmos_timer(offset_set);
26122614

26132615
return ret;
26142616
}

0 commit comments

Comments
 (0)