@@ -492,10 +492,58 @@ int second_overflow(time64_t secs)
return leap;
}
+/*
+ * We need to use a hrtimer to queue our work - normal delayed works use
+ * the timer_list timers, which are too inaccurate: for a 10s delay,
+ * they can be delayed between 1 and 63 jiffies.
+ */
+struct hrtimer_delayed_work {
+ struct work_struct work;
+ struct hrtimer timer;
+};
+
+static enum hrtimer_restart hdw_queue_work(struct hrtimer *hr)
+{
+ struct hrtimer_delayed_work *w;
+
+ w = container_of(hr, struct hrtimer_delayed_work, timer);
+ queue_work(system_power_efficient_wq, &w->work);
+
+ return HRTIMER_NORESTART;
+}
+
+static bool hdw_queued(struct hrtimer_delayed_work *hdw)
+{
+ return hrtimer_is_queued(&hdw->timer) != 0;
+}
+
+static void hdw_queue(struct hrtimer_delayed_work *hdw,
+ const struct timespec64 *t)
+{
+ ktime_t ns = t ? timespec64_to_ktime(*t) : 0;
+
+ hrtimer_start(&hdw->timer, ns, HRTIMER_MODE_REL);
+}
+
+static bool hdw_initialised(struct hrtimer_delayed_work *hdw)
+{
+ return hdw->timer.function != NULL;
+}
+
+static void hdw_init(struct hrtimer_delayed_work *hdw)
+{
+ hrtimer_init(&hdw->timer, CLOCK_REALTIME, HRTIMER_MODE_REL);
+ hdw->timer.function = hdw_queue_work;
+}
+
+
unsigned int sysctl_ntp_rtc_sync = true;
static void sync_hw_clock(struct work_struct *work);
-static DECLARE_DELAYED_WORK(sync_work, sync_hw_clock);
+
+static struct hrtimer_delayed_work sync_work = {
+ .work = __WORK_INITIALIZER(sync_work.work, sync_hw_clock),
+};
static void sched_sync_hw_clock(struct timespec64 now,
unsigned long target_nsec, bool fail)
@@ -526,8 +574,7 @@ static void sched_sync_hw_clock(struct timespec64 now,
next.tv_nsec -= NSEC_PER_SEC;
}
- queue_delayed_work(system_power_efficient_wq, &sync_work,
- timespec64_to_jiffies(&next));
+ hdw_queue(&sync_work, &next);
printk(KERN_DEBUG "%s: adjust=%lld.%09ld now=%lld.%09ld next=%lld.%09ld target=%ld fail=%d\n",
__func__,
@@ -635,9 +682,12 @@ void ntp_notify_cmos_timer(void)
if (!ntp_synced() || !sysctl_ntp_rtc_sync)
return;
- if (IS_ENABLED(CONFIG_GENERIC_CMOS_UPDATE) ||
- IS_ENABLED(CONFIG_RTC_SYSTOHC))
- queue_delayed_work(system_power_efficient_wq, &sync_work, 0);
+ if (!hdw_initialised(&sync_work))
+ hdw_init(&sync_work);
+
+ if ((IS_ENABLED(CONFIG_GENERIC_CMOS_UPDATE) ||
+ IS_ENABLED(CONFIG_RTC_SYSTOHC)) && !hdw_queued(&sync_work))
+ hdw_queue(&sync_work, NULL);
}
/*