aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnton Vorontsov <anton.vorontsov@linaro.org>2012-05-31 19:26:22 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-05-31 20:49:29 -0400
commitcb79295e20a8088a2fd6a9b3cb5f2d889ec36b4d (patch)
treee749be4ebd059d9dd1ff109c1cecb7b63109d2e0
parentf7505d64f2db5da2d7d94873ddf2cd2524847061 (diff)
cpu: introduce clear_tasks_mm_cpumask() helper
Many architectures clear tasks' mm_cpumask like this: read_lock(&tasklist_lock); for_each_process(p) { if (p->mm) cpumask_clear_cpu(cpu, mm_cpumask(p->mm)); } read_unlock(&tasklist_lock); Depending on the context, the code above may have several problems, such as: 1. Working with task->mm w/o getting mm or grabing the task lock is dangerous as ->mm might disappear (exit_mm() assigns NULL under task_lock(), so tasklist lock is not enough). 2. Checking for process->mm is not enough because process' main thread may exit or detach its mm via use_mm(), but other threads may still have a valid mm. This patch implements a small helper function that does things correctly, i.e.: 1. We take the task's lock while whe handle its mm (we can't use get_task_mm()/mmput() pair as mmput() might sleep); 2. To catch exited main thread case, we use find_lock_task_mm(), which walks up all threads and returns an appropriate task (with task lock held). Also, Per Peter Zijlstra's idea, now we don't grab tasklist_lock in the new helper, instead we take the rcu read lock. We can do this because the function is called after the cpu is taken down and marked offline, so no new tasks will get this cpu set in their mm mask. Signed-off-by: Anton Vorontsov <anton.vorontsov@linaro.org> Cc: Richard Weinberger <richard@nod.at> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Russell King <rmk@arm.linux.org.uk> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: Mike Frysinger <vapier@gentoo.org> Cc: Paul Mundt <lethal@linux-sh.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--include/linux/cpu.h1
-rw-r--r--kernel/cpu.c26
2 files changed, 27 insertions, 0 deletions
diff --git a/include/linux/cpu.h b/include/linux/cpu.h
index 7230bb59a06f..2e9b9ebbeb78 100644
--- a/include/linux/cpu.h
+++ b/include/linux/cpu.h
@@ -177,6 +177,7 @@ extern void put_online_cpus(void);
177#define hotcpu_notifier(fn, pri) cpu_notifier(fn, pri) 177#define hotcpu_notifier(fn, pri) cpu_notifier(fn, pri)
178#define register_hotcpu_notifier(nb) register_cpu_notifier(nb) 178#define register_hotcpu_notifier(nb) register_cpu_notifier(nb)
179#define unregister_hotcpu_notifier(nb) unregister_cpu_notifier(nb) 179#define unregister_hotcpu_notifier(nb) unregister_cpu_notifier(nb)
180void clear_tasks_mm_cpumask(int cpu);
180int cpu_down(unsigned int cpu); 181int cpu_down(unsigned int cpu);
181 182
182#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE 183#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
diff --git a/kernel/cpu.c b/kernel/cpu.c
index 0e6353cf147a..0575197deb4a 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -10,6 +10,8 @@
10#include <linux/sched.h> 10#include <linux/sched.h>
11#include <linux/unistd.h> 11#include <linux/unistd.h>
12#include <linux/cpu.h> 12#include <linux/cpu.h>
13#include <linux/oom.h>
14#include <linux/rcupdate.h>
13#include <linux/export.h> 15#include <linux/export.h>
14#include <linux/kthread.h> 16#include <linux/kthread.h>
15#include <linux/stop_machine.h> 17#include <linux/stop_machine.h>
@@ -173,6 +175,30 @@ void __ref unregister_cpu_notifier(struct notifier_block *nb)
173} 175}
174EXPORT_SYMBOL(unregister_cpu_notifier); 176EXPORT_SYMBOL(unregister_cpu_notifier);
175 177
178void clear_tasks_mm_cpumask(int cpu)
179{
180 struct task_struct *p;
181
182 /*
183 * This function is called after the cpu is taken down and marked
184 * offline, so its not like new tasks will ever get this cpu set in
185 * their mm mask. -- Peter Zijlstra
186 * Thus, we may use rcu_read_lock() here, instead of grabbing
187 * full-fledged tasklist_lock.
188 */
189 rcu_read_lock();
190 for_each_process(p) {
191 struct task_struct *t;
192
193 t = find_lock_task_mm(p);
194 if (!t)
195 continue;
196 cpumask_clear_cpu(cpu, mm_cpumask(t->mm));
197 task_unlock(t);
198 }
199 rcu_read_unlock();
200}
201
176static inline void check_for_tasks(int cpu) 202static inline void check_for_tasks(int cpu)
177{ 203{
178 struct task_struct *p; 204 struct task_struct *p;