From 62abf225df93192c9696f9affc6f5a55e8525d22 Mon Sep 17 00:00:00 2001 From: Olav Haugan Date: Fri, 30 Sep 2016 17:04:11 -0700 Subject: hrtimer: Ensure timer is not running before migrating A timer might be running when we are trying to move the timer to another CPU so ensure that we wait for the timer to finish before migrating. Change-Id: I4c9ee39c715baebfbdb8a50476a475e38b092f70 Signed-off-by: Olav Haugan --- kernel/time/hrtimer.c | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) (limited to 'kernel') diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c index 1b0117198a08..9e1349fc5bbe 100644 --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c @@ -1625,20 +1625,36 @@ static void init_hrtimers_cpu(int cpu) } #if defined(CONFIG_HOTPLUG_CPU) -static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base, - struct hrtimer_clock_base *new_base, +static void migrate_hrtimer_list(struct hrtimer_cpu_base *old_base, + struct hrtimer_cpu_base *new_base, + unsigned int i, + bool wait, bool remove_pinned) { struct hrtimer *timer; struct timerqueue_node *node; struct timerqueue_head pinned; int is_pinned; + struct hrtimer_clock_base *old_c_base = &old_base->clock_base[i]; + struct hrtimer_clock_base *new_c_base = &new_base->clock_base[i]; timerqueue_init_head(&pinned); - while ((node = timerqueue_getnext(&old_base->active))) { + while ((node = timerqueue_getnext(&old_c_base->active))) { timer = container_of(node, struct hrtimer, node); - BUG_ON(hrtimer_callback_running(timer)); + if (wait) { + /* Ensure timers are done running before continuing */ + while (hrtimer_callback_running(timer)) { + raw_spin_unlock(&old_base->lock); + raw_spin_unlock(&new_base->lock); + cpu_relax(); + raw_spin_lock(&new_base->lock); + raw_spin_lock_nested(&old_base->lock, + SINGLE_DEPTH_NESTING); + } + } else { + BUG_ON(hrtimer_callback_running(timer)); + } debug_deactivate(timer); /* @@ -1646,7 +1662,7 @@ static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base, * timer could be seen as !active and just vanish away * under us on another CPU */ - __remove_hrtimer(timer, old_base, HRTIMER_STATE_ENQUEUED, 0); + __remove_hrtimer(timer, old_c_base, HRTIMER_STATE_ENQUEUED, 0); is_pinned = timer->state & HRTIMER_STATE_PINNED; if (!remove_pinned && is_pinned) { @@ -1654,7 +1670,7 @@ static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base, continue; } - timer->base = new_base; + timer->base = new_c_base; /* * Enqueue the timers on the new cpu. This does not * reprogram the event device in case the timer @@ -1663,7 +1679,7 @@ static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base, * sort out already expired timers and reprogram the * event device. */ - enqueue_hrtimer(timer, new_base); + enqueue_hrtimer(timer, new_c_base); } /* Re-queue pinned timers for non-hotplug usecase */ @@ -1671,11 +1687,11 @@ static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base, timer = container_of(node, struct hrtimer, node); timerqueue_del(&pinned, &timer->node); - enqueue_hrtimer(timer, old_base); + enqueue_hrtimer(timer, old_c_base); } } -static void __migrate_hrtimers(int scpu, bool remove_pinned) +static void __migrate_hrtimers(int scpu, bool wait, bool remove_pinned) { struct hrtimer_cpu_base *old_base, *new_base; unsigned long flags; @@ -1692,8 +1708,8 @@ static void __migrate_hrtimers(int scpu, bool remove_pinned) raw_spin_lock_nested(&old_base->lock, SINGLE_DEPTH_NESTING); for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++) { - migrate_hrtimer_list(&old_base->clock_base[i], - &new_base->clock_base[i], remove_pinned); + migrate_hrtimer_list(old_base, new_base, i, wait, + remove_pinned); } raw_spin_unlock(&old_base->lock); @@ -1709,12 +1725,12 @@ static void migrate_hrtimers(int scpu) BUG_ON(cpu_online(scpu)); tick_cancel_sched_timer(scpu); - __migrate_hrtimers(scpu, true); + __migrate_hrtimers(scpu, false, true); } void hrtimer_quiesce_cpu(void *cpup) { - __migrate_hrtimers(*(int *)cpup, false); + __migrate_hrtimers(*(int *)cpup, true, false); } #endif /* CONFIG_HOTPLUG_CPU */ -- cgit v1.2.3