1 From 710fa174c245e3567701d8558e63b3f23e7d083a Mon Sep 17 00:00:00 2001
2 From: Thomas Gleixner <tglx@linutronix.de>
3 Date: Wed, 15 Jun 2011 12:36:06 +0200
4 Subject: [PATCH 101/366] hotplug: Lightweight get online cpus
6 get_online_cpus() is a heavy weight function which involves a global
7 mutex. migrate_disable() wants a simpler construct which prevents only
8 a CPU from going doing while a task is in a migrate disabled section.
10 Implement a per cpu lockless mechanism, which serializes only in the
11 real unplug case on a global mutex. That serialization affects only
12 tasks on the cpu which should be brought down.
14 Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
16 include/linux/cpu.h | 7 ++--
17 kernel/cpu.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++-
18 2 files changed, 122 insertions(+), 4 deletions(-)
20 diff --git a/include/linux/cpu.h b/include/linux/cpu.h
21 index 06c4b6f..493ef08 100644
22 --- a/include/linux/cpu.h
23 +++ b/include/linux/cpu.h
24 @@ -222,9 +222,6 @@ static inline void smpboot_thread_init(void)
25 #endif /* CONFIG_SMP */
26 extern struct bus_type cpu_subsys;
28 -static inline void pin_current_cpu(void) { }
29 -static inline void unpin_current_cpu(void) { }
31 #ifdef CONFIG_HOTPLUG_CPU
32 /* Stop CPUs going up and down. */
34 @@ -234,6 +231,8 @@ extern void get_online_cpus(void);
35 extern void put_online_cpus(void);
36 extern void cpu_hotplug_disable(void);
37 extern void cpu_hotplug_enable(void);
38 +extern void pin_current_cpu(void);
39 +extern void unpin_current_cpu(void);
40 #define hotcpu_notifier(fn, pri) cpu_notifier(fn, pri)
41 #define __hotcpu_notifier(fn, pri) __cpu_notifier(fn, pri)
42 #define register_hotcpu_notifier(nb) register_cpu_notifier(nb)
43 @@ -251,6 +250,8 @@ static inline void cpu_hotplug_done(void) {}
44 #define put_online_cpus() do { } while (0)
45 #define cpu_hotplug_disable() do { } while (0)
46 #define cpu_hotplug_enable() do { } while (0)
47 +static inline void pin_current_cpu(void) { }
48 +static inline void unpin_current_cpu(void) { }
49 #define hotcpu_notifier(fn, pri) do { (void)(fn); } while (0)
50 #define __hotcpu_notifier(fn, pri) do { (void)(fn); } while (0)
51 /* These aren't inline functions due to a GCC bug. */
52 diff --git a/kernel/cpu.c b/kernel/cpu.c
53 index 35bb9c6..cfac14c 100644
56 @@ -91,6 +91,100 @@ static struct {
57 #define cpuhp_lock_acquire() lock_map_acquire(&cpu_hotplug.dep_map)
58 #define cpuhp_lock_release() lock_map_release(&cpu_hotplug.dep_map)
61 + struct task_struct *unplug;
63 + struct completion synced;
66 +static DEFINE_PER_CPU(struct hotplug_pcp, hotplug_pcp);
69 + * pin_current_cpu - Prevent the current cpu from being unplugged
71 + * Lightweight version of get_online_cpus() to prevent cpu from being
72 + * unplugged when code runs in a migration disabled region.
74 + * Must be called with preemption disabled (preempt_count = 1)!
76 +void pin_current_cpu(void)
78 + struct hotplug_pcp *hp = this_cpu_ptr(&hotplug_pcp);
81 + if (!hp->unplug || hp->refcount || preempt_count() > 1 ||
82 + hp->unplug == current) {
87 + mutex_lock(&cpu_hotplug.lock);
88 + mutex_unlock(&cpu_hotplug.lock);
94 + * unpin_current_cpu - Allow unplug of current cpu
96 + * Must be called with preemption or interrupts disabled!
98 +void unpin_current_cpu(void)
100 + struct hotplug_pcp *hp = this_cpu_ptr(&hotplug_pcp);
102 + WARN_ON(hp->refcount <= 0);
104 + /* This is safe. sync_unplug_thread is pinned to this cpu */
105 + if (!--hp->refcount && hp->unplug && hp->unplug != current)
106 + wake_up_process(hp->unplug);
110 + * FIXME: Is this really correct under all circumstances ?
112 +static int sync_unplug_thread(void *data)
114 + struct hotplug_pcp *hp = data;
117 + hp->unplug = current;
118 + set_current_state(TASK_UNINTERRUPTIBLE);
119 + while (hp->refcount) {
120 + schedule_preempt_disabled();
121 + set_current_state(TASK_UNINTERRUPTIBLE);
123 + set_current_state(TASK_RUNNING);
125 + complete(&hp->synced);
130 + * Start the sync_unplug_thread on the target cpu and wait for it to
133 +static int cpu_unplug_begin(unsigned int cpu)
135 + struct hotplug_pcp *hp = &per_cpu(hotplug_pcp, cpu);
136 + struct task_struct *tsk;
138 + init_completion(&hp->synced);
139 + tsk = kthread_create(sync_unplug_thread, hp, "sync_unplug/%d\n", cpu);
141 + return (PTR_ERR(tsk));
142 + kthread_bind(tsk, cpu);
143 + wake_up_process(tsk);
144 + wait_for_completion(&hp->synced);
148 +static void cpu_unplug_done(unsigned int cpu)
150 + struct hotplug_pcp *hp = &per_cpu(hotplug_pcp, cpu);
155 void get_online_cpus(void)
157 @@ -340,13 +434,14 @@ static int take_cpu_down(void *_param)
158 /* Requires cpu_add_remove_lock to be held */
159 static int _cpu_down(unsigned int cpu, int tasks_frozen)
161 - int err, nr_calls = 0;
162 + int mycpu, err, nr_calls = 0;
163 void *hcpu = (void *)(long)cpu;
164 unsigned long mod = tasks_frozen ? CPU_TASKS_FROZEN : 0;
165 struct take_cpu_down_param tcd_param = {
169 + cpumask_var_t cpumask;
171 if (num_online_cpus() == 1)
173 @@ -354,7 +449,27 @@ static int _cpu_down(unsigned int cpu, int tasks_frozen)
174 if (!cpu_online(cpu))
177 + /* Move the downtaker off the unplug cpu */
178 + if (!alloc_cpumask_var(&cpumask, GFP_KERNEL))
180 + cpumask_andnot(cpumask, cpu_online_mask, cpumask_of(cpu));
181 + set_cpus_allowed_ptr(current, cpumask);
182 + free_cpumask_var(cpumask);
184 + mycpu = smp_processor_id();
185 + if (mycpu == cpu) {
186 + printk(KERN_ERR "Yuck! Still on unplug CPU\n!");
193 + err = cpu_unplug_begin(cpu);
195 + printk("cpu_unplug_begin(%d) failed\n", cpu);
199 err = __cpu_notify(CPU_DOWN_PREPARE | mod, hcpu, -1, &nr_calls);
201 @@ -427,6 +542,8 @@ static int _cpu_down(unsigned int cpu, int tasks_frozen)
202 check_for_tasks(cpu);
205 + cpu_unplug_done(cpu);
208 trace_sched_cpu_hotplug(cpu, err, 0);