Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Anton Vorontsov and committed by
Linus Torvalds
cb79295e f7505d64

+27
+1
include/linux/cpu.h
··· 177 177 #define hotcpu_notifier(fn, pri) cpu_notifier(fn, pri) 178 178 #define register_hotcpu_notifier(nb) register_cpu_notifier(nb) 179 179 #define unregister_hotcpu_notifier(nb) unregister_cpu_notifier(nb) 180 + void clear_tasks_mm_cpumask(int cpu); 180 181 int cpu_down(unsigned int cpu); 181 182 182 183 #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
+26
kernel/cpu.c
··· 10 10 #include <linux/sched.h> 11 11 #include <linux/unistd.h> 12 12 #include <linux/cpu.h> 13 + #include <linux/oom.h> 14 + #include <linux/rcupdate.h> 13 15 #include <linux/export.h> 14 16 #include <linux/kthread.h> 15 17 #include <linux/stop_machine.h> ··· 174 172 cpu_maps_update_done(); 175 173 } 176 174 EXPORT_SYMBOL(unregister_cpu_notifier); 175 + 176 + void clear_tasks_mm_cpumask(int cpu) 177 + { 178 + struct task_struct *p; 179 + 180 + /* 181 + * This function is called after the cpu is taken down and marked 182 + * offline, so its not like new tasks will ever get this cpu set in 183 + * their mm mask. -- Peter Zijlstra 184 + * Thus, we may use rcu_read_lock() here, instead of grabbing 185 + * full-fledged tasklist_lock. 186 + */ 187 + rcu_read_lock(); 188 + for_each_process(p) { 189 + struct task_struct *t; 190 + 191 + t = find_lock_task_mm(p); 192 + if (!t) 193 + continue; 194 + cpumask_clear_cpu(cpu, mm_cpumask(t->mm)); 195 + task_unlock(t); 196 + } 197 + rcu_read_unlock(); 198 + } 177 199 178 200 static inline void check_for_tasks(int cpu) 179 201 {