From 6cc98f03adbf517986a90c43af0cbc9a732b8435 Mon Sep 17 00:00:00 2001 From: Olav Haugan Date: Fri, 8 Jul 2016 10:59:08 -0700 Subject: timer: Ensure timers are not running before migrating This is needed to support migration of timers during cpu isolation. A timer might be running on the CPU that we want to isolate so we are unable to migrate the timers at this point. We are adding a spin-loop to wait for the timer to finish before migrating the timers. Change-Id: I24d6e91b6dff468c640c2fe3a37a7f31b6f0c79a Signed-off-by: Olav Haugan --- kernel/time/timer.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'kernel/time/timer.c') diff --git a/kernel/time/timer.c b/kernel/time/timer.c index 51896272fcde..be750f6b2a68 100644 --- a/kernel/time/timer.c +++ b/kernel/time/timer.c @@ -1635,7 +1635,7 @@ static void migrate_timer_list(struct tvec_base *new_base, struct hlist_head *he } } -static void migrate_timers(int cpu) +static void migrate_timers(int cpu, bool wait) { struct tvec_base *old_base; struct tvec_base *new_base; @@ -1651,7 +1651,18 @@ static void migrate_timers(int cpu) spin_lock_irq(&new_base->lock); spin_lock_nested(&old_base->lock, SINGLE_DEPTH_NESTING); - BUG_ON(old_base->running_timer); + if (wait) { + /* Ensure timers are done running before continuing */ + while (old_base->running_timer) { + spin_unlock(&old_base->lock); + spin_unlock_irq(&new_base->lock); + cpu_relax(); + spin_lock_irq(&new_base->lock); + spin_lock_nested(&old_base->lock, SINGLE_DEPTH_NESTING); + } + } else { + BUG_ON(old_base->running_timer); + } for (i = 0; i < TVR_SIZE; i++) migrate_timer_list(new_base, old_base->tv1.vec + i); @@ -1676,7 +1687,7 @@ static int timer_cpu_notify(struct notifier_block *self, switch (action) { case CPU_DEAD: case CPU_DEAD_FROZEN: - migrate_timers((long)hcpu); + migrate_timers((long)hcpu, false); break; default: break; -- cgit v1.2.3 From a7dffd7ffbe6aafe8da10f82a922c00e1c65acdc Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 25 Mar 2015 11:47:53 +0530 Subject: timer: create timer_quiesce_cpu() to isolate CPU from timers To isolate CPUs (isolate from timers) from sysfs using cpusets, we need some support from the timer core. i.e. A routine timer_quiesce_cpu() which would migrates away all the unpinned timers, but shouldn't touch the pinned ones. This patch creates this routine. Change-Id: I8624e0659b86b7b8fa425a3fafdb0784fe005124 Signed-off-by: Viresh Kumar [forward port to 3.18] Signed-off-by: Santosh Shukla [ohaugan@codeaurora.org: Port to 4.4. Fixes for compilation error] Git-commit: 313910b70ea0c73f8789d9189c11e1f339080646 Git-repo: git://git.linaro.org/people/mike.holmes/santosh.shukla/lng-isol.git Signed-off-by: Olav Haugan --- kernel/time/timer.c | 56 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 21 deletions(-) (limited to 'kernel/time/timer.c') diff --git a/kernel/time/timer.c b/kernel/time/timer.c index be750f6b2a68..067174a4dde3 100644 --- a/kernel/time/timer.c +++ b/kernel/time/timer.c @@ -1620,44 +1620,49 @@ signed long __sched schedule_timeout_uninterruptible(signed long timeout) } EXPORT_SYMBOL(schedule_timeout_uninterruptible); -#ifdef CONFIG_HOTPLUG_CPU -static void migrate_timer_list(struct tvec_base *new_base, struct hlist_head *head) +#if defined(CONFIG_HOTPLUG_CPU) +static void migrate_timer_list(struct tvec_base *new_base, + struct hlist_head *head, bool remove_pinned) { struct timer_list *timer; int cpu = new_base->cpu; + struct hlist_node *n; + int is_pinned; - while (!hlist_empty(head)) { - timer = hlist_entry(head->first, struct timer_list, entry); - /* We ignore the accounting on the dying cpu */ - detach_timer(timer, false); + hlist_for_each_entry_safe(timer, n, head, entry) { + is_pinned = timer->flags & TIMER_PINNED_ON_CPU; + if (!remove_pinned && is_pinned) + continue; + + detach_if_pending(timer, get_timer_base(timer->flags), false); timer->flags = (timer->flags & ~TIMER_BASEMASK) | cpu; internal_add_timer(new_base, timer); } } -static void migrate_timers(int cpu, bool wait) +static void __migrate_timers(int cpu, bool wait, bool remove_pinned) { struct tvec_base *old_base; struct tvec_base *new_base; + unsigned long flags; int i; - BUG_ON(cpu_online(cpu)); old_base = per_cpu_ptr(&tvec_bases, cpu); new_base = get_cpu_ptr(&tvec_bases); /* * The caller is globally serialized and nobody else * takes two locks at once, deadlock is not possible. */ - spin_lock_irq(&new_base->lock); + spin_lock_irqsave(&new_base->lock, flags); spin_lock_nested(&old_base->lock, SINGLE_DEPTH_NESTING); if (wait) { /* Ensure timers are done running before continuing */ while (old_base->running_timer) { spin_unlock(&old_base->lock); - spin_unlock_irq(&new_base->lock); + spin_unlock_irqrestore(&new_base->lock, flags); cpu_relax(); - spin_lock_irq(&new_base->lock); + spin_lock_irqsave(&new_base->lock, flags); spin_lock_nested(&old_base->lock, SINGLE_DEPTH_NESTING); } } else { @@ -1665,29 +1670,38 @@ static void migrate_timers(int cpu, bool wait) } for (i = 0; i < TVR_SIZE; i++) - migrate_timer_list(new_base, old_base->tv1.vec + i); + migrate_timer_list(new_base, old_base->tv1.vec + i, + remove_pinned); for (i = 0; i < TVN_SIZE; i++) { - migrate_timer_list(new_base, old_base->tv2.vec + i); - migrate_timer_list(new_base, old_base->tv3.vec + i); - migrate_timer_list(new_base, old_base->tv4.vec + i); - migrate_timer_list(new_base, old_base->tv5.vec + i); + migrate_timer_list(new_base, old_base->tv2.vec + i, + remove_pinned); + migrate_timer_list(new_base, old_base->tv3.vec + i, + remove_pinned); + migrate_timer_list(new_base, old_base->tv4.vec + i, + remove_pinned); + migrate_timer_list(new_base, old_base->tv5.vec + i, + remove_pinned); } - old_base->active_timers = 0; - old_base->all_timers = 0; - spin_unlock(&old_base->lock); - spin_unlock_irq(&new_base->lock); + spin_unlock_irqrestore(&new_base->lock, flags); put_cpu_ptr(&tvec_bases); } +/* Migrate timers from 'cpu' to this_cpu */ +static void migrate_timers(int cpu) +{ + BUG_ON(cpu_online(cpu)); + __migrate_timers(cpu, false, true); +} + static int timer_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) { switch (action) { case CPU_DEAD: case CPU_DEAD_FROZEN: - migrate_timers((long)hcpu, false); + migrate_timers((long)hcpu); break; default: break; -- cgit v1.2.3 From bba552f4fc0046325f29156720a04623a71821d5 Mon Sep 17 00:00:00 2001 From: Santosh Shukla Date: Wed, 25 Mar 2015 16:09:32 +0530 Subject: timer: Add function to migrate timers Add function to migrate timer that will be used by later patch set. Change-Id: I370e404001344e635a663822b07557abbe0f6f52 Signed-off-by: Santosh Shukla [ohaugan@codeaurora.org: Updated commit text and fixed trivial merge conflict] Git-commit: 3633b88d8fcb4273807574c27c328b6908a741e5 Git-repo: git://git.linaro.org/people/mike.holmes/santosh.shukla/lng-isol.git Signed-off-by: Olav Haugan --- kernel/time/timer.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'kernel/time/timer.c') diff --git a/kernel/time/timer.c b/kernel/time/timer.c index 067174a4dde3..d38a67a49550 100644 --- a/kernel/time/timer.c +++ b/kernel/time/timer.c @@ -1695,6 +1695,13 @@ static void migrate_timers(int cpu) __migrate_timers(cpu, false, true); } +#ifdef CONFIG_CPUSETS +void timer_quiesce_cpu(void *cpup) +{ + __migrate_timers(*(int *)cpup, true, false); +} +#endif /* CONFIG_CPUSETS */ + static int timer_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) { -- cgit v1.2.3 From 922fed628c625b28a50f66267e4b9f99088e2aa4 Mon Sep 17 00:00:00 2001 From: Olav Haugan Date: Sun, 3 Jul 2016 15:02:08 -0700 Subject: timer: Do not require CPUSETS to be enabled for migration Do not require CPUSETS to be enabled to allow migration of timers and hrtimers. Change-Id: Ib911a0d34c250c4df020bdb265b92d2b8df8db93 Signed-off-by: Olav Haugan --- kernel/time/timer.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'kernel/time/timer.c') diff --git a/kernel/time/timer.c b/kernel/time/timer.c index d38a67a49550..0efb3916f5a4 100644 --- a/kernel/time/timer.c +++ b/kernel/time/timer.c @@ -1695,12 +1695,10 @@ static void migrate_timers(int cpu) __migrate_timers(cpu, false, true); } -#ifdef CONFIG_CPUSETS void timer_quiesce_cpu(void *cpup) { __migrate_timers(*(int *)cpup, true, false); } -#endif /* CONFIG_CPUSETS */ static int timer_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) -- cgit v1.2.3