diff options
author | Oleg Nesterov <oleg@redhat.com> | 2014-12-10 18:55:17 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-10 20:41:18 -0500 |
commit | ad9e206aefa56788b676ebcd6329e828f40d2238 (patch) | |
tree | 860bf8397db9bab9bbf96f5d5f37c522f77a1f22 /kernel/exit.c | |
parent | c9dc05bfdb3f7fd7c00f3cbd33816c99d2cb9029 (diff) |
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>
Diffstat (limited to 'kernel/exit.c')
-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) | |||
571 | 571 | ||
572 | /* Can drop and reacquire tasklist_lock */ | 572 | /* Can drop and reacquire tasklist_lock */ |
573 | reaper = find_child_reaper(father); | 573 | reaper = find_child_reaper(father); |
574 | if (list_empty(&father->children)) | ||
575 | goto unlock; | ||
574 | 576 | ||
575 | reaper = find_new_reaper(father, reaper); | 577 | reaper = find_new_reaper(father, reaper); |
576 | list_for_each_entry(p, &father->children, sibling) { | 578 | list_for_each_entry(p, &father->children, sibling) { |
@@ -591,6 +593,7 @@ static void forget_original_parent(struct task_struct *father) | |||
591 | reparent_leader(father, p, &dead_children); | 593 | reparent_leader(father, p, &dead_children); |
592 | } | 594 | } |
593 | list_splice_tail_init(&father->children, &reaper->children); | 595 | list_splice_tail_init(&father->children, &reaper->children); |
596 | unlock: | ||
594 | write_unlock_irq(&tasklist_lock); | 597 | write_unlock_irq(&tasklist_lock); |
595 | 598 | ||
596 | list_for_each_entry_safe(p, n, &dead_children, ptrace_entry) { | 599 | list_for_each_entry_safe(p, n, &dead_children, ptrace_entry) { |