Commit ca3f74aa authored by Roland McGrath's avatar Roland McGrath Committed by Linus Torvalds

[PATCH] waitid system call

This patch adds a new system call `waitid'.  This is a new POSIX call that
subsumes the rest of the wait* family and can do some things the older
calls cannot.  A minor addition is the ability to select what kinds of
status to check for with a mask of independent bits, so you can wait for
just stops and not terminations, for example.  A more significant
improvement is the WNOWAIT flag, which allows for polling child status
without reaping.  This interface fills in a siginfo_t with the same details
that a SIGCHLD for the status change has; some of that info (e.g.  si_uid)
is not available via wait4 or other calls.

I've added a new system call that has the parameter conventions of the
POSIX function because that seems like the cleanest thing.  This patch
includes the actual system call table additions for i386 and x86-64; other
architectures will need to assign the system call number, and 64-bit ones
may need to implement 32-bit compat support for it as I did for x86-64. 
The new features could instead be provided by some new kludge inventions in
the wait4 system call interface (that's what BSD did).  If kludges are
preferable to adding a system call, I can work up something different.

I added a struct rusage field si_rusage to siginfo_t in the SIGCHLD case
(this does not affect the size or layout of the struct).  This is not part
of the POSIX interface, but it makes it so that `waitid' subsumes all the
functionality of `wait4'.  Future kernel ABIs (new arch's or whatnot) can
have only the `waitid' system call and the rest of the wait* family
including wait3 and wait4 can be implemented in user space using waitid.
There is nothing in user space as yet that would make use of the new field.

Most of the new functionality is implemented purely in the waitid system
call itself.  POSIX also provides for the WCONTINUED flag to report when a
child process had been stopped by job control and then resumed with
SIGCONT.  Corresponding to this, a SIGCHLD is now generated when a child
resumes (unless SA_NOCLDSTOP is set), with the value CLD_CONTINUED in
siginfo_t.si_code.  To implement this, some additional bookkeeping is
required in the signal code handling job control stops.

