diff options
author | Oleg Nesterov <oleg@redhat.com> | 2014-12-10 18:55:20 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-10 20:41:18 -0500 |
commit | 482a3767e5087f6e6ad2486a6655aaa5f3d59301 (patch) | |
tree | 53e54ff3b94db9a691f413684e27612347dbce96 /kernel/exit.c | |
parent | ad9e206aefa56788b676ebcd6329e828f40d2238 (diff) |
exit: reparent: call forget_original_parent() under tasklist_lock
Shift "release dead children" loop from forget_original_parent() to its
caller, exit_notify(). It is safe to reap them even if our parent reaps
us right after we drop tasklist_lock, those children no longer have any
connection to the exiting task.
And this allows us to avoid write_lock_irq(tasklist_lock) right after it
was released by forget_original_parent(), we can simply call it with
tasklist_lock held.
While at it, move the comment about forget_original_parent() up to
this function.
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 | 47 |
1 files changed, 23 insertions, 24 deletions
diff --git a/kernel/exit.c b/kernel/exit.c index 063745699f7f..8061891ddd9b 100644 --- a/kernel/exit.c +++ b/kernel/exit.c | |||
@@ -560,19 +560,26 @@ static void reparent_leader(struct task_struct *father, struct task_struct *p, | |||
560 | kill_orphaned_pgrp(p, father); | 560 | kill_orphaned_pgrp(p, father); |
561 | } | 561 | } |
562 | 562 | ||
563 | static void forget_original_parent(struct task_struct *father) | 563 | /* |
564 | * This does two things: | ||
565 | * | ||
566 | * A. Make init inherit all the child processes | ||
567 | * B. Check to see if any process groups have become orphaned | ||
568 | * as a result of our exiting, and if they have any stopped | ||
569 | * jobs, send them a SIGHUP and then a SIGCONT. (POSIX 3.2.2.2) | ||
570 | */ | ||
571 | static void forget_original_parent(struct task_struct *father, | ||
572 | struct list_head *dead) | ||
564 | { | 573 | { |
565 | struct task_struct *p, *t, *n, *reaper; | 574 | struct task_struct *p, *t, *reaper; |
566 | LIST_HEAD(dead_children); | ||
567 | 575 | ||
568 | write_lock_irq(&tasklist_lock); | ||
569 | if (unlikely(!list_empty(&father->ptraced))) | 576 | if (unlikely(!list_empty(&father->ptraced))) |
570 | exit_ptrace(father, &dead_children); | 577 | exit_ptrace(father, dead); |
571 | 578 | ||
572 | /* Can drop and reacquire tasklist_lock */ | 579 | /* Can drop and reacquire tasklist_lock */ |
573 | reaper = find_child_reaper(father); | 580 | reaper = find_child_reaper(father); |
574 | if (list_empty(&father->children)) | 581 | if (list_empty(&father->children)) |
575 | goto unlock; | 582 | return; |
576 | 583 | ||
577 | reaper = find_new_reaper(father, reaper); | 584 | reaper = find_new_reaper(father, reaper); |
578 | list_for_each_entry(p, &father->children, sibling) { | 585 | list_for_each_entry(p, &father->children, sibling) { |
@@ -590,16 +597,9 @@ static void forget_original_parent(struct task_struct *father) | |||
590 | * notify anyone anything has happened. | 597 | * notify anyone anything has happened. |
591 | */ | 598 | */ |
592 | if (!same_thread_group(reaper, father)) | 599 | if (!same_thread_group(reaper, father)) |
593 | reparent_leader(father, p, &dead_children); | 600 | reparent_leader(father, p, dead); |
594 | } | 601 | } |
595 | list_splice_tail_init(&father->children, &reaper->children); | 602 | list_splice_tail_init(&father->children, &reaper->children); |
596 | unlock: | ||
597 | write_unlock_irq(&tasklist_lock); | ||
598 | |||
599 | list_for_each_entry_safe(p, n, &dead_children, ptrace_entry) { | ||
600 | list_del_init(&p->ptrace_entry); | ||
601 | release_task(p); | ||
602 | } | ||
603 | } | 603 | } |
604 | 604 | ||
605 | /* | 605 | /* |
@@ -609,18 +609,12 @@ static void forget_original_parent(struct task_struct *father) | |||
609 | static void exit_notify(struct task_struct *tsk, int group_dead) | 609 | static void exit_notify(struct task_struct *tsk, int group_dead) |
610 | { | 610 | { |
611 | bool autoreap; | 611 | bool autoreap; |
612 | 612 | struct task_struct *p, *n; | |
613 | /* | 613 | LIST_HEAD(dead); |
614 | * This does two things: | ||
615 | * | ||
616 | * A. Make init inherit all the child processes | ||
617 | * B. Check to see if any process groups have become orphaned | ||
618 | * as a result of our exiting, and if they have any stopped | ||
619 | * jobs, send them a SIGHUP and then a SIGCONT. (POSIX 3.2.2.2) | ||
620 | */ | ||
621 | forget_original_parent(tsk); | ||
622 | 614 | ||
623 | write_lock_irq(&tasklist_lock); | 615 | write_lock_irq(&tasklist_lock); |
616 | forget_original_parent(tsk, &dead); | ||
617 | |||
624 | if (group_dead) | 618 | if (group_dead) |
625 | kill_orphaned_pgrp(tsk->group_leader, NULL); | 619 | kill_orphaned_pgrp(tsk->group_leader, NULL); |
626 | 620 | ||
@@ -644,6 +638,11 @@ static void exit_notify(struct task_struct *tsk, int group_dead) | |||
644 | wake_up_process(tsk->signal->group_exit_task); | 638 | wake_up_process(tsk->signal->group_exit_task); |
645 | write_unlock_irq(&tasklist_lock); | 639 | write_unlock_irq(&tasklist_lock); |
646 | 640 | ||
641 | list_for_each_entry_safe(p, n, &dead, ptrace_entry) { | ||
642 | list_del_init(&p->ptrace_entry); | ||
643 | release_task(p); | ||
644 | } | ||
645 | |||
647 | /* If the process is dead, release it - nobody will wait for it */ | 646 | /* If the process is dead, release it - nobody will wait for it */ |
648 | if (autoreap) | 647 | if (autoreap) |
649 | release_task(tsk); | 648 | release_task(tsk); |