diff options
author | Viresh Kumar <viresh.kumar@linaro.org> | 2015-03-25 11:47:53 +0530 |
---|---|---|
committer | Gary S. Robertson <gary.robertson@linaro.org> | 2015-07-22 16:17:06 -0500 |
commit | e5319a8a6359c078b8f361950ff8639e68bb4dc8 (patch) | |
tree | b5c9fec4d0619d640a74be3571d63442dc52986e | |
parent | 42cbc0aa690f663a96758587a844c7f6a80a1162 (diff) |
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.
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
[forward port to 3.18]
Signed-off-by: Santosh Shukla <santosh.shukla@linaro.org>
-rw-r--r-- | include/linux/timer.h | 3 | ||||
-rw-r--r-- | kernel/time/timer.c | 54 |
2 files changed, 45 insertions, 12 deletions
diff --git a/include/linux/timer.h b/include/linux/timer.h index f68a333230cc..21c8fc901ade 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -196,6 +196,9 @@ extern void set_timer_slack(struct timer_list *time, int slack_hz); */ extern unsigned long get_next_timer_interrupt(unsigned long now); +/* To be used from cpusets, only */ +extern void timer_quiesce_cpu(void *cpup); + /* * Timer-statistics info: */ diff --git a/kernel/time/timer.c b/kernel/time/timer.c index afc591b8070a..faa999557fe4 100644 --- a/kernel/time/timer.c +++ b/kernel/time/timer.c @@ -1677,51 +1677,81 @@ static int init_timers_cpu(int cpu) return 0; } -#ifdef CONFIG_HOTPLUG_CPU -static void migrate_timer_list(struct tvec_base *new_base, struct list_head *head) +#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_CPUSETS) +static void migrate_timer_list(struct tvec_base *new_base, + struct list_head *head, bool remove_pinned) { struct timer_list *timer; + struct list_head pinned_list; + int is_pinned; + + INIT_LIST_HEAD(&pinned_list); while (!list_empty(head)) { timer = list_first_entry(head, struct timer_list, entry); + + is_pinned = tbase_get_pinned(timer->base); + if (!remove_pinned && is_pinned) { + list_move_tail(&timer->entry, &pinned_list); + continue; + } + /* We ignore the accounting on the dying cpu */ - detach_timer(timer, false); + detach_if_pending(timer, tbase_get_base(timer->base), false); + timer_set_base(timer, new_base); internal_add_timer(new_base, timer); } + + if (!list_empty(&pinned_list)) + list_splice_tail(&pinned_list, head); } -static void migrate_timers(int cpu) +/* Migrate timers from 'cpu' to this_cpu */ +static void __migrate_timers(int cpu, 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(tvec_bases, cpu); new_base = get_local_var(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); BUG_ON(old_base->running_timer); 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); } spin_unlock(&old_base->lock); - spin_unlock_irq(&new_base->lock); + spin_unlock_irqrestore(&new_base->lock, flags); put_local_var(tvec_bases); } +#endif /* CONFIG_HOTPLUG_CPU || CONFIG_CPUSETS */ + +#ifdef CONFIG_HOTPLUG_CPU +static void migrate_timers(int cpu) +{ + BUG_ON(cpu_online(cpu)); + __migrate_timers(cpu, true); +} #endif /* CONFIG_HOTPLUG_CPU */ static int timer_cpu_notify(struct notifier_block *self, |