The motivation for this work is to make it possible to implement the POSIX
semantics of the `waitid' function in glibc completely and correctly.  If
changing either the system call interface used to accomplish that, or any
details of the kernel implementation work, would improve the chances of
getting this incorporated, I am more than happy to work through any issues.
Signed-off-by: default avatarRoland McGrath <roland@redhat.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 4c746d40
...@@ -900,5 +900,6 @@ ENTRY(sys_call_table) ...@@ -900,5 +900,6 @@ ENTRY(sys_call_table)
.long sys_mq_notify .long sys_mq_notify
.long sys_mq_getsetattr .long sys_mq_getsetattr
.long sys_ni_syscall /* reserved for kexec */ .long sys_ni_syscall /* reserved for kexec */
.long sys_waitid
syscall_table_size=(.-sys_call_table) syscall_table_size=(.-sys_call_table)
...@@ -74,6 +74,8 @@ int ia32_copy_siginfo_to_user(siginfo_t32 __user *to, siginfo_t *from) ...@@ -74,6 +74,8 @@ int ia32_copy_siginfo_to_user(siginfo_t32 __user *to, siginfo_t *from)
err |= __put_user(from->si_utime, &to->si_utime); err |= __put_user(from->si_utime, &to->si_utime);
err |= __put_user(from->si_stime, &to->si_stime); err |= __put_user(from->si_stime, &to->si_stime);
err |= __put_user(from->si_status, &to->si_status); err |= __put_user(from->si_status, &to->si_status);
err |= put_compat_rusage(&from->si_rusage,
&to->si_rusage);
default: default:
case __SI_KILL >> 16: case __SI_KILL >> 16:
err |= __put_user(from->si_uid, &to->si_uid); err |= __put_user(from->si_uid, &to->si_uid);
......
...@@ -586,6 +586,7 @@ ia32_sys_call_table: ...@@ -586,6 +586,7 @@ ia32_sys_call_table:
.quad compat_sys_mq_notify .quad compat_sys_mq_notify
.quad compat_sys_mq_getsetattr .quad compat_sys_mq_getsetattr
.quad quiet_ni_syscall /* reserved for kexec */ .quad quiet_ni_syscall /* reserved for kexec */
.quad sys32_waitid
/* don't forget to change IA32_NR_syscalls */ /* don't forget to change IA32_NR_syscalls */
ia32_syscall_end: ia32_syscall_end:
.rept IA32_NR_syscalls-(ia32_syscall_end-ia32_sys_call_table)/8 .rept IA32_NR_syscalls-(ia32_syscall_end-ia32_sys_call_table)/8
......
...@@ -1151,6 +1151,25 @@ asmlinkage long sys32_clone(unsigned int clone_flags, unsigned int newsp, ...@@ -1151,6 +1151,25 @@ asmlinkage long sys32_clone(unsigned int clone_flags, unsigned int newsp,
return do_fork(clone_flags, newsp, regs, 0, parent_tid, child_tid); 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. * Some system calls that need sign extended arguments. This could be done by a generic wrapper.
*/ */
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/resource.h>
typedef union sigval { typedef union sigval {
int sival_int; int sival_int;
...@@ -74,6 +75,7 @@ typedef struct siginfo { ...@@ -74,6 +75,7 @@ typedef struct siginfo {
int _status; /* exit code */ int _status; /* exit code */
clock_t _utime; clock_t _utime;
clock_t _stime; clock_t _stime;
struct rusage _rusage;
} _sigchld; } _sigchld;
/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
...@@ -105,6 +107,7 @@ typedef struct siginfo { ...@@ -105,6 +107,7 @@ typedef struct siginfo {
#define si_status _sifields._sigchld._status #define si_status _sifields._sigchld._status
#define si_utime _sifields._sigchld._utime #define si_utime _sifields._sigchld._utime
#define si_stime _sifields._sigchld._stime #define si_stime _sifields._sigchld._stime
#define si_rusage _sifields._sigchld._rusage
#define si_value _sifields._rt._sigval #define si_value _sifields._rt._sigval
#define si_int _sifields._rt._sigval.sival_int #define si_int _sifields._rt._sigval.sival_int
#define si_ptr _sifields._rt._sigval.sival_ptr #define si_ptr _sifields._rt._sigval.sival_ptr
......
...@@ -289,8 +289,9 @@ ...@@ -289,8 +289,9 @@
#define __NR_mq_notify (__NR_mq_open+4) #define __NR_mq_notify (__NR_mq_open+4)
#define __NR_mq_getsetattr (__NR_mq_open+5) #define __NR_mq_getsetattr (__NR_mq_open+5)
#define __NR_sys_kexec_load 283 #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> */ /* user-visible error numbers are in the range -1 - -124: see <asm-i386/errno.h> */
......
...@@ -56,6 +56,7 @@ typedef struct siginfo { ...@@ -56,6 +56,7 @@ typedef struct siginfo {
int _status; /* exit code */ int _status; /* exit code */
clock_t _utime; clock_t _utime;
clock_t _stime; clock_t _stime;
struct rusage _rusage;
} _sigchld; } _sigchld;
/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
......
...@@ -115,6 +115,7 @@ typedef struct siginfo32 { ...@@ -115,6 +115,7 @@ typedef struct siginfo32 {
int _status; /* exit code */ int _status; /* exit code */
compat_clock_t _utime; compat_clock_t _utime;
compat_clock_t _stime; compat_clock_t _stime;
struct compat_rusage _rusage;
} _sigchld; } _sigchld;
/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
......
...@@ -289,7 +289,8 @@ ...@@ -289,7 +289,8 @@
#define __NR_ia32_mq_notify (__NR_ia32_mq_open+4) #define __NR_ia32_mq_notify (__NR_ia32_mq_open+4)
#define __NR_ia32_mq_getsetattr (__NR_ia32_mq_open+5) #define __NR_ia32_mq_getsetattr (__NR_ia32_mq_open+5)
#define __NR_ia32_kexec 283 #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_ */ #endif /* _ASM_X86_64_IA32_UNISTD_H_ */
...@@ -554,8 +554,10 @@ __SYSCALL(__NR_mq_notify, sys_mq_notify) ...@@ -554,8 +554,10 @@ __SYSCALL(__NR_mq_notify, sys_mq_notify)
__SYSCALL(__NR_mq_getsetattr, sys_mq_getsetattr) __SYSCALL(__NR_mq_getsetattr, sys_mq_getsetattr)
#define __NR_kexec_load 246 #define __NR_kexec_load 246
__SYSCALL(__NR_kexec_load, sys_ni_syscall) __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 #ifndef __NO_STUBS
/* user-visible error numbers are in the range -1 - -4095 */ /* user-visible error numbers are in the range -1 - -4095 */
......
...@@ -79,6 +79,8 @@ struct compat_rusage { ...@@ -79,6 +79,8 @@ struct compat_rusage {
compat_long_t ru_nivcsw; compat_long_t ru_nivcsw;
}; };
extern int put_compat_rusage(const struct rusage *, struct compat_rusage __user *);
struct compat_dirent { struct compat_dirent {
u32 d_ino; u32 d_ino;
compat_off_t d_off; compat_off_t d_off;
......
...@@ -287,6 +287,8 @@ struct signal_struct { ...@@ -287,6 +287,8 @@ struct signal_struct {
/* thread group stop support, overloads group_exit_code too */ /* thread group stop support, overloads group_exit_code too */
int group_stop_count; int group_stop_count;
/* 1 if group stopped since last SIGCONT, -1 if SIGCONT since report */
int stop_state;
/* POSIX.1b Interval Timers */ /* POSIX.1b Interval Timers */
struct list_head posix_timers; struct list_head posix_timers;
......
...@@ -162,6 +162,8 @@ asmlinkage long sys_exit(int error_code); ...@@ -162,6 +162,8 @@ asmlinkage long sys_exit(int error_code);
asmlinkage void sys_exit_group(int error_code); asmlinkage void sys_exit_group(int error_code);
asmlinkage long sys_wait4(pid_t pid, unsigned int __user *stat_addr, asmlinkage long sys_wait4(pid_t pid, unsigned int __user *stat_addr,
int options, struct rusage __user *ru); 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_waitpid(pid_t pid, unsigned int __user *stat_addr, int options);
asmlinkage long sys_set_tid_address(int __user *tidptr); asmlinkage long sys_set_tid_address(int __user *tidptr);
asmlinkage long sys_futex(u32 __user *uaddr, int op, int val, asmlinkage long sys_futex(u32 __user *uaddr, int op, int val,
......
...@@ -3,11 +3,20 @@ ...@@ -3,11 +3,20 @@
#define WNOHANG 0x00000001 #define WNOHANG 0x00000001
#define WUNTRACED 0x00000002 #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 __WNOTHREAD 0x20000000 /* Don't wait on children of other threads in this group */
#define __WALL 0x40000000 /* Wait on all children, regardless of type */ #define __WALL 0x40000000 /* Wait on all children, regardless of type */
#define __WCLONE 0x80000000 /* Wait only on non-SIGCHLD children */ #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__ #ifdef __KERNEL__
#include <linux/config.h> #include <linux/config.h>
......
...@@ -310,7 +310,7 @@ asmlinkage long compat_sys_getrlimit (unsigned int resource, ...@@ -310,7 +310,7 @@ asmlinkage long compat_sys_getrlimit (unsigned int resource,
return ret; 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)) || if (!access_ok(VERIFY_WRITE, ru, sizeof(*ru)) ||
__put_user(r->ru_utime.tv_sec, &ru->ru_utime.tv_sec) || __put_user(r->ru_utime.tv_sec, &ru->ru_utime.tv_sec) ||
...@@ -348,7 +348,7 @@ asmlinkage long compat_sys_getrusage(int who, struct compat_rusage __user *ru) ...@@ -348,7 +348,7 @@ asmlinkage long compat_sys_getrusage(int who, struct compat_rusage __user *ru)
if (ret) if (ret)
return ret; return ret;
if (put_compat_rusage(ru, &r)) if (put_compat_rusage(&r, ru))
return -EFAULT; return -EFAULT;
return 0; return 0;
...@@ -374,7 +374,7 @@ compat_sys_wait4(compat_pid_t pid, compat_uint_t __user *stat_addr, int options, ...@@ -374,7 +374,7 @@ compat_sys_wait4(compat_pid_t pid, compat_uint_t __user *stat_addr, int options,
set_fs (old_fs); set_fs (old_fs);
if (ret > 0) { if (ret > 0) {
if (put_compat_rusage(ru, &r)) if (put_compat_rusage(&r, ru))
return -EFAULT; return -EFAULT;
if (stat_addr && put_user(status, stat_addr)) if (stat_addr && put_user(status, stat_addr))
return -EFAULT; return -EFAULT;
......
...@@ -957,16 +957,64 @@ static int eligible_child(pid_t pid, int options, task_t *p) ...@@ -957,16 +957,64 @@ static int eligible_child(pid_t pid, int options, task_t *p)
return 1; 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 * 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 * 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 * the lock and this task is uninteresting. If we return nonzero, we have
* released the lock and the system call should return. * 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; unsigned long state;
int retval; 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 * Try to move the task's state to DEAD
...@@ -977,12 +1025,13 @@ static int wait_task_zombie(task_t *p, unsigned int __user *stat_addr, struct ru ...@@ -977,12 +1025,13 @@ static int wait_task_zombie(task_t *p, unsigned int __user *stat_addr, struct ru
BUG_ON(state != TASK_DEAD); BUG_ON(state != TASK_DEAD);
return 0; 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 * This can only happen in a race with a ptraced thread
* dying on another processor. * dying on another processor.
*/ */
return 0; return 0;
}
/* /*
* Now we are sure this task is interesting, and no other * Now we are sure this task is interesting, and no other
...@@ -991,12 +1040,32 @@ static int wait_task_zombie(task_t *p, unsigned int __user *stat_addr, struct ru ...@@ -991,12 +1040,32 @@ static int wait_task_zombie(task_t *p, unsigned int __user *stat_addr, struct ru
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0; retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0;
if (!retval && stat_addr) { status = p->signal->group_exit
if (p->signal->group_exit) ? p->signal->group_exit_code : p->exit_code;
retval = put_user(p->signal->group_exit_code, stat_addr); if (!retval && stat_addr)
else retval = put_user(status, stat_addr);
retval = put_user(p->exit_code, 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) { if (retval) {
p->state = TASK_ZOMBIE; p->state = TASK_ZOMBIE;
return retval; return retval;
...@@ -1009,8 +1078,9 @@ static int wait_task_zombie(task_t *p, unsigned int __user *stat_addr, struct ru ...@@ -1009,8 +1078,9 @@ static int wait_task_zombie(task_t *p, unsigned int __user *stat_addr, struct ru
__ptrace_unlink(p); __ptrace_unlink(p);
p->state = TASK_ZOMBIE; p->state = TASK_ZOMBIE;
/* /*
* If this is not a detached task, notify the parent. If it's * If this is not a detached task, notify the parent.
* still not detached after that, don't release it now. * If it's still not detached after that, don't release
* it now.
*/ */
if (p->exit_signal != -1) { if (p->exit_signal != -1) {
do_notify_parent(p, p->exit_signal); do_notify_parent(p, p->exit_signal);
...@@ -1032,9 +1102,9 @@ static int wait_task_zombie(task_t *p, unsigned int __user *stat_addr, struct ru ...@@ -1032,9 +1102,9 @@ static int wait_task_zombie(task_t *p, unsigned int __user *stat_addr, struct ru
* the lock and this task is uninteresting. If we return nonzero, we have * the lock and this task is uninteresting. If we return nonzero, we have
* released the lock and the system call should return. * released the lock and the system call should return.
*/ */
static int wait_task_stopped(task_t *p, int delayed_group_leader, static int wait_task_stopped(task_t *p, int delayed_group_leader, int noreap,
unsigned int __user *stat_addr, struct siginfo __user *infop,
struct rusage __user *ru) int __user *stat_addr, struct rusage __user *ru)
{ {
int retval, exit_code; int retval, exit_code;
...@@ -1057,6 +1127,21 @@ static int wait_task_stopped(task_t *p, int delayed_group_leader, ...@@ -1057,6 +1127,21 @@ static int wait_task_stopped(task_t *p, int delayed_group_leader,
*/ */
get_task_struct(p); get_task_struct(p);
read_unlock(&tasklist_lock); 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); write_lock_irq(&tasklist_lock);
/* /*
...@@ -1082,6 +1167,7 @@ static int wait_task_stopped(task_t *p, int delayed_group_leader, ...@@ -1082,6 +1167,7 @@ static int wait_task_stopped(task_t *p, int delayed_group_leader,
* resumed, or it resumed and then died. * resumed, or it resumed and then died.
*/ */
write_unlock_irq(&tasklist_lock); write_unlock_irq(&tasklist_lock);
bail_ref:
put_task_struct(p); put_task_struct(p);
read_lock(&tasklist_lock); read_lock(&tasklist_lock);
return 0; return 0;
...@@ -1096,6 +1182,20 @@ static int wait_task_stopped(task_t *p, int delayed_group_leader, ...@@ -1096,6 +1182,20 @@ static int wait_task_stopped(task_t *p, int delayed_group_leader,
retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0; retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0;
if (!retval && stat_addr) if (!retval && stat_addr)
retval = put_user((exit_code << 8) | 0x7f, 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) if (!retval)
retval = p->pid; retval = p->pid;
put_task_struct(p); put_task_struct(p);
...@@ -1104,15 +1204,13 @@ static int wait_task_stopped(task_t *p, int delayed_group_leader, ...@@ -1104,15 +1204,13 @@ static int wait_task_stopped(task_t *p, int delayed_group_leader,
return retval; 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); DECLARE_WAITQUEUE(wait, current);
struct task_struct *tsk; struct task_struct *tsk;
int flag, retval; int flag, retval;
if (options & ~(WNOHANG|WUNTRACED|__WNOTHREAD|__WCLONE|__WALL))
return -EINVAL;
add_wait_queue(&current->wait_chldexit,&wait); add_wait_queue(&current->wait_chldexit,&wait);
repeat: repeat:
flag = 0; flag = 0;
...@@ -1138,25 +1236,60 @@ asmlinkage long sys_wait4(pid_t pid,unsigned int __user *stat_addr, int options, ...@@ -1138,25 +1236,60 @@ asmlinkage long sys_wait4(pid_t pid,unsigned int __user *stat_addr, int options,
!(p->ptrace & PT_PTRACED)) !(p->ptrace & PT_PTRACED))
continue; continue;
retval = wait_task_stopped(p, ret == 2, retval = wait_task_stopped(p, ret == 2,
(options & WNOWAIT),
infop,
stat_addr, ru); stat_addr, ru);
if (retval != 0) /* He released the lock. */ if (retval != 0) /* He released the lock. */
goto end_wait4; goto end;
break; break;
case TASK_ZOMBIE: case TASK_ZOMBIE:
/* /*
* Eligible but we cannot release it yet: * Eligible but we cannot release it yet:
*/ */
if (ret == 2) if (ret == 2)
goto check_continued;
if (!likely(options & WEXITED))
continue; 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. */ 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; break;
} }
} }
if (!flag) { if (!flag) {
list_for_each (_p,&tsk->ptrace_children) { list_for_each(_p, &tsk->ptrace_children) {
p = list_entry(_p,struct task_struct,ptrace_list); p = list_entry(_p, struct task_struct,
ptrace_list);
if (!eligible_child(pid, options, p)) if (!eligible_child(pid, options, p))
continue; continue;
flag = 1; flag = 1;
...@@ -1169,24 +1302,84 @@ asmlinkage long sys_wait4(pid_t pid,unsigned int __user *stat_addr, int options, ...@@ -1169,24 +1302,84 @@ asmlinkage long sys_wait4(pid_t pid,unsigned int __user *stat_addr, int options,
if (tsk->signal != current->signal) if (tsk->signal != current->signal)
BUG(); BUG();
} while (tsk != current); } while (tsk != current);
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
if (flag) { if (flag) {
retval = 0; retval = 0;
if (options & WNOHANG) if (options & WNOHANG)
goto end_wait4; goto end;
retval = -ERESTARTSYS; retval = -ERESTARTSYS;
if (signal_pending(current)) if (signal_pending(current))
goto end_wait4; goto end;
schedule(); schedule();
goto repeat; goto repeat;
} }
retval = -ECHILD; retval = -ECHILD;
end_wait4: end:
current->state = TASK_RUNNING; current->state = TASK_RUNNING;
remove_wait_queue(&current->wait_chldexit,&wait); remove_wait_queue(&current->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; 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 #ifdef __ARCH_WANT_SYS_WAITPID
/* /*
......
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
#include <asm/unistd.h> #include <asm/unistd.h>
#include <asm/siginfo.h> #include <asm/siginfo.h>
extern void k_getrusage(struct task_struct *, int, struct rusage *);
/* /*
* SLAB caches for signal bits. * SLAB caches for signal bits.
*/ */
...@@ -660,6 +662,7 @@ static void handle_stop_signal(int sig, struct task_struct *p) ...@@ -660,6 +662,7 @@ static void handle_stop_signal(int sig, struct task_struct *p)
* the SIGCHLD was pending on entry to this kill. * the SIGCHLD was pending on entry to this kill.
*/ */
p->signal->group_stop_count = 0; p->signal->group_stop_count = 0;
p->signal->stop_state = 1;
if (p->ptrace & PT_PTRACED) if (p->ptrace & PT_PTRACED)
do_notify_parent_cldstop(p, p->parent); do_notify_parent_cldstop(p, p->parent);
else else
...@@ -696,6 +699,21 @@ static void handle_stop_signal(int sig, struct task_struct *p) ...@@ -696,6 +699,21 @@ static void handle_stop_signal(int sig, struct task_struct *p)
t = next_thread(t); t = next_thread(t);
} while (t != p); } 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);
}
} }
} }
...@@ -1466,6 +1484,7 @@ void do_notify_parent(struct task_struct *tsk, int sig) ...@@ -1466,6 +1484,7 @@ void do_notify_parent(struct task_struct *tsk, int sig)
/* FIXME: find out whether or not this is supposed to be c*time. */ /* FIXME: find out whether or not this is supposed to be c*time. */
info.si_utime = tsk->utime; info.si_utime = tsk->utime;
info.si_stime = tsk->stime; info.si_stime = tsk->stime;
k_getrusage(tsk, RUSAGE_BOTH, &info.si_rusage);
status = tsk->exit_code & 0x7f; status = tsk->exit_code & 0x7f;
why = SI_KERNEL; /* shouldn't happen */ why = SI_KERNEL; /* shouldn't happen */
...@@ -1555,9 +1574,16 @@ do_notify_parent_cldstop(struct task_struct *tsk, struct task_struct *parent) ...@@ -1555,9 +1574,16 @@ do_notify_parent_cldstop(struct task_struct *tsk, struct task_struct *parent)
/* FIXME: find out whether or not this is supposed to be c*time. */ /* FIXME: find out whether or not this is supposed to be c*time. */
info.si_utime = tsk->utime; info.si_utime = tsk->utime;
info.si_stime = tsk->stime; info.si_stime = tsk->stime;
k_getrusage(tsk, RUSAGE_BOTH, &info.si_rusage);
info.si_status = tsk->exit_code & 0x7f; info.si_status = (tsk->signal ? tsk->signal->group_exit_code :
info.si_code = CLD_STOPPED; 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; sighand = parent->sighand;
spin_lock_irqsave(&sighand->siglock, flags); spin_lock_irqsave(&sighand->siglock, flags);
...@@ -1623,14 +1649,17 @@ do_signal_stop(int signr) ...@@ -1623,14 +1649,17 @@ do_signal_stop(int signr)
stop_count = --sig->group_stop_count; stop_count = --sig->group_stop_count;
current->exit_code = signr; current->exit_code = signr;
set_current_state(TASK_STOPPED); set_current_state(TASK_STOPPED);
if (stop_count == 0)
sig->stop_state = 1;
spin_unlock_irq(&sighand->siglock); spin_unlock_irq(&sighand->siglock);
} }
else if (thread_group_empty(current)) { else if (thread_group_empty(current)) {
/* /*
* Lock must be held through transition to stopped state. * 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); set_current_state(TASK_STOPPED);
sig->stop_state = 1;
spin_unlock_irq(&sighand->siglock); spin_unlock_irq(&sighand->siglock);
} }
else { else {
...@@ -1696,6 +1725,8 @@ do_signal_stop(int signr) ...@@ -1696,6 +1725,8 @@ do_signal_stop(int signr)
current->exit_code = signr; current->exit_code = signr;
set_current_state(TASK_STOPPED); set_current_state(TASK_STOPPED);
if (stop_count == 0)
sig->stop_state = 1;
spin_unlock_irq(&sighand->siglock); spin_unlock_irq(&sighand->siglock);
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
...@@ -1736,6 +1767,8 @@ static inline int handle_group_stop(void) ...@@ -1736,6 +1767,8 @@ static inline int handle_group_stop(void)
* without any associated signal being in our queue. * without any associated signal being in our queue.
*/ */
stop_count = --current->signal->group_stop_count; stop_count = --current->signal->group_stop_count;
if (stop_count == 0)
current->signal->stop_state = 1;
current->exit_code = current->signal->group_exit_code; current->exit_code = current->signal->group_exit_code;
set_current_state(TASK_STOPPED); set_current_state(TASK_STOPPED);
spin_unlock_irq(&current->sighand->siglock); spin_unlock_irq(&current->sighand->siglock);
...@@ -2098,6 +2131,8 @@ int copy_siginfo_to_user(siginfo_t __user *to, siginfo_t *from) ...@@ -2098,6 +2131,8 @@ int copy_siginfo_to_user(siginfo_t __user *to, siginfo_t *from)
err |= __put_user(from->si_status, &to->si_status); err |= __put_user(from->si_status, &to->si_status);
err |= __put_user(from->si_utime, &to->si_utime); err |= __put_user(from->si_utime, &to->si_utime);
err |= __put_user(from->si_stime, &to->si_stime); err |= __put_user(from->si_stime, &to->si_stime);
err |= __copy_to_user(&to->si_rusage, &from->si_rusage,
sizeof(to->si_rusage));
break; break;
case __SI_RT: /* This is not generated by the kernel as of now. */ case __SI_RT: /* This is not generated by the kernel as of now. */
case __SI_MESGQ: /* But this is */ case __SI_MESGQ: /* But this is */
......
...@@ -1540,37 +1540,43 @@ asmlinkage long sys_setrlimit(unsigned int resource, struct rlimit __user *rlim) ...@@ -1540,37 +1540,43 @@ asmlinkage long sys_setrlimit(unsigned int resource, struct rlimit __user *rlim)
* reaped till shortly after the call to getrusage(), in both cases the * 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. * 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) { switch (who) {
case RUSAGE_SELF: case RUSAGE_SELF:
jiffies_to_timeval(p->utime, &r.ru_utime); jiffies_to_timeval(p->utime, &r->ru_utime);
jiffies_to_timeval(p->stime, &r.ru_stime); jiffies_to_timeval(p->stime, &r->ru_stime);
r.ru_nvcsw = p->nvcsw; r->ru_nvcsw = p->nvcsw;
r.ru_nivcsw = p->nivcsw; r->ru_nivcsw = p->nivcsw;
r.ru_minflt = p->min_flt; r->ru_minflt = p->min_flt;
r.ru_majflt = p->maj_flt; r->ru_majflt = p->maj_flt;
break; break;
case RUSAGE_CHILDREN: case RUSAGE_CHILDREN:
jiffies_to_timeval(p->cutime, &r.ru_utime); jiffies_to_timeval(p->cutime, &r->ru_utime);
jiffies_to_timeval(p->cstime, &r.ru_stime); jiffies_to_timeval(p->cstime, &r->ru_stime);
r.ru_nvcsw = p->cnvcsw; r->ru_nvcsw = p->cnvcsw;
r.ru_nivcsw = p->cnivcsw; r->ru_nivcsw = p->cnivcsw;
r.ru_minflt = p->cmin_flt; r->ru_minflt = p->cmin_flt;
r.ru_majflt = p->cmaj_flt; r->ru_majflt = p->cmaj_flt;
break; break;
default: default:
jiffies_to_timeval(p->utime + p->cutime, &r.ru_utime); jiffies_to_timeval(p->utime + p->cutime, &r->ru_utime);
jiffies_to_timeval(p->stime + p->cstime, &r.ru_stime); jiffies_to_timeval(p->stime + p->cstime, &r->ru_stime);
r.ru_nvcsw = p->nvcsw + p->cnvcsw; r->ru_nvcsw = p->nvcsw + p->cnvcsw;
r.ru_nivcsw = p->nivcsw + p->cnivcsw; r->ru_nivcsw = p->nivcsw + p->cnivcsw;
r.ru_minflt = p->min_flt + p->cmin_flt; r->ru_minflt = p->min_flt + p->cmin_flt;
r.ru_majflt = p->maj_flt + p->cmaj_flt; r->ru_majflt = p->maj_flt + p->cmaj_flt;
break; 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; return copy_to_user(ru, &r, sizeof(r)) ? -EFAULT : 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment