From d3069b4dd0767b4e24debdb21b632b2f8dd72474 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 18 Oct 2004 09:12:06 -0700 Subject: [PATCH] [PATCH] fix & clean up zombie/dead task handling & preemption This patch fixes all the preempt-after-task->state-is-TASK_DEAD problems we had. Right now, the moment procfs does a down() that sleeps in proc_pid_flush() [it could] our TASK_DEAD state is zapped and we might be back to TASK_RUNNING to and we trigger this assert: schedule(); BUG(); /* Avoid "noreturn function does return". */ for (;;) ; I have split out TASK_ZOMBIE and TASK_DEAD into a separate p->exit_state field, to allow the detaching of exit-signal/parent/wait-handling from descheduling a dead task. Dead-task freeing is done via PF_DEAD. Tested the patch on x86 SMP and UP, but all architectures should work fine. Signed-off-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/alpha/kernel/ptrace.c | 2 +- arch/arm/kernel/ptrace.c | 2 +- arch/arm26/kernel/ptrace.c | 2 +- arch/cris/arch-v10/kernel/ptrace.c | 2 +- arch/h8300/kernel/ptrace.c | 2 +- arch/i386/kernel/ptrace.c | 2 +- arch/ia64/kernel/perfmon.c | 2 +- arch/ia64/kernel/ptrace.c | 2 +- arch/m32r/kernel/ptrace.c | 2 +- arch/m68k/kernel/ptrace.c | 2 +- arch/m68knommu/kernel/ptrace.c | 2 +- arch/mips/kernel/ptrace.c | 2 +- arch/mips/kernel/ptrace32.c | 2 +- arch/parisc/kernel/ptrace.c | 2 +- arch/ppc/kernel/ptrace.c | 2 +- arch/ppc64/kernel/ptrace.c | 2 +- arch/ppc64/kernel/ptrace32.c | 2 +- arch/s390/kernel/ptrace.c | 2 +- arch/sh/kernel/ptrace.c | 2 +- arch/sh64/kernel/ptrace.c | 2 +- arch/sparc/kernel/ptrace.c | 2 +- arch/sparc64/kernel/ptrace.c | 2 +- arch/um/kernel/ptrace.c | 2 +- arch/um/kernel/tt/process_kern.c | 6 +-- arch/v850/kernel/ptrace.c | 2 +- arch/x86_64/kernel/ptrace.c | 2 +- fs/exec.c | 8 +-- fs/proc/array.c | 14 ++--- include/linux/sched.h | 5 +- kernel/exit.c | 85 +++++++++++++++++------------- kernel/fork.c | 3 +- kernel/power/process.c | 4 +- kernel/ptrace.c | 2 +- kernel/sched.c | 12 +++-- kernel/signal.c | 4 +- kernel/timer.c | 2 +- 36 files changed, 104 insertions(+), 91 deletions(-) diff --git a/arch/alpha/kernel/ptrace.c b/arch/alpha/kernel/ptrace.c index 9c21fbd4f34f9..d005831615744 100644 --- a/arch/alpha/kernel/ptrace.c +++ b/arch/alpha/kernel/ptrace.c @@ -355,7 +355,7 @@ do_sys_ptrace(long request, long pid, long addr, long data, */ case PTRACE_KILL: ret = 0; - if (child->state == TASK_ZOMBIE) + if (child->exit_state == EXIT_ZOMBIE) break; child->exit_code = SIGKILL; /* make sure single-step breakpoint is gone. */ diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index 7aeb80f8551a2..c82f70b5d66cd 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c @@ -677,7 +677,7 @@ static int do_ptrace(int request, struct task_struct *child, long addr, long dat /* make sure single-step breakpoint is gone. */ child->ptrace &= ~PT_SINGLESTEP; ptrace_cancel_bpt(child); - if (child->state != TASK_ZOMBIE) { + if (child->exit_state != EXIT_ZOMBIE) { child->exit_code = SIGKILL; wake_up_process(child); } diff --git a/arch/arm26/kernel/ptrace.c b/arch/arm26/kernel/ptrace.c index e2ffb20074654..8597ab3574fe2 100644 --- a/arch/arm26/kernel/ptrace.c +++ b/arch/arm26/kernel/ptrace.c @@ -614,7 +614,7 @@ static int do_ptrace(int request, struct task_struct *child, long addr, long dat /* make sure single-step breakpoint is gone. */ child->ptrace &= ~PT_SINGLESTEP; ptrace_cancel_bpt(child); - if (child->state != TASK_ZOMBIE) { + if (child->exit_state != EXIT_ZOMBIE) { child->exit_code = SIGKILL; wake_up_process(child); } diff --git a/arch/cris/arch-v10/kernel/ptrace.c b/arch/cris/arch-v10/kernel/ptrace.c index 3c7e0a51a2824..55fd631224b3f 100644 --- a/arch/cris/arch-v10/kernel/ptrace.c +++ b/arch/cris/arch-v10/kernel/ptrace.c @@ -176,7 +176,7 @@ sys_ptrace(long request, long pid, long addr, long data) case PTRACE_KILL: ret = 0; - if (child->state == TASK_ZOMBIE) + if (child->exit_state == EXIT_ZOMBIE) break; child->exit_code = SIGKILL; diff --git a/arch/h8300/kernel/ptrace.c b/arch/h8300/kernel/ptrace.c index b60d234d1d276..5f19d774a2887 100644 --- a/arch/h8300/kernel/ptrace.c +++ b/arch/h8300/kernel/ptrace.c @@ -192,7 +192,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) case PTRACE_KILL: { ret = 0; - if (child->state == TASK_ZOMBIE) /* already dead */ + if (child->exit_state == EXIT_ZOMBIE) /* already dead */ break; child->exit_code = SIGKILL; h8300_disable_trace(child); diff --git a/arch/i386/kernel/ptrace.c b/arch/i386/kernel/ptrace.c index e0c9a03739a80..77c48ce67e08b 100644 --- a/arch/i386/kernel/ptrace.c +++ b/arch/i386/kernel/ptrace.c @@ -390,7 +390,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) long tmp; ret = 0; - if (child->state == TASK_ZOMBIE) /* already dead */ + if (child->exit_state == EXIT_ZOMBIE) /* already dead */ break; child->exit_code = SIGKILL; clear_tsk_thread_flag(child, TIF_SINGLESTEP); diff --git a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c index e86722b7fce70..2685e6ea8bbd2 100644 --- a/arch/ia64/kernel/perfmon.c +++ b/arch/ia64/kernel/perfmon.c @@ -2593,7 +2593,7 @@ pfm_task_incompatible(pfm_context_t *ctx, struct task_struct *task) return -EINVAL; } - if (task->state == TASK_ZOMBIE) { + if (task->exit_state == EXIT_ZOMBIE) { DPRINT(("cannot attach to zombie task [%d]\n", task->pid)); return -EBUSY; } diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c index d8ff70dfd84b6..1e515083e23fc 100644 --- a/arch/ia64/kernel/ptrace.c +++ b/arch/ia64/kernel/ptrace.c @@ -1422,7 +1422,7 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data, * sigkill. Perhaps it should be put in the status * that it wants to exit. */ - if (child->state == TASK_ZOMBIE) /* already dead */ + if (child->exit_state == EXIT_ZOMBIE) /* already dead */ goto out_tsk; child->exit_code = SIGKILL; diff --git a/arch/m32r/kernel/ptrace.c b/arch/m32r/kernel/ptrace.c index ab4137a3398a0..6d58f972b35c5 100644 --- a/arch/m32r/kernel/ptrace.c +++ b/arch/m32r/kernel/ptrace.c @@ -713,7 +713,7 @@ do_ptrace(long request, struct task_struct *child, long addr, long data) ret = 0; unregister_all_debug_traps(child); invalidate_cache(); - if (child->state == TASK_ZOMBIE) /* already dead */ + if (child->exit_state == EXIT_ZOMBIE) /* already dead */ break; child->exit_code = SIGKILL; wake_up_process(child); diff --git a/arch/m68k/kernel/ptrace.c b/arch/m68k/kernel/ptrace.c index b043a7f9409fe..0beb53333ba35 100644 --- a/arch/m68k/kernel/ptrace.c +++ b/arch/m68k/kernel/ptrace.c @@ -277,7 +277,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) long tmp; ret = 0; - if (child->state == TASK_ZOMBIE) /* already dead */ + if (child->exit_state == EXIT_ZOMBIE) /* already dead */ break; child->exit_code = SIGKILL; /* make sure the single step bit is not set. */ diff --git a/arch/m68knommu/kernel/ptrace.c b/arch/m68knommu/kernel/ptrace.c index 773dc91e46e9d..15cf79080b151 100644 --- a/arch/m68knommu/kernel/ptrace.c +++ b/arch/m68knommu/kernel/ptrace.c @@ -264,7 +264,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) long tmp; ret = 0; - if (child->state == TASK_ZOMBIE) /* already dead */ + if (child->exit_state == EXIT_ZOMBIE) /* already dead */ break; child->exit_code = SIGKILL; /* make sure the single step bit is not set. */ diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 6e8511390a579..1ce7bd1509408 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -277,7 +277,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) */ case PTRACE_KILL: ret = 0; - if (child->state == TASK_ZOMBIE) /* already dead */ + if (child->exit_state == EXIT_ZOMBIE) /* already dead */ break; child->exit_code = SIGKILL; wake_up_process(child); diff --git a/arch/mips/kernel/ptrace32.c b/arch/mips/kernel/ptrace32.c index df7a51cceeb07..799919d9beaa5 100644 --- a/arch/mips/kernel/ptrace32.c +++ b/arch/mips/kernel/ptrace32.c @@ -262,7 +262,7 @@ asmlinkage int sys32_ptrace(int request, int pid, int addr, int data) */ case PTRACE_KILL: ret = 0; - if (child->state == TASK_ZOMBIE) /* already dead */ + if (child->exit_state == EXIT_ZOMBIE) /* already dead */ break; child->exit_code = SIGKILL; wake_up_process(child); diff --git a/arch/parisc/kernel/ptrace.c b/arch/parisc/kernel/ptrace.c index 598aadd36aae9..a9200a3ae6d8e 100644 --- a/arch/parisc/kernel/ptrace.c +++ b/arch/parisc/kernel/ptrace.c @@ -303,7 +303,7 @@ long sys_ptrace(long request, pid_t pid, long addr, long data) * that it wants to exit. */ DBG(("sys_ptrace(KILL)\n")); - if (child->state == TASK_ZOMBIE) /* already dead */ + if (child->exit_state == EXIT_ZOMBIE) /* already dead */ goto out_tsk; child->exit_code = SIGKILL; goto out_wake_notrap; diff --git a/arch/ppc/kernel/ptrace.c b/arch/ppc/kernel/ptrace.c index 5cf5624e90d11..118d97ed10fd7 100644 --- a/arch/ppc/kernel/ptrace.c +++ b/arch/ppc/kernel/ptrace.c @@ -377,7 +377,7 @@ int sys_ptrace(long request, long pid, long addr, long data) */ case PTRACE_KILL: { ret = 0; - if (child->state == TASK_ZOMBIE) /* already dead */ + if (child->exit_state == EXIT_ZOMBIE) /* already dead */ break; child->exit_code = SIGKILL; /* make sure the single step bit is not set. */ diff --git a/arch/ppc64/kernel/ptrace.c b/arch/ppc64/kernel/ptrace.c index aeead8375a5f1..0a4011f58f2f3 100644 --- a/arch/ppc64/kernel/ptrace.c +++ b/arch/ppc64/kernel/ptrace.c @@ -182,7 +182,7 @@ int sys_ptrace(long request, long pid, long addr, long data) */ case PTRACE_KILL: { ret = 0; - if (child->state == TASK_ZOMBIE) /* already dead */ + if (child->exit_state == EXIT_ZOMBIE) /* already dead */ break; child->exit_code = SIGKILL; /* make sure the single step bit is not set. */ diff --git a/arch/ppc64/kernel/ptrace32.c b/arch/ppc64/kernel/ptrace32.c index e0ab44dc1fc4d..ee81b1b776ccd 100644 --- a/arch/ppc64/kernel/ptrace32.c +++ b/arch/ppc64/kernel/ptrace32.c @@ -314,7 +314,7 @@ int sys32_ptrace(long request, long pid, unsigned long addr, unsigned long data) */ case PTRACE_KILL: { ret = 0; - if (child->state == TASK_ZOMBIE) /* already dead */ + if (child->exit_state == EXIT_ZOMBIE) /* already dead */ break; child->exit_code = SIGKILL; /* make sure the single step bit is not set. */ diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index 26b89e55d9c1b..1acb2d8932b17 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c @@ -626,7 +626,7 @@ do_ptrace(struct task_struct *child, long request, long addr, long data) * perhaps it should be put in the status that it wants to * exit. */ - if (child->state == TASK_ZOMBIE) /* already dead */ + if (child->exit_state == EXIT_ZOMBIE) /* already dead */ return 0; child->exit_code = SIGKILL; /* make sure the single step bit is not set. */ diff --git a/arch/sh/kernel/ptrace.c b/arch/sh/kernel/ptrace.c index 017826912cc5b..d14810a459d97 100644 --- a/arch/sh/kernel/ptrace.c +++ b/arch/sh/kernel/ptrace.c @@ -217,7 +217,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) */ case PTRACE_KILL: { ret = 0; - if (child->state == TASK_ZOMBIE) /* already dead */ + if (child->exit_state == EXIT_ZOMBIE) /* already dead */ break; child->exit_code = SIGKILL; wake_up_process(child); diff --git a/arch/sh64/kernel/ptrace.c b/arch/sh64/kernel/ptrace.c index c48bc4a27b44b..f27c696b3e008 100644 --- a/arch/sh64/kernel/ptrace.c +++ b/arch/sh64/kernel/ptrace.c @@ -257,7 +257,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) */ case PTRACE_KILL: { ret = 0; - if (child->state == TASK_ZOMBIE) /* already dead */ + if (child->exit_state == EXIT_ZOMBIE) /* already dead */ break; child->exit_code = SIGKILL; wake_up_process(child); diff --git a/arch/sparc/kernel/ptrace.c b/arch/sparc/kernel/ptrace.c index 589652ea05b80..62b8fd1dba7fc 100644 --- a/arch/sparc/kernel/ptrace.c +++ b/arch/sparc/kernel/ptrace.c @@ -567,7 +567,7 @@ asmlinkage void do_ptrace(struct pt_regs *regs) * exit. */ case PTRACE_KILL: { - if (child->state == TASK_ZOMBIE) { /* already dead */ + if (child->exit_state == EXIT_ZOMBIE) { /* already dead */ pt_succ_return(regs, 0); goto out_tsk; } diff --git a/arch/sparc64/kernel/ptrace.c b/arch/sparc64/kernel/ptrace.c index fd73a3ab8f1fc..721ab5ba859fa 100644 --- a/arch/sparc64/kernel/ptrace.c +++ b/arch/sparc64/kernel/ptrace.c @@ -559,7 +559,7 @@ asmlinkage void do_ptrace(struct pt_regs *regs) * exit. */ case PTRACE_KILL: { - if (child->state == TASK_ZOMBIE) { /* already dead */ + if (child->exit_state == EXIT_ZOMBIE) { /* already dead */ pt_succ_return(regs, 0); goto out_tsk; } diff --git a/arch/um/kernel/ptrace.c b/arch/um/kernel/ptrace.c index 40912d18cb2fa..b4efb669be413 100644 --- a/arch/um/kernel/ptrace.c +++ b/arch/um/kernel/ptrace.c @@ -158,7 +158,7 @@ int sys_ptrace(long request, long pid, long addr, long data) */ case PTRACE_KILL: { ret = 0; - if (child->state == TASK_ZOMBIE) /* already dead */ + if (child->exit_state == EXIT_ZOMBIE) /* already dead */ break; child->exit_code = SIGKILL; wake_up_process(child); diff --git a/arch/um/kernel/tt/process_kern.c b/arch/um/kernel/tt/process_kern.c index 6224afcd84d7e..d547435ee5a5c 100644 --- a/arch/um/kernel/tt/process_kern.c +++ b/arch/um/kernel/tt/process_kern.c @@ -65,7 +65,7 @@ void *switch_to_tt(void *prev, void *next, void *last) panic("write of switch_pipe failed, err = %d", -err); reading = 1; - if((from->state == TASK_ZOMBIE) || (from->state == TASK_DEAD)) + if((from->exit_state == EXIT_ZOMBIE) || (from->exit_state == EXIT_DEAD)) os_kill_process(os_getpid(), 0); err = os_read_file(from->thread.mode.tt.switch_pipe[0], &c, sizeof(c)); @@ -80,8 +80,8 @@ void *switch_to_tt(void *prev, void *next, void *last) * in case it has not already killed itself. */ prev_sched = current->thread.prev_sched; - if((prev_sched->state == TASK_ZOMBIE) || - (prev_sched->state == TASK_DEAD)) + if((prev_sched->exit_state == EXIT_ZOMBIE) || + (prev_sched->exit_state == EXIT_DEAD)) os_kill_process(prev_sched->thread.mode.tt.extern_pid, 1); /* This works around a nasty race with 'jail'. If we are switching diff --git a/arch/v850/kernel/ptrace.c b/arch/v850/kernel/ptrace.c index c0ee8f0b7f4c4..ab60fa953e747 100644 --- a/arch/v850/kernel/ptrace.c +++ b/arch/v850/kernel/ptrace.c @@ -232,7 +232,7 @@ int sys_ptrace(long request, long pid, long addr, long data) */ case PTRACE_KILL: rval = 0; - if (child->state == TASK_ZOMBIE) /* already dead */ + if (child->exit_state == EXIT_ZOMBIE) /* already dead */ break; child->exit_code = SIGKILL; wake_up_process(child); diff --git a/arch/x86_64/kernel/ptrace.c b/arch/x86_64/kernel/ptrace.c index 73ff5494a6c15..ebd621e96787b 100644 --- a/arch/x86_64/kernel/ptrace.c +++ b/arch/x86_64/kernel/ptrace.c @@ -395,7 +395,7 @@ asmlinkage long sys_ptrace(long request, long pid, unsigned long addr, long data long tmp; ret = 0; - if (child->state == TASK_ZOMBIE) /* already dead */ + if (child->exit_state == EXIT_ZOMBIE) /* already dead */ break; clear_tsk_thread_flag(child, TIF_SINGLESTEP); child->exit_code = SIGKILL; diff --git a/fs/exec.c b/fs/exec.c index 125a04e3692b9..e715541b2db4f 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -632,14 +632,14 @@ static inline int de_thread(struct task_struct *tsk) if (current->pid != current->tgid) { struct task_struct *leader = current->group_leader, *parent; struct dentry *proc_dentry1, *proc_dentry2; - unsigned long state, ptrace; + unsigned long exit_state, ptrace; /* * Wait for the thread group leader to be a zombie. * It should already be zombie at this point, most * of the time. */ - while (leader->state != TASK_ZOMBIE) + while (leader->exit_state != EXIT_ZOMBIE) yield(); spin_lock(&leader->proc_lock); @@ -683,7 +683,7 @@ static inline int de_thread(struct task_struct *tsk) list_del(¤t->tasks); list_add_tail(¤t->tasks, &init_task.tasks); current->exit_signal = SIGCHLD; - state = leader->state; + exit_state = leader->exit_state; write_unlock_irq(&tasklist_lock); spin_unlock(&leader->proc_lock); @@ -691,7 +691,7 @@ static inline int de_thread(struct task_struct *tsk) proc_pid_flush(proc_dentry1); proc_pid_flush(proc_dentry2); - if (state != TASK_ZOMBIE) + if (exit_state != EXIT_ZOMBIE) BUG(); release_task(leader); } diff --git a/fs/proc/array.c b/fs/proc/array.c index d05dab0697d31..5c289c73a00e3 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -137,13 +137,13 @@ static const char *task_state_array[] = { static inline const char * get_task_state(struct task_struct *tsk) { - unsigned int state = tsk->state & (TASK_RUNNING | - TASK_INTERRUPTIBLE | - TASK_UNINTERRUPTIBLE | - TASK_ZOMBIE | - TASK_DEAD | - TASK_STOPPED | - TASK_TRACED); + unsigned int state = (tsk->state & (TASK_RUNNING | + TASK_INTERRUPTIBLE | + TASK_UNINTERRUPTIBLE | + TASK_STOPPED | + TASK_TRACED)) | + (tsk->exit_state & (EXIT_ZOMBIE | + EXIT_DEAD)); const char **p = &task_state_array[0]; while (state) { diff --git a/include/linux/sched.h b/include/linux/sched.h index 69648ed7fd2b1..dc3f297a726d5 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -108,8 +108,8 @@ extern unsigned long nr_iowait(void); #define TASK_UNINTERRUPTIBLE 2 #define TASK_STOPPED 4 #define TASK_TRACED 8 -#define TASK_ZOMBIE 16 -#define TASK_DEAD 32 +#define EXIT_ZOMBIE 16 +#define EXIT_DEAD 32 #define __set_task_state(tsk, state_value) \ do { (tsk)->state = (state_value); } while (0) @@ -564,6 +564,7 @@ struct task_struct { /* task state */ struct linux_binfmt *binfmt; + long exit_state; int exit_code, exit_signal; int pdeath_signal; /* The signal sent when the parent dies */ /* ??? */ diff --git a/kernel/exit.c b/kernel/exit.c index 031e17486eee2..55d8533925245 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -76,7 +76,7 @@ repeat: */ zap_leader = 0; leader = p->group_leader; - if (leader != p && thread_group_empty(leader) && leader->state == TASK_ZOMBIE) { + if (leader != p && thread_group_empty(leader) && leader->exit_state == EXIT_ZOMBIE) { BUG_ON(leader->exit_signal == -1); do_notify_parent(leader, leader->exit_signal); /* @@ -158,7 +158,7 @@ static int will_become_orphaned_pgrp(int pgrp, task_t *ignored_task) do_each_task_pid(pgrp, PIDTYPE_PGID, p) { if (p == ignored_task - || p->state >= TASK_ZOMBIE + || p->exit_state >= EXIT_ZOMBIE || p->real_parent->pid == 1) continue; if (process_group(p->real_parent) != pgrp @@ -519,7 +519,7 @@ static inline void choose_new_parent(task_t *p, task_t *reaper, task_t *child_re * Make sure we're not reparenting to ourselves and that * the parent is not a zombie. */ - BUG_ON(p == reaper || reaper->state >= TASK_ZOMBIE); + BUG_ON(p == reaper || reaper->state >= EXIT_ZOMBIE || reaper->exit_state >= EXIT_ZOMBIE); p->real_parent = reaper; if (p->parent == p->real_parent) BUG(); @@ -554,7 +554,7 @@ static inline void reparent_thread(task_t *p, task_t *father, int traced) /* If we'd notified the old parent about this child's death, * also notify the new parent. */ - if (p->state == TASK_ZOMBIE && p->exit_signal != -1 && + if (p->exit_state == EXIT_ZOMBIE && p->exit_signal != -1 && thread_group_empty(p)) do_notify_parent(p, p->exit_signal); else if (p->state == TASK_TRACED) { @@ -602,7 +602,7 @@ static inline void forget_original_parent(struct task_struct * father, reaper = child_reaper; break; } - } while (reaper->state >= TASK_ZOMBIE); + } while (reaper->exit_state >= EXIT_ZOMBIE); /* * There are only two places where our children can be: @@ -628,7 +628,7 @@ static inline void forget_original_parent(struct task_struct * father, } else { /* reparent ptraced task to its real parent */ __ptrace_unlink (p); - if (p->state == TASK_ZOMBIE && p->exit_signal != -1 && + if (p->exit_state == EXIT_ZOMBIE && p->exit_signal != -1 && thread_group_empty(p)) do_notify_parent(p, p->exit_signal); } @@ -639,7 +639,7 @@ static inline void forget_original_parent(struct task_struct * father, * zombie forever since we prevented it from self-reap itself * while it was being traced by us, to be able to see it in wait4. */ - if (unlikely(ptrace && p->state == TASK_ZOMBIE && p->exit_signal == -1)) + if (unlikely(ptrace && p->exit_state == EXIT_ZOMBIE && p->exit_signal == -1)) list_add(&p->ptrace_list, to_release); } list_for_each_safe(_p, _n, &father->ptrace_children) { @@ -752,10 +752,10 @@ static void exit_notify(struct task_struct *tsk) do_notify_parent(tsk, SIGCHLD); } - state = TASK_ZOMBIE; + state = EXIT_ZOMBIE; if (tsk->exit_signal == -1 && tsk->ptrace == 0) - state = TASK_DEAD; - tsk->state = state; + state = EXIT_DEAD; + tsk->exit_state = state; /* * Clear these here so that update_process_times() won't try to deliver @@ -773,7 +773,7 @@ static void exit_notify(struct task_struct *tsk) } /* If the process is dead, release it - nobody will wait for it */ - if (state == TASK_DEAD) + if (state == EXIT_DEAD) release_task(tsk); /* PF_DEAD causes final put_task_struct after we schedule. */ @@ -830,6 +830,8 @@ asmlinkage NORET_TYPE void do_exit(long code) mpol_free(tsk->mempolicy); tsk->mempolicy = NULL; #endif + + BUG_ON(!(current->flags & PF_DEAD)); schedule(); BUG(); /* Avoid "noreturn function does return". */ @@ -973,7 +975,7 @@ static int wait_noreap_copyout(task_t *p, pid_t pid, uid_t uid, } /* - * Handle sys_wait4 work for one task in state TASK_ZOMBIE. We hold + * Handle sys_wait4 work for one task in state EXIT_ZOMBIE. We hold * read_lock(&tasklist_lock) on entry. If we return zero, we still hold * the lock and this task is uninteresting. If we return nonzero, we have * released the lock and the system call should return. @@ -992,7 +994,7 @@ static int wait_task_zombie(task_t *p, int noreap, int exit_code = p->exit_code; int why, status; - if (unlikely(p->state != TASK_ZOMBIE)) + if (unlikely(p->exit_state != EXIT_ZOMBIE)) return 0; if (unlikely(p->exit_signal == -1 && p->ptrace == 0)) return 0; @@ -1013,9 +1015,9 @@ static int wait_task_zombie(task_t *p, int noreap, * Try to move the task's state to DEAD * only one thread is allowed to do this: */ - state = xchg(&p->state, TASK_DEAD); - if (state != TASK_ZOMBIE) { - BUG_ON(state != TASK_DEAD); + state = xchg(&p->exit_state, EXIT_DEAD); + if (state != EXIT_ZOMBIE) { + BUG_ON(state != EXIT_DEAD); return 0; } if (unlikely(p->exit_signal == -1 && p->ptrace == 0)) { @@ -1060,7 +1062,7 @@ static int wait_task_zombie(task_t *p, int noreap, /* * Now we are sure this task is interesting, and no other - * thread can reap it because we set its state to TASK_DEAD. + * thread can reap it because we set its state to EXIT_DEAD. */ read_unlock(&tasklist_lock); @@ -1092,7 +1094,8 @@ static int wait_task_zombie(task_t *p, int noreap, if (!retval && infop) retval = put_user(p->uid, &infop->si_uid); if (retval) { - p->state = TASK_ZOMBIE; + // TODO: is this safe? + p->exit_state = EXIT_ZOMBIE; return retval; } retval = p->pid; @@ -1101,7 +1104,8 @@ static int wait_task_zombie(task_t *p, int noreap, /* Double-check with lock held. */ if (p->real_parent != p->parent) { __ptrace_unlink(p); - p->state = TASK_ZOMBIE; + // TODO: is this safe? + p->exit_state = EXIT_ZOMBIE; /* * If this is not a detached task, notify the parent. * If it's still not detached after that, don't release @@ -1172,13 +1176,13 @@ static int wait_task_stopped(task_t *p, int delayed_group_leader, int noreap, /* * This uses xchg to be atomic with the thread resuming and setting * it. It must also be done with the write lock held to prevent a - * race with the TASK_ZOMBIE case. + * race with the EXIT_ZOMBIE case. */ exit_code = xchg(&p->exit_code, 0); - if (unlikely(p->state >= TASK_ZOMBIE)) { + if (unlikely(p->exit_state >= EXIT_ZOMBIE)) { /* * The task resumed and then died. Let the next iteration - * catch it in TASK_ZOMBIE. Note that exit_code might + * catch it in EXIT_ZOMBIE. Note that exit_code might * already be zero here if it resumed and did _exit(0). * The task itself is dead and won't touch exit_code again; * other processors in this function are locked out. @@ -1339,23 +1343,28 @@ repeat: if (retval != 0) /* He released the lock. */ goto end; break; - case TASK_ZOMBIE: - /* - * Eligible but we cannot release it yet: - */ - if (ret == 2) - goto check_continued; - if (!likely(options & WEXITED)) - continue; - retval = wait_task_zombie( - p, (options & WNOWAIT), - infop, stat_addr, ru); - if (retval != 0) /* He released the lock. */ - goto end; - break; - case TASK_DEAD: - continue; default: + // case EXIT_DEAD: + if (p->exit_state == EXIT_DEAD) + continue; + // case EXIT_ZOMBIE: + if (p->exit_state == EXIT_ZOMBIE) { + /* + * Eligible but we cannot release + * it yet: + */ + if (ret == 2) + goto check_continued; + if (!likely(options & WEXITED)) + continue; + retval = wait_task_zombie( + p, (options & WNOWAIT), + infop, stat_addr, ru); + /* He released the lock. */ + if (retval != 0) + goto end; + break; + } check_continued: if (!unlikely(options & WCONTINUED)) continue; diff --git a/kernel/fork.c b/kernel/fork.c index bd33d8a507d73..3020dccc548ff 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -86,7 +86,7 @@ EXPORT_SYMBOL(free_task); void __put_task_struct(struct task_struct *tsk) { - WARN_ON(!(tsk->state & (TASK_DEAD | TASK_ZOMBIE))); + WARN_ON(!(tsk->exit_state & (EXIT_DEAD | EXIT_ZOMBIE))); WARN_ON(atomic_read(&tsk->usage)); WARN_ON(tsk == current); @@ -1062,6 +1062,7 @@ static task_t *copy_process(unsigned long clone_flags, /* ok, now we should be set up.. */ p->exit_signal = (clone_flags & CLONE_THREAD) ? -1 : (clone_flags & CSIGNAL); p->pdeath_signal = 0; + p->exit_state = 0; /* Perform scheduler related setup */ sched_fork(p); diff --git a/kernel/power/process.c b/kernel/power/process.c index bda013de59a51..78d92dc6a1edc 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c @@ -23,8 +23,8 @@ static inline int freezeable(struct task_struct * p) { if ((p == current) || (p->flags & PF_NOFREEZE) || - (p->state == TASK_ZOMBIE) || - (p->state == TASK_DEAD) || + (p->exit_state == EXIT_ZOMBIE) || + (p->exit_state == EXIT_DEAD) || (p->state == TASK_STOPPED) || (p->state == TASK_TRACED)) return 0; diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 09ba057222c33..c3ac348e23685 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -163,7 +163,7 @@ int ptrace_detach(struct task_struct *child, unsigned int data) write_lock_irq(&tasklist_lock); __ptrace_unlink(child); /* .. and wake it up. */ - if (child->state != TASK_ZOMBIE) + if (child->exit_state != EXIT_ZOMBIE) wake_up_process(child); write_unlock_irq(&tasklist_lock); diff --git a/kernel/sched.c b/kernel/sched.c index 5dc237eb32a5a..8e5e2af64509a 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -1329,10 +1329,10 @@ static void finish_task_switch(task_t *prev) /* * A task struct has one reference for the use as "current". - * If a task dies, then it sets TASK_ZOMBIE in tsk->state and calls - * schedule one last time. The schedule call will never return, + * If a task dies, then it sets EXIT_ZOMBIE in tsk->exit_state and + * calls schedule one last time. The schedule call will never return, * and the scheduled task must drop that reference. - * The test for TASK_ZOMBIE must occur while the runqueue locks are + * The test for EXIT_ZOMBIE must occur while the runqueue locks are * still held, otherwise prev could be scheduled on another cpu, die * there before we look at prev->state, and then the reference would * be dropped twice. @@ -2489,7 +2489,7 @@ asmlinkage void __sched schedule(void) * schedule() atomically, we ignore that path for now. * Otherwise, whine if we are scheduling when we should not be. */ - if (likely(!(current->state & (TASK_DEAD | TASK_ZOMBIE)))) { + if (likely(!(current->exit_state & (EXIT_DEAD | EXIT_ZOMBIE)))) { if (unlikely(in_atomic())) { printk(KERN_ERR "scheduling while atomic: " "%s/0x%08x/%d\n", @@ -2531,6 +2531,8 @@ need_resched: spin_lock_irq(&rq->lock); + if (unlikely(current->flags & PF_DEAD)) + current->state = EXIT_DEAD; /* * if entering off of a kernel preemption go straight * to picking the next task. @@ -3920,7 +3922,7 @@ static void migrate_dead(unsigned int dead_cpu, task_t *tsk) struct runqueue *rq = cpu_rq(dead_cpu); /* Must be exiting, otherwise would be on tasklist. */ - BUG_ON(tsk->state != TASK_ZOMBIE && tsk->state != TASK_DEAD); + BUG_ON(tsk->exit_state != EXIT_ZOMBIE && tsk->exit_state != EXIT_DEAD); /* Cannot have done final schedule yet: would have vanished. */ BUG_ON(tsk->flags & PF_DEAD); diff --git a/kernel/signal.c b/kernel/signal.c index 9f6bd72bdd820..f67390806d736 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -914,7 +914,7 @@ __group_complete_signal(int sig, struct task_struct *p) * Don't bother zombies and stopped tasks (but * SIGKILL will punch through stopped state) */ - mask = TASK_DEAD | TASK_ZOMBIE | TASK_TRACED; + mask = EXIT_DEAD | EXIT_ZOMBIE | TASK_TRACED; if (sig != SIGKILL) mask |= TASK_STOPPED; @@ -1070,7 +1070,7 @@ void zap_other_threads(struct task_struct *p) /* * Don't bother with already dead threads */ - if (t->state & (TASK_ZOMBIE|TASK_DEAD)) + if (t->exit_state & (EXIT_ZOMBIE|EXIT_DEAD)) continue; /* diff --git a/kernel/timer.c b/kernel/timer.c index 0a390a67c0f22..e3c9b5fcd52f5 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -805,7 +805,7 @@ static inline void do_process_times(struct task_struct *p, psecs = (p->utime += user); psecs += (p->stime += system); - if (!unlikely(p->state & (TASK_DEAD|TASK_ZOMBIE)) && + if (p->signal && !unlikely(p->state & (EXIT_DEAD|EXIT_ZOMBIE)) && psecs / HZ >= p->signal->rlim[RLIMIT_CPU].rlim_cur) { /* Send SIGXCPU every second.. */ if (!(psecs % HZ)) -- 2.53.0