Skip to content
Snippets Groups Projects
exit.c 41.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	 * hashed anymore:
    	 */
    	if (unlikely(!list_empty(&tsk->pi_state_list)))
    		exit_pi_state_list(tsk);
    	if (unlikely(current->pi_state_cache))
    		kfree(current->pi_state_cache);
    
    	 * Make sure we are holding no locks:
    
    	debug_check_no_locks_held(tsk);
    
    	/*
    	 * We can do this unlocked here. The futex code uses this flag
    	 * just to verify whether the pi state cleanup has been done
    	 * or not. In the worst case it loops once more.
    	 */
    	tsk->flags |= PF_EXITPIDONE;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	if (tsk->splice_pipe)
    		__free_pipe_info(tsk->splice_pipe);
    
    
    Coywolf Qi Hunt's avatar
    Coywolf Qi Hunt committed
    	preempt_disable();
    
    Oleg Nesterov's avatar
    Oleg Nesterov committed
    	/* causes final put_task_struct in finish_task_switch(). */
    
    	tsk->state = TASK_DEAD;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	schedule();
    	BUG();
    	/* Avoid "noreturn function does return".  */
    
    Alan Cox's avatar
    Alan Cox committed
    	for (;;)
    		cpu_relax();	/* For when BUG is null */
    
    EXPORT_SYMBOL_GPL(do_exit);
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    NORET_TYPE void complete_and_exit(struct completion *comp, long code)
    {
    	if (comp)
    		complete(comp);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	do_exit(code);
    }
    
    EXPORT_SYMBOL(complete_and_exit);
    
    asmlinkage long sys_exit(int error_code)
    {
    	do_exit((error_code&0xff)<<8);
    }
    
    /*
     * Take down every thread in the group.  This is called by fatal signals
     * as well as by sys_exit_group (below).
     */
    NORET_TYPE void
    do_group_exit(int exit_code)
    {
    	BUG_ON(exit_code & 0x80); /* core dumps don't get here */
    
    	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;
    		spin_lock_irq(&sighand->siglock);
    
    		if (signal_group_exit(sig))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			/* Another thread got here before we took the lock.  */
    			exit_code = sig->group_exit_code;
    		else {
    			sig->group_exit_code = exit_code;
    
    			sig->flags = SIGNAL_GROUP_EXIT;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			zap_other_threads(current);
    		}
    		spin_unlock_irq(&sighand->siglock);
    	}
    
    	do_exit(exit_code);
    	/* NOTREACHED */
    }
    
    /*
     * this kills every thread in the thread group. Note that any externally
     * wait4()-ing process will get the correct exit code - even if this
     * thread is not the thread group leader.
     */
    asmlinkage void sys_exit_group(int error_code)
    {
    	do_group_exit((error_code & 0xff) << 8);
    }
    
    
    static struct pid *task_pid_type(struct task_struct *task, enum pid_type type)
    {
    	struct pid *pid = NULL;
    	if (type == PIDTYPE_PID)
    		pid = task->pids[type].pid;
    	else if (type < PIDTYPE_MAX)
    		pid = task->group_leader->pids[type].pid;
    	return pid;
    }
    
    static int eligible_child(enum pid_type type, struct pid *pid, int options,
    			  struct task_struct *p)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	if (type < PIDTYPE_MAX) {
    		if (task_pid_type(p, type) != pid)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			return 0;
    	}
    
    	/*
    	 * Do not consider detached threads that are
    	 * not ptraced:
    	 */
    	if (p->exit_signal == -1 && !p->ptrace)
    		return 0;
    
    	/* Wait for all children (clone and not) if __WALL is set;
    	 * otherwise, wait for clone children *only* if __WCLONE is
    	 * set; otherwise, wait for non-clone children *only*.  (Note:
    	 * A "clone" child here is one that reports to its parent
    	 * using a signal other than SIGCHLD.) */
    	if (((p->exit_signal != SIGCHLD) ^ ((options & __WCLONE) != 0))
    	    && !(options & __WALL))
    		return 0;
    
    
    	err = security_task_wait(p);
    
    	if (likely(!err))
    		return 1;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	if (type != PIDTYPE_PID)
    
    		return 0;
    	/* This child was explicitly requested, abort */
    	read_unlock(&tasklist_lock);
    	return err;
    
    static int wait_noreap_copyout(struct task_struct *p, pid_t pid, uid_t uid,
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			       int why, int status,
    			       struct siginfo __user *infop,
    			       struct rusage __user *rusagep)
    {
    	int retval = rusagep ? getrusage(p, RUSAGE_BOTH, rusagep) : 0;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	put_task_struct(p);
    	if (!retval)
    		retval = put_user(SIGCHLD, &infop->si_signo);
    	if (!retval)
    		retval = put_user(0, &infop->si_errno);
    	if (!retval)
    		retval = put_user((short)why, &infop->si_code);
    	if (!retval)
    		retval = put_user(pid, &infop->si_pid);
    	if (!retval)
    		retval = put_user(uid, &infop->si_uid);
    	if (!retval)
    		retval = put_user(status, &infop->si_status);
    	if (!retval)
    		retval = pid;
    	return retval;
    }
    
    /*
     * 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.
     */
    
    static int wait_task_zombie(struct task_struct *p, int noreap,
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			    struct siginfo __user *infop,
    			    int __user *stat_addr, struct rusage __user *ru)
    {
    	unsigned long state;
    
    	int retval, status, traced;
    
    	pid_t pid = task_pid_nr_ns(p, current->nsproxy->pid_ns);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	if (unlikely(noreap)) {
    		uid_t uid = p->uid;
    		int exit_code = p->exit_code;
    		int why, status;
    
    		get_task_struct(p);
    		read_unlock(&tasklist_lock);
    		if ((exit_code & 0x7f) == 0) {
    			why = CLD_EXITED;
    			status = exit_code >> 8;
    		} else {
    			why = (exit_code & 0x80) ? CLD_DUMPED : CLD_KILLED;
    			status = exit_code & 0x7f;
    		}
    		return wait_noreap_copyout(p, pid, uid, why,
    					   status, infop, ru);
    	}
    
    	/*
    	 * Try to move the task's state to DEAD
    	 * only one thread is allowed to do this:
    	 */
    	state = xchg(&p->exit_state, EXIT_DEAD);
    	if (state != EXIT_ZOMBIE) {
    		BUG_ON(state != EXIT_DEAD);
    		return 0;
    	}
    
    
    	/* traced means p->ptrace, but not vice versa */
    	traced = (p->real_parent != p->parent);
    
    	if (likely(!traced)) {
    
    		struct signal_struct *psig;
    		struct signal_struct *sig;
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		/*
    		 * The resource counters for the group leader are in its
    		 * own task_struct.  Those for dead threads in the group
    		 * are in its signal_struct, as are those for the child
    		 * processes it has previously reaped.  All these
    		 * accumulate in the parent's signal_struct c* fields.
    		 *
    		 * We don't bother to take a lock here to protect these
    		 * p->signal fields, because they are only touched by
    		 * __exit_signal, which runs with tasklist_lock
    		 * write-locked anyway, and so is excluded here.  We do
    		 * need to protect the access to p->parent->signal fields,
    		 * as other threads in the parent group can be right
    		 * here reaping other children at the same time.
    		 */
    		spin_lock_irq(&p->parent->sighand->siglock);
    
    		psig = p->parent->signal;
    		sig = p->signal;
    		psig->cutime =
    			cputime_add(psig->cutime,
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			cputime_add(p->utime,
    
    			cputime_add(sig->utime,
    				    sig->cutime)));
    		psig->cstime =
    			cputime_add(psig->cstime,
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			cputime_add(p->stime,
    
    			cputime_add(sig->stime,
    				    sig->cstime)));
    
    		psig->cgtime =
    			cputime_add(psig->cgtime,
    			cputime_add(p->gtime,
    			cputime_add(sig->gtime,
    				    sig->cgtime)));
    
    		psig->cmin_flt +=
    			p->min_flt + sig->min_flt + sig->cmin_flt;
    		psig->cmaj_flt +=
    			p->maj_flt + sig->maj_flt + sig->cmaj_flt;
    		psig->cnvcsw +=
    			p->nvcsw + sig->nvcsw + sig->cnvcsw;
    		psig->cnivcsw +=
    			p->nivcsw + sig->nivcsw + sig->cnivcsw;
    
    		psig->cinblock +=
    			task_io_get_inblock(p) +
    			sig->inblock + sig->cinblock;
    		psig->coublock +=
    			task_io_get_oublock(p) +
    			sig->oublock + sig->coublock;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		spin_unlock_irq(&p->parent->sighand->siglock);
    	}
    
    	/*
    	 * Now we are sure this task is interesting, and no other
    	 * 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->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(SIGCHLD, &infop->si_signo);
    	if (!retval && infop)
    		retval = put_user(0, &infop->si_errno);
    	if (!retval && infop) {
    		int why;
    
    		if ((status & 0x7f) == 0) {
    			why = CLD_EXITED;
    			status >>= 8;
    		} else {
    			why = (status & 0x80) ? CLD_DUMPED : CLD_KILLED;
    			status &= 0x7f;
    		}
    		retval = put_user((short)why, &infop->si_code);
    		if (!retval)
    			retval = put_user(status, &infop->si_status);
    	}
    	if (!retval && infop)
    
    		retval = put_user(pid, &infop->si_pid);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (!retval && infop)
    		retval = put_user(p->uid, &infop->si_uid);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		write_lock_irq(&tasklist_lock);
    
    		/* We dropped tasklist, ptracer could die and untrace */
    		ptrace_unlink(p);
    		/*
    		 * If this is not a detached task, notify the parent.
    		 * If it's still not detached after that, don't release
    		 * it now.
    		 */
    		if (p->exit_signal != -1) {
    			do_notify_parent(p, p->exit_signal);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			if (p->exit_signal != -1) {
    
    				p->exit_state = EXIT_ZOMBIE;
    				p = NULL;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			}
    		}
    		write_unlock_irq(&tasklist_lock);
    	}
    	if (p != NULL)
    		release_task(p);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return retval;
    }
    
    /*
     * Handle sys_wait4 work for one task in state TASK_STOPPED.  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.
     */
    
    static int wait_task_stopped(struct task_struct *p,
    
    			     int noreap, struct siginfo __user *infop,
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			     int __user *stat_addr, struct rusage __user *ru)
    {
    
    	int retval, exit_code, why;
    	uid_t uid = 0; /* unneeded, required by compiler */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	exit_code = 0;
    	spin_lock_irq(&p->sighand->siglock);
    
    	if (unlikely(!task_is_stopped_or_traced(p)))
    		goto unlock_sig;
    
    
    	if (!(p->ptrace & PT_PTRACED) && p->signal->group_stop_count > 0)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		/*
    		 * A group stop is in progress and this is the group leader.
    		 * We won't report until all threads have stopped.
    		 */
    
    		goto unlock_sig;
    
    	exit_code = p->exit_code;
    	if (!exit_code)
    		goto unlock_sig;
    
    	if (!noreap)
    		p->exit_code = 0;
    
    	uid = p->uid;
    unlock_sig:
    	spin_unlock_irq(&p->sighand->siglock);
    	if (!exit_code)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		return 0;
    
    	/*
    	 * Now we are pretty sure this task is interesting.
    	 * Make sure it doesn't get reaped out from under us while we
    	 * give up the lock and then examine it below.  We don't want to
    	 * keep holding onto the tasklist_lock while we call getrusage and
    	 * possibly take page faults for user memory.
    	 */
    	get_task_struct(p);
    
    	pid = task_pid_nr_ns(p, current->nsproxy->pid_ns);
    	why = (p->ptrace & PT_PTRACED) ? CLD_TRAPPED : CLD_STOPPED;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	read_unlock(&tasklist_lock);
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		return wait_noreap_copyout(p, pid, uid,
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    					   infop, ru);
    
    	retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0;
    	if (!retval && stat_addr)
    		retval = put_user((exit_code << 8) | 0x7f, stat_addr);
    	if (!retval && infop)
    		retval = put_user(SIGCHLD, &infop->si_signo);
    	if (!retval && infop)
    		retval = put_user(0, &infop->si_errno);
    	if (!retval && infop)
    
    		retval = put_user(why, &infop->si_code);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (!retval && infop)
    		retval = put_user(exit_code, &infop->si_status);
    	if (!retval && infop)
    
    		retval = put_user(pid, &infop->si_pid);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (!retval && infop)
    
    		retval = put_user(uid, &infop->si_uid);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (!retval)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	put_task_struct(p);
    
    	BUG_ON(!retval);
    	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(struct task_struct *p, int noreap,
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			       struct siginfo __user *infop,
    			       int __user *stat_addr, struct rusage __user *ru)
    {
    	int retval;
    	pid_t pid;
    	uid_t uid;
    
    	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 = task_pid_nr_ns(p, current->nsproxy->pid_ns);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	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)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	} else {
    		retval = wait_noreap_copyout(p, pid, uid,
    					     CLD_CONTINUED, SIGCONT,
    					     infop, ru);
    		BUG_ON(retval == 0);
    	}
    
    	return retval;
    }
    
    
    static long do_wait(enum pid_type type, struct pid *pid, int options,
    		    struct siginfo __user *infop, int __user *stat_addr,
    		    struct rusage __user *ru)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	DECLARE_WAITQUEUE(wait, current);
    	struct task_struct *tsk;
    	int flag, retval;
    
    	add_wait_queue(&current->signal->wait_chldexit,&wait);
    repeat:
    
    	/* If there is nothing that can match our critier just get out */
    	retval = -ECHILD;
    	if ((type < PIDTYPE_MAX) && (!pid || hlist_empty(&pid->tasks[type])))
    		goto end;
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	/*
    	 * 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 = retval = 0;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	current->state = TASK_INTERRUPTIBLE;
    	read_lock(&tasklist_lock);
    	tsk = current;
    	do {
    		struct task_struct *p;
    
    
    		list_for_each_entry(p, &tsk->children, sibling) {
    
    			int ret = eligible_child(type, pid, options, p);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			if (!ret)
    				continue;
    
    
    			if (unlikely(ret < 0)) {
    
    				retval = ret;
    			} else if (task_is_stopped_or_traced(p)) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    				/*
    				 * It's stopped now, so it might later
    				 * continue, exit, or stop again.
    				 */
    				flag = 1;
    
    Oleg Nesterov's avatar
    Oleg Nesterov committed
    				if (!(p->ptrace & PT_PTRACED) &&
    				    !(options & WUNTRACED))
    					continue;
    
    				retval = wait_task_stopped(p,
    
    Matthew Wilcox's avatar
    Matthew Wilcox committed
    						(options & WNOWAIT), infop,
    						stat_addr, ru);
    
    			} else if (p->exit_state == EXIT_ZOMBIE &&
    					!delay_group_leader(p)) {
    
    Matthew Wilcox's avatar
    Matthew Wilcox committed
    				/*
    
    				 * We don't reap group leaders with subthreads.
    
    Matthew Wilcox's avatar
    Matthew Wilcox committed
    				 */
    				if (!likely(options & WEXITED))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    					continue;
    
    Matthew Wilcox's avatar
    Matthew Wilcox committed
    				retval = wait_task_zombie(p,
    						(options & WNOWAIT), infop,
    						stat_addr, ru);
    
    			} else if (p->exit_state != EXIT_DEAD) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    				/*
    				 * It's running now, so it might later
    				 * exit, stop, or stop and then continue.
    				 */
    				flag = 1;
    				if (!unlikely(options & WCONTINUED))
    					continue;
    
    Matthew Wilcox's avatar
    Matthew Wilcox committed
    				retval = wait_task_continued(p,
    						(options & WNOWAIT), infop,
    						stat_addr, ru);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			}
    
    			if (retval != 0) /* tasklist_lock released */
    				goto end;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		}
    		if (!flag) {
    
    			list_for_each_entry(p, &tsk->ptrace_children,
    
    								ptrace_list) {
    
    				flag = eligible_child(type, pid, options, p);
    
    				if (!flag)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    					continue;
    
    				if (likely(flag > 0))
    					break;
    				retval = flag;
    				goto end;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			}
    		}
    		if (options & __WNOTHREAD)
    			break;
    		tsk = next_thread(tsk);
    
    		BUG_ON(tsk->signal != current->signal);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	} while (tsk != current);
    	read_unlock(&tasklist_lock);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (flag) {
    		if (options & WNOHANG)
    			goto end;
    		retval = -ERESTARTSYS;
    		if (signal_pending(current))
    			goto end;
    		schedule();
    		goto repeat;
    	}
    	retval = -ECHILD;
    end:
    	current->state = TASK_RUNNING;
    	remove_wait_queue(&current->signal->wait_chldexit,&wait);
    	if (infop) {
    		if (retval > 0)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		else {
    			/*
    			 * For a WNOHANG return, clear out all the fields
    			 * we would set so the user can easily tell the
    			 * difference.
    			 */
    			if (!retval)
    				retval = put_user(0, &infop->si_signo);
    			if (!retval)
    				retval = put_user(0, &infop->si_errno);
    			if (!retval)
    				retval = put_user(0, &infop->si_code);
    			if (!retval)
    				retval = put_user(0, &infop->si_pid);
    			if (!retval)
    				retval = put_user(0, &infop->si_uid);
    			if (!retval)
    				retval = put_user(0, &infop->si_status);
    		}
    	}
    	return retval;
    }
    
    
    asmlinkage long sys_waitid(int which, pid_t upid,
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			   struct siginfo __user *infop, int options,
    			   struct rusage __user *ru)
    {
    
    	struct pid *pid = NULL;
    	enum pid_type type;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	long ret;
    
    	if (options & ~(WNOHANG|WNOWAIT|WEXITED|WSTOPPED|WCONTINUED))
    		return -EINVAL;
    	if (!(options & (WEXITED|WSTOPPED|WCONTINUED)))
    		return -EINVAL;
    
    	switch (which) {
    	case P_ALL:
    
    		type = PIDTYPE_MAX;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		break;
    	case P_PID:
    
    		type = PIDTYPE_PID;
    		if (upid <= 0)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			return -EINVAL;
    		break;
    	case P_PGID:
    
    		type = PIDTYPE_PGID;
    		if (upid <= 0)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			return -EINVAL;
    		break;
    	default:
    		return -EINVAL;
    	}
    
    
    	if (type < PIDTYPE_MAX)
    		pid = find_get_pid(upid);
    	ret = do_wait(type, pid, options, infop, NULL, ru);
    	put_pid(pid);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	/* avoid REGPARM breakage on x86: */
    	prevent_tail_call(ret);
    	return ret;
    }
    
    
    asmlinkage long sys_wait4(pid_t upid, int __user *stat_addr,
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			  int options, struct rusage __user *ru)
    {
    
    	struct pid *pid = NULL;
    	enum pid_type type;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	long ret;
    
    	if (options & ~(WNOHANG|WUNTRACED|WCONTINUED|
    			__WNOTHREAD|__WCLONE|__WALL))
    		return -EINVAL;
    
    
    	if (upid == -1)
    		type = PIDTYPE_MAX;
    	else if (upid < 0) {
    		type = PIDTYPE_PGID;
    		pid = find_get_pid(-upid);
    	} else if (upid == 0) {
    		type = PIDTYPE_PGID;
    		pid = get_pid(task_pgrp(current));
    	} else /* upid > 0 */ {
    		type = PIDTYPE_PID;
    		pid = find_get_pid(upid);
    	}
    
    	ret = do_wait(type, pid, options | WEXITED, NULL, stat_addr, ru);
    	put_pid(pid);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	/* avoid REGPARM breakage on x86: */
    	prevent_tail_call(ret);
    	return ret;
    }
    
    #ifdef __ARCH_WANT_SYS_WAITPID
    
    /*
     * sys_waitpid() remains for compatibility. waitpid() should be
     * implemented by calling sys_wait4() from libc.a.
     */
    asmlinkage long sys_waitpid(pid_t pid, int __user *stat_addr, int options)
    {
    	return sys_wait4(pid, stat_addr, options, NULL);
    }
    
    #endif