summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/time/tick-common.c36
1 files changed, 33 insertions, 3 deletions
diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c
index 7e5d3524e924..cce4ed1515c7 100644
--- a/kernel/time/tick-common.c
+++ b/kernel/time/tick-common.c
@@ -16,6 +16,7 @@
#include <linux/profile.h>
#include <linux/sched.h>
#include <linux/module.h>
+#include <linux/sched/clock.h>
#include <trace/events/power.h>
#include <asm/irq_regs.h>
@@ -84,12 +85,41 @@ int tick_is_oneshot_available(void)
static void tick_periodic(int cpu)
{
if (tick_do_timer_cpu == cpu) {
+ /*
+ * Use running_clock() as reference to check for missing ticks.
+ */
+ static ktime_t last_update;
+ ktime_t now;
+ int ticks = 1;
+
+ now = ns_to_ktime(running_clock());
write_seqlock(&jiffies_lock);
- /* Keep track of the next tick event */
- tick_next_period = ktime_add(tick_next_period, tick_period);
+ if (last_update) {
+ u64 delta = ktime_sub(now, last_update);
- do_timer(1);
+ /*
+ * Check for eventually missed ticks
+ *
+ * There is likely a persistent delta between
+ * last_update and tick_next_period. So they are
+ * updated separately.
+ */
+ if (delta >= 2 * tick_period) {
+ s64 period = ktime_to_ns(tick_period);
+
+ ticks = ktime_divns(delta, period);
+ }
+ last_update = ktime_add(last_update,
+ ticks * tick_period);
+ } else {
+ last_update = now;
+ }
+
+ /* Keep track of the next tick event */
+ tick_next_period = ktime_add(tick_next_period,
+ ticks * tick_period);
+ do_timer(ticks);
write_sequnlock(&jiffies_lock);
update_wall_time();
}