.long sys_mq_notify
.long sys_mq_getsetattr
.long sys_ni_syscall /* reserved for kexec */
+ .long sys_waitid
syscall_table_size=(.-sys_call_table)
err |= __put_user(from->si_utime, &to->si_utime);
err |= __put_user(from->si_stime, &to->si_stime);
err |= __put_user(from->si_status, &to->si_status);
+ err |= put_compat_rusage(&from->si_rusage,
+ &to->si_rusage);
default:
case __SI_KILL >> 16:
err |= __put_user(from->si_uid, &to->si_uid);
.quad compat_sys_mq_notify
.quad compat_sys_mq_getsetattr
.quad quiet_ni_syscall /* reserved for kexec */
+ .quad sys32_waitid
/* don't forget to change IA32_NR_syscalls */
ia32_syscall_end:
.rept IA32_NR_syscalls-(ia32_syscall_end-ia32_sys_call_table)/8
return do_fork(clone_flags, newsp, regs, 0, parent_tid, child_tid);
}
+asmlinkage long sys32_waitid(int which, compat_pid_t pid,
+ siginfo_t32 __user *uinfo, int options)
+{
+ siginfo_t info;
+ long ret;
+ mm_segment_t old_fs = get_fs();
+
+ info.si_signo = 0;
+ set_fs (KERNEL_DS);
+ ret = sys_waitid(which, pid, (siginfo_t __user *) &info, options);
+ set_fs (old_fs);
+
+ if (ret < 0 || info.si_signo == 0)
+ return ret;
+ BUG_ON(info.si_code & __SI_MASK);
+ info.si_code |= __SI_CHLD;
+ return ia32_copy_siginfo_to_user(uinfo, &info);
+}
+
/*
* Some system calls that need sign extended arguments. This could be done by a generic wrapper.
*/
#include <linux/compiler.h>
#include <linux/types.h>
+#include <linux/resource.h>
typedef union sigval {
int sival_int;
int _status; /* exit code */
clock_t _utime;
clock_t _stime;
+ struct rusage _rusage;
} _sigchld;
/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
#define si_status _sifields._sigchld._status
#define si_utime _sifields._sigchld._utime
#define si_stime _sifields._sigchld._stime
+#define si_rusage _sifields._sigchld._rusage
#define si_value _sifields._rt._sigval
#define si_int _sifields._rt._sigval.sival_int
#define si_ptr _sifields._rt._sigval.sival_ptr
#define __NR_mq_notify (__NR_mq_open+4)
#define __NR_mq_getsetattr (__NR_mq_open+5)
#define __NR_sys_kexec_load 283
+#define __NR_waitid 284
-#define NR_syscalls 284
+#define NR_syscalls 285
/* user-visible error numbers are in the range -1 - -124: see <asm-i386/errno.h> */
int _status; /* exit code */
clock_t _utime;
clock_t _stime;
+ struct rusage _rusage;
} _sigchld;
/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
int _status; /* exit code */
compat_clock_t _utime;
compat_clock_t _stime;
+ struct compat_rusage _rusage;
} _sigchld;
/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
#define __NR_ia32_mq_notify (__NR_ia32_mq_open+4)
#define __NR_ia32_mq_getsetattr (__NR_ia32_mq_open+5)
#define __NR_ia32_kexec 283
+#define __NR_ia32_waitid 284
-#define IA32_NR_syscalls 287 /* must be > than biggest syscall! */
+#define IA32_NR_syscalls 285 /* must be > than biggest syscall! */
#endif /* _ASM_X86_64_IA32_UNISTD_H_ */
__SYSCALL(__NR_mq_getsetattr, sys_mq_getsetattr)
#define __NR_kexec_load 246
__SYSCALL(__NR_kexec_load, sys_ni_syscall)
+#define __NR_waitid (253)
+__SYSCALL(__NR_waitid, sys_waitid)
-#define __NR_syscall_max __NR_kexec_load
+#define __NR_syscall_max __NR_waitid
#ifndef __NO_STUBS
/* user-visible error numbers are in the range -1 - -4095 */
compat_long_t ru_nivcsw;
};
+extern int put_compat_rusage(const struct rusage *, struct compat_rusage __user *);
+
struct compat_dirent {
u32 d_ino;
compat_off_t d_off;
/* thread group stop support, overloads group_exit_code too */
int group_stop_count;
+ /* 1 if group stopped since last SIGCONT, -1 if SIGCONT since report */
+ int stop_state;
/* POSIX.1b Interval Timers */
struct list_head posix_timers;
asmlinkage void sys_exit_group(int error_code);
asmlinkage long sys_wait4(pid_t pid, unsigned int __user *stat_addr,
int options, struct rusage __user *ru);
+asmlinkage long sys_waitid(int which, pid_t pid,
+ struct siginfo __user *infop, int options);
asmlinkage long sys_waitpid(pid_t pid, unsigned int __user *stat_addr, int options);
asmlinkage long sys_set_tid_address(int __user *tidptr);
asmlinkage long sys_futex(u32 __user *uaddr, int op, int val,
#define WNOHANG 0x00000001
#define WUNTRACED 0x00000002
+#define WSTOPPED WUNTRACED
+#define WEXITED 0x00000004
+#define WCONTINUED 0x00000008
+#define WNOWAIT 0x01000000 /* Don't reap, just poll status. */
#define __WNOTHREAD 0x20000000 /* Don't wait on children of other threads in this group */
#define __WALL 0x40000000 /* Wait on all children, regardless of type */
#define __WCLONE 0x80000000 /* Wait only on non-SIGCHLD children */
+/* First argument to waitid: */
+#define P_ALL 0
+#define P_PID 1
+#define P_PGID 2
+
#ifdef __KERNEL__
#include <linux/config.h>
return ret;
}
-static long put_compat_rusage(struct compat_rusage __user *ru, struct rusage *r)
+int put_compat_rusage(const struct rusage *r, struct compat_rusage __user *ru)
{
if (!access_ok(VERIFY_WRITE, ru, sizeof(*ru)) ||
__put_user(r->ru_utime.tv_sec, &ru->ru_utime.tv_sec) ||
if (ret)
return ret;
- if (put_compat_rusage(ru, &r))
+ if (put_compat_rusage(&r, ru))
return -EFAULT;
return 0;
set_fs (old_fs);
if (ret > 0) {
- if (put_compat_rusage(ru, &r))
+ if (put_compat_rusage(&r, ru))
return -EFAULT;
if (stat_addr && put_user(status, stat_addr))
return -EFAULT;
return 1;
}
+static int wait_noreap_copyout(task_t *p, pid_t pid, uid_t uid,
+ int why, int status,
+ struct siginfo __user *infop)
+{
+ int retval = getrusage(p, RUSAGE_BOTH, &infop->si_rusage);
+ 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 TASK_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(task_t *p, unsigned int __user *stat_addr, struct rusage __user *ru)
+static int wait_task_zombie(task_t *p, int noreap,
+ struct siginfo __user *infop,
+ int __user *stat_addr, struct rusage __user *ru)
{
unsigned long state;
int retval;
+ int status;
+
+ if (unlikely(noreap)) {
+ pid_t pid = p->pid;
+ uid_t uid = p->uid;
+ int exit_code = p->exit_code;
+ int why, status;
+
+ if (unlikely(p->state != TASK_ZOMBIE))
+ return 0;
+ if (unlikely(p->exit_signal == -1 && p->ptrace == 0))
+ return 0;
+ 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);
+ }
/*
* Try to move the task's state to DEAD
BUG_ON(state != TASK_DEAD);
return 0;
}
- if (unlikely(p->exit_signal == -1 && p->ptrace == 0))
+ if (unlikely(p->exit_signal == -1 && p->ptrace == 0)) {
/*
* This can only happen in a race with a ptraced thread
* dying on another processor.
*/
return 0;
+ }
/*
* Now we are sure this task is interesting, and no other
read_unlock(&tasklist_lock);
retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0;
- if (!retval && stat_addr) {
- if (p->signal->group_exit)
- retval = put_user(p->signal->group_exit_code, stat_addr);
- else
- retval = put_user(p->exit_code, stat_addr);
+ status = p->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(p->pid, &infop->si_pid);
+ if (!retval && infop)
+ retval = put_user(p->uid, &infop->si_uid);
if (retval) {
p->state = TASK_ZOMBIE;
return retval;
__ptrace_unlink(p);
p->state = TASK_ZOMBIE;
/*
- * If this is not a detached task, notify the parent. If it's
- * still not detached after that, don't release it now.
+ * 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);
* 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(task_t *p, int delayed_group_leader,
- unsigned int __user *stat_addr,
- struct rusage __user *ru)
+static int wait_task_stopped(task_t *p, int delayed_group_leader, int noreap,
+ struct siginfo __user *infop,
+ int __user *stat_addr, struct rusage __user *ru)
{
int retval, exit_code;
*/
get_task_struct(p);
read_unlock(&tasklist_lock);
+
+ if (unlikely(noreap)) {
+ pid_t pid = p->pid;
+ uid_t uid = p->uid;
+ int why = (p->ptrace & PT_PTRACED) ? CLD_TRAPPED : CLD_STOPPED;
+
+ exit_code = p->exit_code;
+ if (unlikely(!exit_code) ||
+ unlikely(p->state > TASK_STOPPED))
+ goto bail_ref;
+ return wait_noreap_copyout(p, pid, uid,
+ why, (exit_code << 8) | 0x7f,
+ infop);
+ }
+
write_lock_irq(&tasklist_lock);
/*
* resumed, or it resumed and then died.
*/
write_unlock_irq(&tasklist_lock);
+bail_ref:
put_task_struct(p);
read_lock(&tasklist_lock);
return 0;
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((short)((p->ptrace & PT_PTRACED)
+ ? CLD_TRAPPED : CLD_STOPPED),
+ &infop->si_code);
+ if (!retval && infop)
+ retval = put_user(exit_code, &infop->si_status);
+ if (!retval && infop)
+ retval = put_user(p->pid, &infop->si_pid);
+ if (!retval && infop)
+ retval = put_user(p->uid, &infop->si_uid);
if (!retval)
retval = p->pid;
put_task_struct(p);
return retval;
}
-asmlinkage long sys_wait4(pid_t pid,unsigned int __user *stat_addr, int options, struct rusage __user *ru)
+static long do_wait(pid_t pid, int options, struct siginfo __user *infop,
+ int __user *stat_addr, struct rusage __user *ru)
{
DECLARE_WAITQUEUE(wait, current);
struct task_struct *tsk;
int flag, retval;
- if (options & ~(WNOHANG|WUNTRACED|__WNOTHREAD|__WCLONE|__WALL))
- return -EINVAL;
-
add_wait_queue(¤t->wait_chldexit,&wait);
repeat:
flag = 0;
!(p->ptrace & PT_PTRACED))
continue;
retval = wait_task_stopped(p, ret == 2,
+ (options & WNOWAIT),
+ infop,
stat_addr, ru);
if (retval != 0) /* He released the lock. */
- goto end_wait4;
+ 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, stat_addr, ru);
+ retval = wait_task_zombie(
+ p, (options & WNOWAIT),
+ infop, stat_addr, ru);
if (retval != 0) /* He released the lock. */
- goto end_wait4;
+ 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);
+ BUG_ON(retval == 0);
+ goto end;
+ }
+ spin_unlock_irq(&p->sighand->siglock);
break;
}
}
if (!flag) {
- list_for_each (_p,&tsk->ptrace_children) {
- p = list_entry(_p,struct task_struct,ptrace_list);
+ list_for_each(_p, &tsk->ptrace_children) {
+ p = list_entry(_p, struct task_struct,
+ ptrace_list);
if (!eligible_child(pid, options, p))
continue;
flag = 1;
if (tsk->signal != current->signal)
BUG();
} while (tsk != current);
+
read_unlock(&tasklist_lock);
if (flag) {
retval = 0;
if (options & WNOHANG)
- goto end_wait4;
+ goto end;
retval = -ERESTARTSYS;
if (signal_pending(current))
- goto end_wait4;
+ goto end;
schedule();
goto repeat;
}
retval = -ECHILD;
-end_wait4:
+end:
current->state = TASK_RUNNING;
remove_wait_queue(¤t->wait_chldexit,&wait);
+ if (infop) {
+ if (retval > 0)
+ retval = 0;
+ 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 pid,
+ struct siginfo __user *infop, int options)
+{
+ if (options & ~(WNOHANG|WNOWAIT|WEXITED|WSTOPPED|WCONTINUED))
+ return -EINVAL;
+ if (!(options & (WEXITED|WSTOPPED|WCONTINUED)))
+ return -EINVAL;
+
+ switch (which) {
+ case P_ALL:
+ pid = -1;
+ break;
+ case P_PID:
+ if (pid <= 0)
+ return -EINVAL;
+ break;
+ case P_PGID:
+ if (pid <= 0)
+ return -EINVAL;
+ pid = -pid;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return do_wait(pid, options, infop, NULL, &infop->si_rusage);
+}
+
+asmlinkage long sys_wait4(pid_t pid, unsigned int *stat_addr,
+ int options, struct rusage *ru)
+{
+ if (options & ~(WNOHANG|WUNTRACED|__WNOTHREAD|__WCLONE|__WALL))
+ return -EINVAL;
+ return do_wait(pid, options | WEXITED, NULL, stat_addr, ru);
+}
+
#ifdef __ARCH_WANT_SYS_WAITPID
/*
#include <asm/unistd.h>
#include <asm/siginfo.h>
+extern void k_getrusage(struct task_struct *, int, struct rusage *);
+
/*
* SLAB caches for signal bits.
*/
* the SIGCHLD was pending on entry to this kill.
*/
p->signal->group_stop_count = 0;
+ p->signal->stop_state = 1;
if (p->ptrace & PT_PTRACED)
do_notify_parent_cldstop(p, p->parent);
else
t = next_thread(t);
} while (t != p);
+
+ if (p->signal->stop_state > 0) {
+ /*
+ * We were in fact stopped, and are now continued.
+ * Notify the parent with CLD_CONTINUED.
+ */
+ p->signal->stop_state = -1;
+ p->signal->group_exit_code = 0;
+ if (p->ptrace & PT_PTRACED)
+ do_notify_parent_cldstop(p, p->parent);
+ else
+ do_notify_parent_cldstop(
+ p->group_leader,
+ p->group_leader->real_parent);
+ }
}
}
/* FIXME: find out whether or not this is supposed to be c*time. */
info.si_utime = tsk->utime;
info.si_stime = tsk->stime;
+ k_getrusage(tsk, RUSAGE_BOTH, &info.si_rusage);
status = tsk->exit_code & 0x7f;
why = SI_KERNEL; /* shouldn't happen */
/* FIXME: find out whether or not this is supposed to be c*time. */
info.si_utime = tsk->utime;
info.si_stime = tsk->stime;
+ k_getrusage(tsk, RUSAGE_BOTH, &info.si_rusage);
- info.si_status = tsk->exit_code & 0x7f;
- info.si_code = CLD_STOPPED;
+ info.si_status = (tsk->signal ? tsk->signal->group_exit_code :
+ tsk->exit_code) & 0x7f;
+ if (info.si_status == 0) {
+ info.si_status = SIGCONT;
+ info.si_code = CLD_CONTINUED;
+ } else {
+ info.si_code = CLD_STOPPED;
+ }
sighand = parent->sighand;
spin_lock_irqsave(&sighand->siglock, flags);
stop_count = --sig->group_stop_count;
current->exit_code = signr;
set_current_state(TASK_STOPPED);
+ if (stop_count == 0)
+ sig->stop_state = 1;
spin_unlock_irq(&sighand->siglock);
}
else if (thread_group_empty(current)) {
/*
* Lock must be held through transition to stopped state.
*/
- current->exit_code = signr;
+ current->exit_code = current->signal->group_exit_code = signr;
set_current_state(TASK_STOPPED);
+ sig->stop_state = 1;
spin_unlock_irq(&sighand->siglock);
}
else {
current->exit_code = signr;
set_current_state(TASK_STOPPED);
+ if (stop_count == 0)
+ sig->stop_state = 1;
spin_unlock_irq(&sighand->siglock);
read_unlock(&tasklist_lock);
* without any associated signal being in our queue.
*/
stop_count = --current->signal->group_stop_count;
+ if (stop_count == 0)
+ current->signal->stop_state = 1;
current->exit_code = current->signal->group_exit_code;
set_current_state(TASK_STOPPED);
spin_unlock_irq(¤t->sighand->siglock);
err |= __put_user(from->si_status, &to->si_status);
err |= __put_user(from->si_utime, &to->si_utime);
err |= __put_user(from->si_stime, &to->si_stime);
+ err |= __copy_to_user(&to->si_rusage, &from->si_rusage,
+ sizeof(to->si_rusage));
break;
case __SI_RT: /* This is not generated by the kernel as of now. */
case __SI_MESGQ: /* But this is */
* reaped till shortly after the call to getrusage(), in both cases the
* task being examined is in a frozen state so the counters won't change.
*/
-int getrusage(struct task_struct *p, int who, struct rusage __user *ru)
-{
- struct rusage r;
- memset((char *) &r, 0, sizeof(r));
+
+void k_getrusage(struct task_struct *p, int who, struct rusage *r)
+{
+ memset((char *) r, 0, sizeof *r);
switch (who) {
case RUSAGE_SELF:
- jiffies_to_timeval(p->utime, &r.ru_utime);
- jiffies_to_timeval(p->stime, &r.ru_stime);
- r.ru_nvcsw = p->nvcsw;
- r.ru_nivcsw = p->nivcsw;
- r.ru_minflt = p->min_flt;
- r.ru_majflt = p->maj_flt;
+ jiffies_to_timeval(p->utime, &r->ru_utime);
+ jiffies_to_timeval(p->stime, &r->ru_stime);
+ r->ru_nvcsw = p->nvcsw;
+ r->ru_nivcsw = p->nivcsw;
+ r->ru_minflt = p->min_flt;
+ r->ru_majflt = p->maj_flt;
break;
case RUSAGE_CHILDREN:
- jiffies_to_timeval(p->cutime, &r.ru_utime);
- jiffies_to_timeval(p->cstime, &r.ru_stime);
- r.ru_nvcsw = p->cnvcsw;
- r.ru_nivcsw = p->cnivcsw;
- r.ru_minflt = p->cmin_flt;
- r.ru_majflt = p->cmaj_flt;
+ jiffies_to_timeval(p->cutime, &r->ru_utime);
+ jiffies_to_timeval(p->cstime, &r->ru_stime);
+ r->ru_nvcsw = p->cnvcsw;
+ r->ru_nivcsw = p->cnivcsw;
+ r->ru_minflt = p->cmin_flt;
+ r->ru_majflt = p->cmaj_flt;
break;
default:
- jiffies_to_timeval(p->utime + p->cutime, &r.ru_utime);
- jiffies_to_timeval(p->stime + p->cstime, &r.ru_stime);
- r.ru_nvcsw = p->nvcsw + p->cnvcsw;
- r.ru_nivcsw = p->nivcsw + p->cnivcsw;
- r.ru_minflt = p->min_flt + p->cmin_flt;
- r.ru_majflt = p->maj_flt + p->cmaj_flt;
+ jiffies_to_timeval(p->utime + p->cutime, &r->ru_utime);
+ jiffies_to_timeval(p->stime + p->cstime, &r->ru_stime);
+ r->ru_nvcsw = p->nvcsw + p->cnvcsw;
+ r->ru_nivcsw = p->nivcsw + p->cnivcsw;
+ r->ru_minflt = p->min_flt + p->cmin_flt;
+ r->ru_majflt = p->maj_flt + p->cmaj_flt;
break;
}
+}
+
+int getrusage(struct task_struct *p, int who, struct rusage __user *ru)
+{
+ struct rusage r;
+ k_getrusage(p, who, &r);
return copy_to_user(ru, &r, sizeof(r)) ? -EFAULT : 0;
}