diff options
author | Oleg Nesterov <oleg@redhat.com> | 2009-07-16 19:09:36 -0400 |
---|---|---|
committer | James Morris <jmorris@namei.org> | 2009-07-16 19:09:36 -0400 |
commit | d2e3ee9b29f5de5b01e611b04e6fb29760589b01 (patch) | |
tree | ad73b17c2c6b83050a948685a0a3af3f907de67f | |
parent | 713c0ecdb888e9ef6f085e828555455c5916b07f (diff) |
kernel: fix is_single_threaded
- Fix the comment, is_single_threaded(p) actually means that nobody shares
->mm with p.
I think this helper should be renamed, and it should not have arguments.
With or without this patch it must not be used unless p == current,
otherwise we can't safely use p->signal or p->mm.
- "if (atomic_read(&p->signal->count) != 1)" is not right when we have a
zombie group leader, use signal->live instead.
- Add PF_KTHREAD check to skip kernel threads which may borrow p->mm,
otherwise we can return the wrong "false".
- Use for_each_process() instead of do_each_thread(), all threads must use
the same ->mm.
- Use down_write(mm->mmap_sem) + rcu_read_lock() instead of tasklist_lock
to iterate over the process list. If there is another CLONE_VM process
it can't pass exit_mm() which takes the same mm->mmap_sem. We can miss
a freshly forked CLONE_VM task, but this doesn't matter because we must
see its parent and return false.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Cc: David Howells <dhowells@redhat.com>
Cc: James Morris <jmorris@namei.org>
Cc: Roland McGrath <roland@redhat.com>
Cc: Stephen Smalley <sds@tycho.nsa.gov>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: James Morris <jmorris@namei.org>
-rw-r--r-- | lib/is_single_threaded.c | 62 |
1 files changed, 36 insertions, 26 deletions
diff --git a/lib/is_single_threaded.c b/lib/is_single_threaded.c index f1ed2fe76c65..2762516e0a5e 100644 --- a/lib/is_single_threaded.c +++ b/lib/is_single_threaded.c | |||
@@ -12,34 +12,44 @@ | |||
12 | 12 | ||
13 | #include <linux/sched.h> | 13 | #include <linux/sched.h> |
14 | 14 | ||
15 | /** | 15 | /* |
16 | * is_single_threaded - Determine if a thread group is single-threaded or not | 16 | * Returns true if the task does not share ->mm with another thread/process. |
17 | * @p: A task in the thread group in question | ||
18 | * | ||
19 | * This returns true if the thread group to which a task belongs is single | ||
20 | * threaded, false if it is not. | ||
21 | */ | 17 | */ |
22 | bool is_single_threaded(struct task_struct *p) | 18 | bool is_single_threaded(struct task_struct *task) |
23 | { | 19 | { |
24 | struct task_struct *g, *t; | 20 | struct mm_struct *mm = task->mm; |
25 | struct mm_struct *mm = p->mm; | 21 | struct task_struct *p, *t; |
26 | 22 | bool ret; | |
27 | if (atomic_read(&p->signal->count) != 1) | 23 | |
28 | goto no; | 24 | might_sleep(); |
29 | 25 | ||
30 | if (atomic_read(&p->mm->mm_users) != 1) { | 26 | if (atomic_read(&task->signal->live) != 1) |
31 | read_lock(&tasklist_lock); | 27 | return false; |
32 | do_each_thread(g, t) { | ||
33 | if (t->mm == mm && t != p) | ||
34 | goto no_unlock; | ||
35 | } while_each_thread(g, t); | ||
36 | read_unlock(&tasklist_lock); | ||
37 | } | ||
38 | 28 | ||
39 | return true; | 29 | if (atomic_read(&mm->mm_users) == 1) |
30 | return true; | ||
31 | |||
32 | ret = false; | ||
33 | down_write(&mm->mmap_sem); | ||
34 | rcu_read_lock(); | ||
35 | for_each_process(p) { | ||
36 | if (unlikely(p->flags & PF_KTHREAD)) | ||
37 | continue; | ||
38 | if (unlikely(p == task->group_leader)) | ||
39 | continue; | ||
40 | |||
41 | t = p; | ||
42 | do { | ||
43 | if (unlikely(t->mm == mm)) | ||
44 | goto found; | ||
45 | if (likely(t->mm)) | ||
46 | break; | ||
47 | } while_each_thread(p, t); | ||
48 | } | ||
49 | ret = true; | ||
50 | found: | ||
51 | rcu_read_unlock(); | ||
52 | up_write(&mm->mmap_sem); | ||
40 | 53 | ||
41 | no_unlock: | 54 | return ret; |
42 | read_unlock(&tasklist_lock); | ||
43 | no: | ||
44 | return false; | ||
45 | } | 55 | } |