From: Roland McGrath 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: Roland McGrath Signed-off-by: Andrew Morton --- 25-akpm/arch/i386/kernel/entry.S | 1 25-akpm/arch/x86_64/ia32/ia32_signal.c | 2 25-akpm/arch/x86_64/ia32/ia32entry.S | 1 25-akpm/arch/x86_64/ia32/sys_ia32.c | 19 ++ 25-akpm/include/asm-generic/siginfo.h | 3 25-akpm/include/asm-i386/unistd.h | 3 25-akpm/include/asm-x86_64/ia32.h | 1 25-akpm/include/asm-x86_64/ia32_unistd.h | 3 25-akpm/include/asm-x86_64/unistd.h | 4 25-akpm/include/linux/sched.h | 2 25-akpm/include/linux/syscalls.h | 2 25-akpm/include/linux/wait.h | 9 + 25-akpm/kernel/exit.c | 202 ++++++++++++++++++++++++++++--- 25-akpm/kernel/signal.c | 41 +++++- 25-akpm/kernel/sys.c | 50 ++++--- 15 files changed, 296 insertions(+), 47 deletions(-) diff -puN arch/i386/kernel/entry.S~waitid-system-call arch/i386/kernel/entry.S --- 25/arch/i386/kernel/entry.S~waitid-system-call 2004-08-15 17:40:43.656474632 -0700 +++ 25-akpm/arch/i386/kernel/entry.S 2004-08-15 17:41:25.029185024 -0700 @@ -943,5 +943,6 @@ ENTRY(sys_call_table) .long sys_vperfctr_unlink .long sys_vperfctr_iresume .long sys_vperfctr_read + .long sys_waitid syscall_table_size=(.-sys_call_table) diff -puN arch/x86_64/ia32/ia32entry.S~waitid-system-call arch/x86_64/ia32/ia32entry.S --- 25/arch/x86_64/ia32/ia32entry.S~waitid-system-call 2004-08-15 17:40:43.657474480 -0700 +++ 25-akpm/arch/x86_64/ia32/ia32entry.S 2004-08-15 17:41:48.112675800 -0700 @@ -592,6 +592,7 @@ ia32_sys_call_table: .quad sys_vperfctr_unlink .quad sys_vperfctr_iresume .quad sys_vperfctr_read + .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 diff -puN arch/x86_64/ia32/ia32_signal.c~waitid-system-call arch/x86_64/ia32/ia32_signal.c --- 25/arch/x86_64/ia32/ia32_signal.c~waitid-system-call 2004-08-15 17:40:43.659474176 -0700 +++ 25-akpm/arch/x86_64/ia32/ia32_signal.c 2004-08-15 17:40:43.682470680 -0700 @@ -74,6 +74,8 @@ int ia32_copy_siginfo_to_user(siginfo_t3 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 |= __copy_to_user(&to->si_rusage, &from->si_rusage, + sizeof(to->si_rusage)); default: case __SI_KILL >> 16: err |= __put_user(from->si_uid, &to->si_uid); diff -puN arch/x86_64/ia32/sys_ia32.c~waitid-system-call arch/x86_64/ia32/sys_ia32.c --- 25/arch/x86_64/ia32/sys_ia32.c~waitid-system-call 2004-08-15 17:40:43.660474024 -0700 +++ 25-akpm/arch/x86_64/ia32/sys_ia32.c 2004-08-15 17:40:43.684470376 -0700 @@ -1151,6 +1151,25 @@ asmlinkage long sys32_clone(unsigned int 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. */ diff -puN include/asm-generic/siginfo.h~waitid-system-call include/asm-generic/siginfo.h --- 25/include/asm-generic/siginfo.h~waitid-system-call 2004-08-15 17:40:43.662473720 -0700 +++ 25-akpm/include/asm-generic/siginfo.h 2004-08-15 17:40:43.686470072 -0700 @@ -3,6 +3,7 @@ #include #include +#include typedef union sigval { int sival_int; @@ -74,6 +75,7 @@ typedef struct siginfo { int _status; /* exit code */ clock_t _utime; clock_t _stime; + struct rusage _rusage; } _sigchld; /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ @@ -105,6 +107,7 @@ typedef struct siginfo { #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 diff -puN include/asm-i386/unistd.h~waitid-system-call include/asm-i386/unistd.h --- 25/include/asm-i386/unistd.h~waitid-system-call 2004-08-15 17:40:43.663473568 -0700 +++ 25-akpm/include/asm-i386/unistd.h 2004-08-15 17:42:29.194430424 -0700 @@ -295,8 +295,9 @@ #define __NR_vperfctr_unlink (__NR_perfctr_info+3) #define __NR_vperfctr_iresume (__NR_perfctr_info+4) #define __NR_vperfctr_read (__NR_perfctr_info+5) +#define __NR_waitid 290 -#define NR_syscalls 290 +#define NR_syscalls 291 /* user-visible error numbers are in the range -1 - -124: see */ diff -puN include/asm-x86_64/ia32.h~waitid-system-call include/asm-x86_64/ia32.h --- 25/include/asm-x86_64/ia32.h~waitid-system-call 2004-08-15 17:40:43.664473416 -0700 +++ 25-akpm/include/asm-x86_64/ia32.h 2004-08-15 17:40:43.689469616 -0700 @@ -115,6 +115,7 @@ typedef struct siginfo32 { int _status; /* exit code */ compat_clock_t _utime; compat_clock_t _stime; + struct compat_rusage _rusage; } _sigchld; /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ diff -puN include/asm-x86_64/ia32_unistd.h~waitid-system-call include/asm-x86_64/ia32_unistd.h --- 25/include/asm-x86_64/ia32_unistd.h~waitid-system-call 2004-08-15 17:40:43.666473112 -0700 +++ 25-akpm/include/asm-x86_64/ia32_unistd.h 2004-08-15 17:42:59.604807344 -0700 @@ -295,7 +295,8 @@ #define __NR_ia32_vperfctr_unlink (__NR_ia32_perfctr_info+3) #define __NR_ia32_vperfctr_iresume (__NR_ia32_perfctr_info+4) #define __NR_ia32_vperfctr_read (__NR_ia32_perfctr_info+5) +#define __NR_ia32_waitid 290 -#define IA32_NR_syscalls 290 /* must be > than biggest syscall! */ +#define IA32_NR_syscalls 291 /* must be > than biggest syscall! */ #endif /* _ASM_X86_64_IA32_UNISTD_H_ */ diff -puN include/asm-x86_64/unistd.h~waitid-system-call include/asm-x86_64/unistd.h --- 25/include/asm-x86_64/unistd.h~waitid-system-call 2004-08-15 17:40:43.667472960 -0700 +++ 25-akpm/include/asm-x86_64/unistd.h 2004-08-15 17:44:03.130150024 -0700 @@ -566,8 +566,10 @@ __SYSCALL(__NR_vperfctr_unlink, sys_vper __SYSCALL(__NR_vperfctr_iresume, sys_vperfctr_iresume) #define __NR_vperfctr_read (__NR_perfctr_info+5) __SYSCALL(__NR_vperfctr_read, sys_vperfctr_read) +#define __NR_waitid (253) +__SYSCALL(__NR_waitid, sys_waitid) -#define __NR_syscall_max __NR_vperfctr_read +#define __NR_syscall_max __NR_waitid #ifndef __NO_STUBS /* user-visible error numbers are in the range -1 - -4095 */ diff -puN include/linux/sched.h~waitid-system-call include/linux/sched.h --- 25/include/linux/sched.h~waitid-system-call 2004-08-15 17:40:43.669472656 -0700 +++ 25-akpm/include/linux/sched.h 2004-08-15 17:40:43.693469008 -0700 @@ -287,6 +287,8 @@ struct signal_struct { /* 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; diff -puN include/linux/syscalls.h~waitid-system-call include/linux/syscalls.h --- 25/include/linux/syscalls.h~waitid-system-call 2004-08-15 17:40:43.670472504 -0700 +++ 25-akpm/include/linux/syscalls.h 2004-08-15 17:40:43.691469312 -0700 @@ -162,6 +162,8 @@ asmlinkage long sys_exit(int error_code) 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, diff -puN include/linux/wait.h~waitid-system-call include/linux/wait.h --- 25/include/linux/wait.h~waitid-system-call 2004-08-15 17:40:43.672472200 -0700 +++ 25-akpm/include/linux/wait.h 2004-08-15 17:40:43.692469160 -0700 @@ -3,11 +3,20 @@ #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 diff -puN kernel/exit.c~waitid-system-call kernel/exit.c --- 25/kernel/exit.c~waitid-system-call 2004-08-15 17:40:43.673472048 -0700 +++ 25-akpm/kernel/exit.c 2004-08-15 17:40:43.696468552 -0700 @@ -971,16 +971,64 @@ static int eligible_child(pid_t pid, int 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 @@ -1005,12 +1053,32 @@ static int wait_task_zombie(task_t *p, u 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; @@ -1046,9 +1114,9 @@ static int wait_task_zombie(task_t *p, u * 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; @@ -1071,6 +1139,20 @@ static int wait_task_stopped(task_t *p, */ 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); /* @@ -1096,6 +1178,7 @@ static int wait_task_stopped(task_t *p, * resumed, or it resumed and then died. */ write_unlock_irq(&tasklist_lock); + bail_ref: put_task_struct(p); read_lock(&tasklist_lock); return 0; @@ -1110,6 +1193,20 @@ static int wait_task_stopped(task_t *p, 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); @@ -1118,15 +1215,13 @@ static int wait_task_stopped(task_t *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; @@ -1152,19 +1247,51 @@ repeat: !(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; + 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; } } @@ -1187,20 +1314,57 @@ repeat: 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 && retval > 0) + retval = 0; 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 /* diff -puN kernel/signal.c~waitid-system-call kernel/signal.c --- 25/kernel/signal.c~waitid-system-call 2004-08-15 17:40:43.675471744 -0700 +++ 25-akpm/kernel/signal.c 2004-08-15 17:40:43.699468096 -0700 @@ -26,6 +26,8 @@ #include #include +extern void k_getrusage(struct task_struct *, int, struct rusage *); + /* * SLAB caches for signal bits. */ @@ -660,6 +662,7 @@ static void handle_stop_signal(int sig, * 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 @@ -696,6 +699,21 @@ static void handle_stop_signal(int sig, 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); + } } } @@ -1447,6 +1465,7 @@ void do_notify_parent(struct task_struct /* 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 */ @@ -1536,9 +1555,16 @@ do_notify_parent_cldstop(struct task_str /* 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); @@ -1604,14 +1630,17 @@ do_signal_stop(int signr) 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 { @@ -1677,6 +1706,8 @@ do_signal_stop(int signr) 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); @@ -1717,6 +1748,8 @@ static inline int handle_group_stop(void * 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); @@ -2079,6 +2112,8 @@ int copy_siginfo_to_user(siginfo_t __use 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 */ diff -puN kernel/sys.c~waitid-system-call kernel/sys.c --- 25/kernel/sys.c~waitid-system-call 2004-08-15 17:40:43.676471592 -0700 +++ 25-akpm/kernel/sys.c 2004-08-15 17:40:43.700467944 -0700 @@ -1566,37 +1566,43 @@ asmlinkage long sys_setrlimit(unsigned i * 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; } _