diff options
author | Oleg Nesterov <oleg@redhat.com> | 2014-12-11 02:55:17 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-11 04:41:18 +0300 |
commit | ad9e206aefa56788b676ebcd6329e828f40d2238 (patch) | |
tree | 860bf8397db9bab9bbf96f5d5f37c522f77a1f22 | |
parent | c9dc05bfdb3f7fd7c00f3cbd33816c99d2cb9029 (diff) | |
download | linux-ad9e206aefa56788b676ebcd6329e828f40d2238.tar.xz |
exit: reparent: avoid find_new_reaper() if no children
Now that pid_ns logic was isolated we can change forget_original_parent()
to return right after find_child_reaper() when father->children is empty,
there is nothing to reparent in this case.
In particular this avoids find_alive_thread() and this can help if the
whole process exits and it has a lot of PF_EXITING threads at the start of
the thread list, this can easily lead to O(nr_threads ** 2) iterations.
Trivial test case (tested under KVM, 2 CPUs):
static void *tfunc(void *arg)
{
pause();
return NULL;
}
static int child(unsigned int nt)
{
pthread_t pt;
while (nt--)
assert(pthread_create(&pt, NULL, tfunc, NULL) == 0);
pthread_kill(pt, SIGTRAP);
pause();
return 0;
}
int main(int argc, const char *argv[])
{
int stat;
unsigned int nf = atoi(argv[1]);
unsigned int nt = atoi(argv[2]);
while (nf--) {
if (!fork())
return child(nt);
wait(&stat);
assert(stat == SIGTRAP);
}
return 0;
}
$ time ./test 16 16536 shows:
real user sys
- 5m37.628s 0m4.437s 8m5.560s
+ 0m50.032s 0m7.130s 1m4.927s
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Cc: Aaron Tomlin <atomlin@redhat.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Sterling Alexander <stalexan@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | kernel/exit.c | 3 |
1 files changed, 3 insertions, 0 deletions
diff --git a/kernel/exit.c b/kernel/exit.c index b0f482f5daf9..063745699f7f 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -571,6 +571,8 @@ static void forget_original_parent(struct task_struct *father) /* Can drop and reacquire tasklist_lock */ reaper = find_child_reaper(father); + if (list_empty(&father->children)) + goto unlock; reaper = find_new_reaper(father, reaper); list_for_each_entry(p, &father->children, sibling) { @@ -591,6 +593,7 @@ static void forget_original_parent(struct task_struct *father) reparent_leader(father, p, &dead_children); } list_splice_tail_init(&father->children, &reaper->children); + unlock: write_unlock_irq(&tasklist_lock); list_for_each_entry_safe(p, n, &dead_children, ptrace_entry) { |