#include <linux/personality.h>
#include <linux/tty.h>
#include <linux/namespace.h>
+#include <linux/key.h>
#include <linux/security.h>
#include <linux/cpu.h>
#include <linux/acct.h>
#include <linux/mount.h>
#include <linux/proc_fs.h>
#include <linux/mempolicy.h>
+#include <linux/cpuset.h>
+#include <linux/syscalls.h>
+#include <linux/signal.h>
#include <linux/vs_limit.h>
+#include <linux/vs_network.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
int getrusage(struct task_struct *, int, struct rusage __user *);
+static void exit_mm(struct task_struct * tsk);
+
static void __unhash_process(struct task_struct *p)
{
nr_threads--;
*/
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);
/*
spin_unlock(&p->proc_lock);
proc_pid_flush(proc_dentry);
release_thread(p);
+ if (p->vx_info)
+ release_vx_info(p->vx_info, p);
+ if (p->nx_info)
+ release_nx_info(p->nx_info, p);
put_task_struct(p);
p = leader;
do_each_task_pid(pgrp, PIDTYPE_PGID, p) {
if (p == ignored_task
- || p->state >= TASK_ZOMBIE
+ || p->exit_state
|| p->real_parent->pid == 1)
continue;
if (process_group(p->real_parent) != pgrp
}
/**
- * reparent_to_init() - Reparent the calling kernel thread to the init task.
+ * reparent_to_init - Reparent the calling kernel thread to the init task.
*
* If a kernel thread is launched as a result of a system call, or if
* it ever exits, it should generally reparent itself to init so that
*
* NOTE that reparent_to_init() gives the caller full capabilities.
*/
-void reparent_to_init(void)
+static inline void reparent_to_init(void)
{
write_lock_irq(&tasklist_lock);
/* rt_priority? */
/* signals? */
security_task_reparent_to_init(current);
- memcpy(current->rlim, init_task.rlim, sizeof(*(current->rlim)));
+ memcpy(current->signal->rlim, init_task.signal->rlim,
+ sizeof(current->signal->rlim));
atomic_inc(&(INIT_USER->__count));
- switch_uid(INIT_USER);
-
write_unlock_irq(&tasklist_lock);
+ switch_uid(INIT_USER);
}
void __set_special_pids(pid_t session, pid_t pgrp)
*/
int allow_signal(int sig)
{
- if (sig < 1 || sig > _NSIG)
+ if (!valid_signal(sig) || sig < 1)
return -EINVAL;
spin_lock_irq(¤t->sighand->siglock);
int disallow_signal(int sig)
{
- if (sig < 1 || sig > _NSIG)
+ if (!valid_signal(sig) || sig < 1)
return -EINVAL;
spin_lock_irq(¤t->sighand->siglock);
exit_mm(current);
set_special_pids(1, 1);
+ down(&tty_sem);
current->signal->tty = NULL;
+ up(&tty_sem);
/* Block and flush all signals */
sigfillset(&blocked);
struct file * file = xchg(&files->fd[i], NULL);
if (file)
filp_close(file, files);
- // vx_openfd_dec(i);
+ vx_openfd_dec(i);
}
i++;
set >>= 1;
* Turn us into a lazy TLB process if we
* aren't already..
*/
-static inline void __exit_mm(struct task_struct * tsk)
+static void exit_mm(struct task_struct * tsk)
{
struct mm_struct *mm = tsk->mm;
mmput(mm);
}
-void exit_mm(struct task_struct *tsk)
-{
- __exit_mm(tsk);
-}
-
-EXPORT_SYMBOL(exit_mm);
-
static inline void choose_new_parent(task_t *p, task_t *reaper, task_t *child_reaper)
{
/*
* 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->exit_state >= EXIT_ZOMBIE);
p->real_parent = reaper;
- if (p->parent == p->real_parent)
- BUG();
}
static inline void reparent_thread(task_t *p, task_t *father, int traced)
/* We don't want people slaying init. */
if (p->exit_signal != -1)
p->exit_signal = SIGCHLD;
- p->self_exec_id++;
if (p->pdeath_signal)
/* We already hold the tasklist_lock here. */
/* 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) {
* a normal stop since it's no longer being
* traced.
*/
- p->state = TASK_STOPPED;
+ ptrace_untrace(p);
}
}
reaper = child_reaper;
break;
}
- } while (reaper->state >= TASK_ZOMBIE);
+ } while (reaper->exit_state);
/*
* There are only two places where our children can be:
} 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);
}
* 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) {
struct task_struct *t;
struct list_head ptrace_dead, *_p, *_n;
- if (signal_pending(tsk) && !tsk->signal->group_exit
+ if (signal_pending(tsk) && !(tsk->signal->flags & SIGNAL_GROUP_EXIT)
&& !thread_group_empty(tsk)) {
/*
* This occurs when there was a race between our exit
do_notify_parent(tsk, SIGCHLD);
}
- state = TASK_ZOMBIE;
- if (tsk->exit_signal == -1 && tsk->ptrace == 0)
- state = TASK_DEAD;
- tsk->state = state;
-
- /*
- * Clear these here so that update_process_times() won't try to deliver
- * itimer, profile or rlimit signals to this task while it is in late exit.
- */
- tsk->it_virt_value = 0;
- tsk->it_prof_value = 0;
- tsk->rlim[RLIMIT_CPU].rlim_cur = RLIM_INFINITY;
+ state = EXIT_ZOMBIE;
+ if (tsk->exit_signal == -1 &&
+ (likely(tsk->ptrace == 0) ||
+ unlikely(tsk->parent->signal->flags & SIGNAL_GROUP_EXIT)))
+ state = EXIT_DEAD;
+ tsk->exit_state = state;
write_unlock_irq(&tasklist_lock);
}
/* 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. */
tsk->flags |= PF_DEAD;
}
-asmlinkage NORET_TYPE void do_exit(long code)
+fastcall NORET_TYPE void do_exit(long code)
{
struct task_struct *tsk = current;
+ int group_dead;
profile_task_exit(tsk);
panic("Attempted to kill init!");
if (tsk->io_context)
exit_io_context();
+
+ if (unlikely(current->ptrace & PT_TRACE_EXIT)) {
+ current->ptrace_message = code;
+ ptrace_notify((PTRACE_EVENT_EXIT << 8) | SIGTRAP);
+ }
+
tsk->flags |= PF_EXITING;
- del_timer_sync(&tsk->real_timer);
+
+ /*
+ * Make sure we don't try to process any timer firings
+ * while we are already exiting.
+ */
+ tsk->it_virt_expires = cputime_zero;
+ tsk->it_prof_expires = cputime_zero;
+ tsk->it_sched_expires = 0;
if (unlikely(in_atomic()))
printk(KERN_INFO "note: %s[%d] exited with preempt_count %d\n",
current->comm, current->pid,
preempt_count());
- if (unlikely(current->ptrace & PT_TRACE_EXIT)) {
- current->ptrace_message = code;
- ptrace_notify((PTRACE_EVENT_EXIT << 8) | SIGTRAP);
- }
-
- acct_process(code);
- __exit_mm(tsk);
+ acct_update_integrals(tsk);
+ update_mem_hiwater(tsk);
+ group_dead = atomic_dec_and_test(&tsk->signal->live);
+ if (group_dead)
+ acct_process(code);
+ exit_mm(tsk);
exit_sem(tsk);
__exit_files(tsk);
__exit_fs(tsk);
exit_namespace(tsk);
exit_thread();
+ cpuset_exit(tsk);
+ exit_keys(tsk);
- if (tsk->signal->leader)
+ if (group_dead && tsk->signal->leader)
disassociate_ctty(1);
module_put(tsk->thread_info->exec_domain->module);
mpol_free(tsk->mempolicy);
tsk->mempolicy = NULL;
#endif
+
+ BUG_ON(!(current->flags & PF_DEAD));
schedule();
BUG();
/* Avoid "noreturn function does return". */
for (;;) ;
}
+EXPORT_SYMBOL_GPL(do_exit);
+
NORET_TYPE void complete_and_exit(struct completion *comp, long code)
{
if (comp)
task_t fastcall *next_thread(const task_t *p)
{
-#ifdef CONFIG_SMP
- if (!p->sighand)
- BUG();
- if (!spin_is_locked(&p->sighand->siglock) &&
- !rwlock_is_locked(&tasklist_lock))
- BUG();
-#endif
return pid_task(p->pids[PIDTYPE_TGID].pid_list.next, PIDTYPE_TGID);
}
{
BUG_ON(exit_code & 0x80); /* core dumps don't get here */
- if (current->signal->group_exit)
+ if (current->signal->flags & SIGNAL_GROUP_EXIT)
exit_code = current->signal->group_exit_code;
else if (!thread_group_empty(current)) {
struct signal_struct *const sig = current->signal;
struct sighand_struct *const sighand = current->sighand;
read_lock(&tasklist_lock);
spin_lock_irq(&sighand->siglock);
- if (sig->group_exit)
+ if (sig->flags & SIGNAL_GROUP_EXIT)
/* Another thread got here before we took the lock. */
exit_code = sig->group_exit_code;
else {
- sig->group_exit = 1;
+ sig->flags = SIGNAL_GROUP_EXIT;
sig->group_exit_code = exit_code;
zap_other_threads(current);
}
}
/*
- * 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.
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;
* 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)) {
* here reaping other children at the same time.
*/
spin_lock_irq(&p->parent->sighand->siglock);
- p->parent->signal->cutime +=
- p->utime + p->signal->utime + p->signal->cutime;
- p->parent->signal->cstime +=
- p->stime + p->signal->stime + p->signal->cstime;
+ p->parent->signal->cutime =
+ cputime_add(p->parent->signal->cutime,
+ cputime_add(p->utime,
+ cputime_add(p->signal->utime,
+ p->signal->cutime)));
+ p->parent->signal->cstime =
+ cputime_add(p->parent->signal->cstime,
+ cputime_add(p->stime,
+ cputime_add(p->signal->stime,
+ p->signal->cstime)));
p->parent->signal->cmin_flt +=
p->min_flt + p->signal->min_flt + p->signal->cmin_flt;
p->parent->signal->cmaj_flt +=
/*
* 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);
retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0;
- status = p->signal->group_exit
+ status = (p->signal->flags & SIGNAL_GROUP_EXIT)
? p->signal->group_exit_code : p->exit_code;
if (!retval && stat_addr)
retval = put_user(status, stat_addr);
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;
/* 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
/*
* 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)) {
/*
* 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.
write_unlock_irq(&tasklist_lock);
bail_ref:
put_task_struct(p);
- read_lock(&tasklist_lock);
- return 0;
+ /*
+ * We are returning to the wait loop without having successfully
+ * removed the process and having released the lock. We cannot
+ * continue, since the "p" task pointer is potentially stale.
+ *
+ * Return -EAGAIN, and do_wait() will restart the loop from the
+ * beginning. Do _not_ re-acquire the lock.
+ */
+ return -EAGAIN;
}
/* move to end of parent's list to avoid starvation */
return retval;
}
+/*
+ * Handle do_wait work for one task in a live, non-stopped state.
+ * 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.
+ */
+static int wait_task_continued(task_t *p, int noreap,
+ struct siginfo __user *infop,
+ int __user *stat_addr, struct rusage __user *ru)
+{
+ int retval;
+ pid_t pid;
+ uid_t uid;
+
+ if (unlikely(!p->signal))
+ return 0;
+
+ if (!(p->signal->flags & SIGNAL_STOP_CONTINUED))
+ return 0;
+
+ spin_lock_irq(&p->sighand->siglock);
+ /* Re-check with the lock held. */
+ if (!(p->signal->flags & SIGNAL_STOP_CONTINUED)) {
+ spin_unlock_irq(&p->sighand->siglock);
+ return 0;
+ }
+ if (!noreap)
+ p->signal->flags &= ~SIGNAL_STOP_CONTINUED;
+ spin_unlock_irq(&p->sighand->siglock);
+
+ pid = p->pid;
+ uid = p->uid;
+ get_task_struct(p);
+ read_unlock(&tasklist_lock);
+
+ if (!infop) {
+ retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0;
+ put_task_struct(p);
+ if (!retval && stat_addr)
+ retval = put_user(0xffff, stat_addr);
+ if (!retval)
+ retval = p->pid;
+ } else {
+ retval = wait_noreap_copyout(p, pid, uid,
+ CLD_CONTINUED, SIGCONT,
+ infop, ru);
+ BUG_ON(retval == 0);
+ }
+
+ return retval;
+}
+
+
+static inline int my_ptrace_child(struct task_struct *p)
+{
+ if (!(p->ptrace & PT_PTRACED))
+ return 0;
+ if (!(p->ptrace & PT_ATTACHED))
+ return 1;
+ /*
+ * This child was PTRACE_ATTACH'd. We should be seeing it only if
+ * we are the attacher. If we are the real parent, this is a race
+ * inside ptrace_attach. It is waiting for the tasklist_lock,
+ * which we have to switch the parent links, but has already set
+ * the flags in p->ptrace.
+ */
+ return (p->parent != p->real_parent);
+}
+
static long do_wait(pid_t pid, int options, struct siginfo __user *infop,
int __user *stat_addr, struct rusage __user *ru)
{
struct task_struct *tsk;
int flag, retval;
- add_wait_queue(¤t->wait_chldexit,&wait);
+ add_wait_queue(¤t->signal->wait_chldexit,&wait);
repeat:
+ /*
+ * We will set this flag if we see any child that might later
+ * match our criteria, even if we are not able to reap it yet.
+ */
flag = 0;
current->state = TASK_INTERRUPTIBLE;
read_lock(&tasklist_lock);
ret = eligible_child(pid, options, p);
if (!ret)
continue;
- flag = 1;
switch (p->state) {
case TASK_TRACED:
- if (!(p->ptrace & PT_PTRACED))
+ if (!my_ptrace_child(p))
continue;
/*FALLTHROUGH*/
case TASK_STOPPED:
+ /*
+ * It's stopped now, so it might later
+ * continue, exit, or stop again.
+ */
+ flag = 1;
if (!(options & WUNTRACED) &&
- !(p->ptrace & PT_PTRACED))
+ !my_ptrace_child(p))
continue;
retval = wait_task_stopped(p, ret == 2,
(options & WNOWAIT),
infop,
stat_addr, ru);
+ if (retval == -EAGAIN)
+ goto repeat;
if (retval != 0) /* He released the lock. */
goto end;
break;
- case TASK_ZOMBIE:
+ 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:
/*
- * Eligible but we cannot release it yet:
+ * It's running now, so it might later
+ * exit, stop, or stop and then continue.
*/
- if (ret == 2)
- goto check_continued;
- if (!likely(options & WEXITED))
+ flag = 1;
+ if (!unlikely(options & WCONTINUED))
continue;
- retval = wait_task_zombie(
+ retval = wait_task_continued(
p, (options & WNOWAIT),
infop, stat_addr, ru);
if (retval != 0) /* He released the lock. */
goto end;
break;
- case TASK_DEAD:
- continue;
- default:
-check_continued:
- if (!unlikely(options & WCONTINUED))
- continue;
- if (unlikely(!p->signal))
- continue;
- spin_lock_irq(&p->sighand->siglock);
- if (p->signal->stop_state < 0) {
- pid_t pid;
- uid_t uid;
-
- if (!(options & WNOWAIT))
- p->signal->stop_state = 0;
- spin_unlock_irq(&p->sighand->siglock);
- pid = p->pid;
- uid = p->uid;
- get_task_struct(p);
- read_unlock(&tasklist_lock);
- retval = wait_noreap_copyout(p, pid,
- uid, CLD_CONTINUED,
- SIGCONT, infop, ru);
- BUG_ON(retval == 0);
- goto end;
- }
- spin_unlock_irq(&p->sighand->siglock);
- break;
}
}
if (!flag) {
retval = -ECHILD;
end:
current->state = TASK_RUNNING;
- remove_wait_queue(¤t->wait_chldexit,&wait);
+ remove_wait_queue(¤t->signal->wait_chldexit,&wait);
if (infop) {
if (retval > 0)
retval = 0;
{
long ret;
- if (options & ~(WNOHANG|WUNTRACED|__WNOTHREAD|__WCLONE|__WALL))
+ if (options & ~(WNOHANG|WUNTRACED|WCONTINUED|
+ __WNOTHREAD|__WCLONE|__WALL))
return -EINVAL;
ret = do_wait(pid, options | WEXITED, NULL, stat_addr, ru);