History

ntp: Consolidate the RTC update implementation

The code for the legacy RTC and the RTC class based update are pretty much the same. Consolidate the common parts into one function and just invoke the actual setter functions. For RTC class based devices the update code checks whether the offset is valid for the device, which is usually not the case for the first invocation. If it's not the same it stores the correct offset and lets the caller try again. That's not much different from the previous approach where the first invocation had a pretty low probability to actually hit the allowed window.

Thomas Gleixner committed 5 years ago

Commit 76e87d91 parent 69eca251 file · +52 −92Browse files

Showing the changes to kernel (1 of 1 file). Show all

Unified · Split

kernel/time/ntp.c+52−92
@@ -564,118 +564,53 @@ static inline bool rtc_tv_nsec_ok(unsigned long set_offset_nsec, · 563 unmodified lines
564564 return false;
565565 }
566566
567+#ifdef CONFIG_GENERIC_CMOS_UPDATE
568+int __weak update_persistent_clock64(struct timespec64 now64)
569+{
570+ return -ENODEV;
571+}
572+#else
573+static inline int update_persistent_clock64(struct timespec64 now64)
574+{
575+ return -ENODEV;
576+}
577+#endif
578+
567579 #ifdef CONFIG_RTC_SYSTOHC
568−/*
569− * rtc_set_ntp_time - Save NTP synchronized time to the RTC
570− */
571−static int rtc_set_ntp_time(struct timespec64 now, unsigned long *offset_nsec)
580+/* Save NTP synchronized time to the RTC */
581+static int update_rtc(struct timespec64 *to_set, unsigned long *offset_nsec)
572582 {
573− struct timespec64 to_set;
574583 struct rtc_device *rtc;
575584 struct rtc_time tm;
576585 int err = -ENODEV;
577− bool ok;
578586
579587 rtc = rtc_class_open(CONFIG_RTC_SYSTOHC_DEVICE);
580588 if (!rtc)
581− goto out_err;
589+ return -ENODEV;
582590
583591 if (!rtc->ops || !rtc->ops->set_time)
584592 goto out_close;
585593
586− /* Store the update offset for this RTC */
587− *offset_nsec = rtc->set_offset_nsec;
588−
589− ok = rtc_tv_nsec_ok(rtc->set_offset_nsec, &to_set, &now);
590− if (!ok) {
591− err = -EPROTO;
592− goto out_close;
594+ /* First call might not have the correct offset */
595+ if (*offset_nsec == rtc->set_offset_nsec) {
596+ rtc_time64_to_tm(to_set->tv_sec, &tm);
597+ err = rtc_set_time(rtc, &tm);
598+ } else {
599+ /* Store the update offset and let the caller try again */
600+ *offset_nsec = rtc->set_offset_nsec;
601+ err = -EAGAIN;
593602 }
594−
595− rtc_time64_to_tm(to_set.tv_sec, &tm);
596−
597− err = rtc_set_time(rtc, &tm);
598−
599603 out_close:
600604 rtc_class_close(rtc);
601−out_err:
602605 return err;
603606 }
604−
605−static void sync_rtc_clock(void)
606−{
607− unsigned long offset_nsec;
608− struct timespec64 adjust;
609− int rc;
610−
611− ktime_get_real_ts64(&adjust);
612−
613− if (persistent_clock_is_local)
614− adjust.tv_sec -= (sys_tz.tz_minuteswest * 60);
615−
616− /*
617− * The current RTC in use will provide the nanoseconds offset prior
618− * to a full second it wants to be called at, and invokes
619− * rtc_tv_nsec_ok() internally.
620− */
621− rc = rtc_set_ntp_time(adjust, &offset_nsec);
622− if (rc == -ENODEV)
623− return;
624−
625− sched_sync_hw_clock(offset_nsec, rc != 0);
626−}
627607 #else
628−static inline void sync_rtc_clock(void) { }
629−#endif
630−
631−#ifdef CONFIG_GENERIC_CMOS_UPDATE
632−int __weak update_persistent_clock64(struct timespec64 now64)
608+static inline int update_rtc(struct timespec64 *to_set, unsigned long *offset_nsec)
633609 {
634610 return -ENODEV;
635611 }
636612 #endif
637613
638−static bool sync_cmos_clock(void)
639−{
640− static bool no_cmos;
641− struct timespec64 now;
642− struct timespec64 adjust;
643− int rc = -EPROTO;
644− long target_nsec = NSEC_PER_SEC / 2;
645−
646− if (!IS_ENABLED(CONFIG_GENERIC_CMOS_UPDATE))
647− return false;
648−
649− if (no_cmos)
650− return false;
651−
652− /*
653− * Historically update_persistent_clock64() has followed x86
654− * semantics, which match the MC146818A/etc RTC. This RTC will store
655− * 'adjust' and then in .5s it will advance once second.
656− *
657− * Architectures are strongly encouraged to use rtclib and not
658− * implement this legacy API.
659− */
660− ktime_get_real_ts64(&now);
661− if (rtc_tv_nsec_ok(target_nsec, &adjust, &now)) {
662− if (persistent_clock_is_local)
663− adjust.tv_sec -= (sys_tz.tz_minuteswest * 60);
664− rc = update_persistent_clock64(adjust);
665− /*
666− * The machine does not support update_persistent_clock64 even
667− * though it defines CONFIG_GENERIC_CMOS_UPDATE.
668− */
669− if (rc == -ENODEV) {
670− no_cmos = true;
671− return false;
672− }
673− }
674−
675− sched_sync_hw_clock(target_nsec, rc != 0);
676− return true;
677−}
678−
679614 /*
680615 * If we have an externally synchronized Linux clock, then update RTC clock
681616 * accordingly every ~11 minutes. Generally RTCs can only store second
@@ -686,6 +621,15 @@ static bool sync_cmos_clock(void) · 4 unmodified lines
686621 */
687622 static void sync_hw_clock(struct work_struct *work)
688623 {
624+ /*
625+ * The default synchronization offset is 500ms for the deprecated
626+ * update_persistent_clock64() under the assumption that it uses
627+ * the infamous CMOS clock (MC146818).
628+ */
629+ static unsigned long offset_nsec = NSEC_PER_SEC / 2;
630+ struct timespec64 now, to_set;
631+ int res = -EAGAIN;
632+
689633 /*
690634 * Don't update if STA_UNSYNC is set and if ntp_notify_cmos_timer()
691635 * managed to schedule the work between the timer firing and the
@@ -694,10 +638,26 @@ static void sync_hw_clock(struct work_struct *work) · 2 unmodified lines
694638 if (!ntp_synced() || hrtimer_is_queued(&sync_hrtimer))
695639 return;
696640
697− if (sync_cmos_clock())
698− return;
641+ ktime_get_real_ts64(&now);
642+ /* If @now is not in the allowed window, try again */
643+ if (!rtc_tv_nsec_ok(offset_nsec, &to_set, &now))
644+ goto rearm;
699645
700− sync_rtc_clock();
646+ /* Take timezone adjusted RTCs into account */
647+ if (persistent_clock_is_local)
648+ to_set.tv_sec -= (sys_tz.tz_minuteswest * 60);
649+
650+ /* Try the legacy RTC first. */
651+ res = update_persistent_clock64(to_set);
652+ if (res != -ENODEV)
653+ goto rearm;
654+
655+ /* Try the RTC class */
656+ res = update_rtc(&to_set, &offset_nsec);
657+ if (res == -ENODEV)
658+ return;
659+rearm:
660+ sched_sync_hw_clock(offset_nsec, res != 0);
701661 }
702662
703663 void ntp_notify_cmos_timer(void)

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t