summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorOlav Haugan <ohaugan@codeaurora.org>2016-09-30 17:04:11 -0700
committerOlav Haugan <ohaugan@codeaurora.org>2016-09-30 17:04:11 -0700
commit62abf225df93192c9696f9affc6f5a55e8525d22 (patch)
tree26fc8e5afcef4d317c86909dfd448d3e30b113df /kernel
parent46692be6dd06ce405cb09cf8338b1f0edbbfb295 (diff)
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 <ohaugan@codeaurora.org>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/time/hrtimer.c42
1 files changed, 29 insertions, 13 deletions
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 */