* [PATCH] 1/3 Add pselect/ppoll() system calls [not found] <1133225007.31573.86.camel@baythorne.infradead.org> @ 2005-11-29 0:57 ` David Woodhouse 2005-11-29 0:57 ` David Woodhouse 2005-11-29 0:57 ` David Woodhouse 2 siblings, 0 replies; 14+ messages in thread From: David Woodhouse @ 2005-11-29 0:57 UTC (permalink / raw) To: linux-kernel; +Cc: akpm, linuxppc-dev, drepper This patch adds the pselect() and ppoll() system calls. Most of this implementation is as it was in the -mm kernel for a period of time already before the difficulties with signal delivery became apparent. Signed-off-by: David Woodhouse <dwmw2@infradead.org> diff --git a/fs/select.c b/fs/select.c index f10a103..4e0da96 100644 --- a/fs/select.c +++ b/fs/select.c @@ -179,12 +179,11 @@ get_max: #define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR) #define POLLEX_SET (POLLPRI) -int do_select(int n, fd_set_bits *fds, long *timeout) +int do_select(int n, fd_set_bits *fds, int64_t *timeout) { struct poll_wqueues table; poll_table *wait; int retval, i; - long __timeout = *timeout; rcu_read_lock(); retval = max_select_fd(n, fds); @@ -196,11 +195,12 @@ int do_select(int n, fd_set_bits *fds, l poll_initwait(&table); wait = &table.pt; - if (!__timeout) + if (!*timeout) wait = NULL; retval = 0; for (;;) { unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp; + long __timeout; set_current_state(TASK_INTERRUPTIBLE); @@ -255,22 +255,32 @@ int do_select(int n, fd_set_bits *fds, l *rexp = res_ex; } wait = NULL; - if (retval || !__timeout || signal_pending(current)) + if (retval || !*timeout || signal_pending(current)) break; if(table.error) { retval = table.error; break; } + + if (*timeout < 0) { + /* Wait indefinitely */ + __timeout = MAX_SCHEDULE_TIMEOUT; + } else if (unlikely(*timeout >= (int64_t)MAX_SCHEDULE_TIMEOUT - 1)) { + /* Wait for longer than MAX_SCHEDULE_TIMEOUT. Do it in a loop */ + __timeout = MAX_SCHEDULE_TIMEOUT - 1; + *timeout -= __timeout; + } else { + __timeout = *timeout; + *timeout = 0; + } __timeout = schedule_timeout(__timeout); + if (*timeout >= 0) + *timeout += __timeout; } __set_current_state(TASK_RUNNING); poll_freewait(&table); - /* - * Up-to-date the caller timeout. - */ - *timeout = __timeout; return retval; } @@ -295,36 +305,14 @@ static void select_bits_free(void *bits, #define MAX_SELECT_SECONDS \ ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1) -asmlinkage long -sys_select(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, struct timeval __user *tvp) +static int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp, + fd_set __user *exp, int64_t *timeout) { fd_set_bits fds; char *bits; - long timeout; int ret, size, max_fdset; struct fdtable *fdt; - timeout = MAX_SCHEDULE_TIMEOUT; - if (tvp) { - time_t sec, usec; - - if (!access_ok(VERIFY_READ, tvp, sizeof(*tvp)) - || __get_user(sec, &tvp->tv_sec) - || __get_user(usec, &tvp->tv_usec)) { - ret = -EFAULT; - goto out_nofds; - } - - ret = -EINVAL; - if (sec < 0 || usec < 0) - goto out_nofds; - - if ((unsigned long) sec < MAX_SELECT_SECONDS) { - timeout = ROUND_UP(usec, 1000000/HZ); - timeout += sec * (unsigned long) HZ; - } - } - ret = -EINVAL; if (n < 0) goto out_nofds; @@ -362,18 +350,7 @@ sys_select(int n, fd_set __user *inp, fd zero_fd_set(n, fds.res_out); zero_fd_set(n, fds.res_ex); - ret = do_select(n, &fds, &timeout); - - if (tvp && !(current->personality & STICKY_TIMEOUTS)) { - time_t sec = 0, usec = 0; - if (timeout) { - sec = timeout / HZ; - usec = timeout % HZ; - usec *= (1000000/HZ); - } - put_user(sec, &tvp->tv_sec); - put_user(usec, &tvp->tv_usec); - } + ret = do_select(n, &fds, timeout); if (ret < 0) goto out; @@ -395,6 +372,150 @@ out_nofds: return ret; } +asmlinkage long sys_select(int n, fd_set __user *inp, fd_set __user *outp, + fd_set __user *exp, struct timeval __user *tvp) +{ + int64_t timeout = -1; + struct timeval tv; + int ret; + + if (tvp) { + if (copy_from_user(&tv, tvp, sizeof(tv))) + return -EFAULT; + + if (tv.tv_sec < 0 || tv.tv_usec < 0) + return -EINVAL; + + /* Cast to uint64_t to make GCC stop complaining */ + if ((uint64_t)tv.tv_sec >= (uint64_t)MAX_INT64_SECONDS) + timeout = -1; /* infinite */ + else { + timeout = ROUND_UP(tv.tv_sec, 1000000/HZ); + timeout += tv.tv_sec * HZ; + } + } + + ret = core_sys_select(n, inp, outp, exp, &timeout); + + if (tvp) { + if (current->personality & STICKY_TIMEOUTS) + goto sticky; + tv.tv_usec = jiffies_to_usecs(do_div((*(uint64_t*)&timeout), HZ)); + tv.tv_sec = timeout; + if (copy_to_user(tvp, &tv, sizeof(tv))) { + sticky: + /* + * If an application puts its timeval in read-only + * memory, we don't want the Linux-specific update to + * the timeval to cause a fault after the select has + * completed successfully. However, because we're not + * updating the timeval, we can't restart the system + * call. + */ + if (ret == -ERESTARTNOHAND) + ret = -EINTR; + } + } + + return ret; +} + +#ifdef TIF_RESTORE_SIGMASK +asmlinkage long sys_pselect7(int n, fd_set __user *inp, fd_set __user *outp, + fd_set __user *exp, struct timespec __user *tsp, + const sigset_t __user *sigmask, size_t sigsetsize) +{ + int64_t timeout = MAX_SCHEDULE_TIMEOUT; + sigset_t ksigmask, sigsaved; + struct timespec ts; + int ret; + + if (tsp) { + if (copy_from_user(&ts, tsp, sizeof(ts))) + return -EFAULT; + + if (ts.tv_sec < 0 || ts.tv_nsec < 0) + return -EINVAL; + + /* Cast to uint64_t to make GCC stop complaining */ + if ((uint64_t)ts.tv_sec >= (uint64_t)MAX_INT64_SECONDS) + timeout = -1; /* infinite */ + else { + timeout = ROUND_UP(ts.tv_sec, 1000000000/HZ); + timeout += ts.tv_sec * HZ; + } + } + + if (sigmask) { + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + if (copy_from_user(&ksigmask, sigmask, sizeof(ksigmask))) + return -EFAULT; + + sigdelsetmask(&ksigmask, sigmask(SIGKILL)|sigmask(SIGSTOP)); + sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved); + } + + ret = core_sys_select(n, inp, outp, exp, &timeout); + + if (tsp) { + if (current->personality & STICKY_TIMEOUTS) + goto sticky; + ts.tv_nsec = jiffies_to_usecs(do_div((*(uint64_t*)&timeout), HZ)) * 1000; + ts.tv_sec = timeout; + if (copy_to_user(tsp, &ts, sizeof(ts))) { + sticky: + /* + * If an application puts its timeval in read-only + * memory, we don't want the Linux-specific update to + * the timeval to cause a fault after the select has + * completed successfully. However, because we're not + * updating the timeval, we can't restart the system + * call. + */ + if (ret == -ERESTARTNOHAND) + ret = -EINTR; + } + } + + if (ret == -ERESTARTNOHAND) { + /* Don't restore the signal mask yet. Let do_signal() deliver the signal + on the way back to userspace, before the signal mask is restored. */ + if (sigmask) { + memcpy(¤t->saved_sigmask, &sigsaved, sizeof(sigsaved)); + set_thread_flag(TIF_RESTORE_SIGMASK); + } + } else if (sigmask) + sigprocmask(SIG_SETMASK, &sigsaved, NULL); + + return ret; +} + +/* + * Most architectures can't handle 7-argument syscalls. So we provide a + * 6-argument version where the sixth argument is a pointer to a structure + * which has a pointer to the sigset_t itself followed by a size_t containing + * the sigset size. + */ +asmlinkage long sys_pselect6(int n, fd_set __user *inp, fd_set __user *outp, + fd_set __user *exp, struct timespec __user *tsp, void __user *sig) +{ + size_t sigsetsize = 0; + sigset_t __user *up = NULL; + + if (sig) { + if (!access_ok(VERIFY_READ, sig, sizeof(void *)+sizeof(size_t)) + || __get_user(up, (sigset_t * __user *)sig) + || __get_user(sigsetsize, + (size_t * __user)(sig+sizeof(void *)))) + return -EFAULT; + } + + return sys_pselect7(n, inp, outp, exp, tsp, up, sigsetsize); +} +#endif /* TIF_RESTORE_SIGMASK */ + struct poll_list { struct poll_list *next; int len; @@ -436,16 +557,19 @@ static void do_pollfd(unsigned int num, } static int do_poll(unsigned int nfds, struct poll_list *list, - struct poll_wqueues *wait, long timeout) + struct poll_wqueues *wait, int64_t *timeout) { int count = 0; poll_table* pt = &wait->pt; - if (!timeout) + /* Optimise the no-wait case */ + if (!(*timeout)) pt = NULL; for (;;) { struct poll_list *walk; + long __timeout; + set_current_state(TASK_INTERRUPTIBLE); walk = list; while(walk != NULL) { @@ -453,18 +577,33 @@ static int do_poll(unsigned int nfds, s walk = walk->next; } pt = NULL; - if (count || !timeout || signal_pending(current)) + if (count || !*timeout || signal_pending(current)) break; count = wait->error; if (count) break; - timeout = schedule_timeout(timeout); + + if (*timeout < 0) { + /* Wait indefinitely */ + __timeout = MAX_SCHEDULE_TIMEOUT; + } else if (unlikely(*timeout >= (int64_t)MAX_SCHEDULE_TIMEOUT - 1)) { + /* Wait for longer than MAX_SCHEDULE_TIMEOUT. Do it in a loop */ + __timeout = MAX_SCHEDULE_TIMEOUT - 1; + *timeout -= __timeout; + } else { + __timeout = *timeout; + *timeout = 0; + } + + __timeout = schedule_timeout(__timeout); + if (*timeout >= 0) + *timeout += __timeout; } __set_current_state(TASK_RUNNING); return count; } -asmlinkage long sys_poll(struct pollfd __user * ufds, unsigned int nfds, long timeout) +int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, int64_t *timeout) { struct poll_wqueues table; int fdcount, err; @@ -482,14 +621,6 @@ asmlinkage long sys_poll(struct pollfd _ if (nfds > max_fdset && nfds > OPEN_MAX) return -EINVAL; - if (timeout) { - /* Careful about overflow in the intermediate values */ - if ((unsigned long) timeout < MAX_SCHEDULE_TIMEOUT / HZ) - timeout = (unsigned long)(timeout*HZ+999)/1000+1; - else /* Negative or overflow */ - timeout = MAX_SCHEDULE_TIMEOUT; - } - poll_initwait(&table); head = NULL; @@ -519,6 +650,7 @@ asmlinkage long sys_poll(struct pollfd _ } i -= pp->len; } + fdcount = do_poll(nfds, head, &table, timeout); /* OK, now copy the revents fields back to user space. */ @@ -547,3 +679,94 @@ out_fds: poll_freewait(&table); return err; } + +asmlinkage long sys_poll(struct pollfd __user *ufds, unsigned int nfds, + long timeout_msecs) +{ + int64_t timeout_jiffies = 0; + + if (timeout_msecs) { +#if HZ > 1000 + /* We can only overflow if HZ > 1000 */ + if (timeout_msecs / 1000 > (int64_t)0x7fffffffffffffffULL / (int64_t)HZ) + timeout_jiffies = -1; + else +#endif + timeout_jiffies = msecs_to_jiffies(timeout_msecs); + } + + return do_sys_poll(ufds, nfds, &timeout_jiffies); +} + +#ifdef TIF_RESTORE_SIGMASK +asmlinkage long sys_ppoll(struct pollfd __user *ufds, unsigned int nfds, + struct timespec __user *tsp, const sigset_t __user *sigmask, + size_t sigsetsize) +{ + sigset_t ksigmask, sigsaved; + struct timespec ts; + int64_t timeout = -1; + int ret; + + if (tsp) { + if (copy_from_user(&ts, tsp, sizeof(ts))) + return -EFAULT; + + /* Cast to uint64_t to make GCC stop complaining */ + if ((uint64_t)ts.tv_sec >= (uint64_t)MAX_INT64_SECONDS) + timeout = -1; /* infinite */ + else { + timeout = ROUND_UP(ts.tv_sec, 1000000000/HZ); + timeout += ts.tv_sec * HZ; + } + } + + if (sigmask) { + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + if (copy_from_user(&ksigmask, sigmask, sizeof(ksigmask))) + return -EFAULT; + + sigdelsetmask(&ksigmask, sigmask(SIGKILL)|sigmask(SIGSTOP)); + sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved); + } + + ret = do_sys_poll(ufds, nfds, &timeout); + + /* We can restart this syscall, usually */ + if (ret == -EINTR) { + /* Don't restore the signal mask yet. Let do_signal() deliver the signal + on the way back to userspace, before the signal mask is restored. */ + if (sigmask) { + memcpy(¤t->saved_sigmask, &sigsaved, sizeof(sigsaved)); + set_thread_flag(TIF_RESTORE_SIGMASK); + } + ret = -ERESTARTNOHAND; + } else if (sigmask) + sigprocmask(SIG_SETMASK, &sigsaved, NULL); + + if (tsp && timeout >= 0) { + if (current->personality & STICKY_TIMEOUTS) + goto sticky; + /* Yes, we know it's actually an int64_t, but it's also positive. */ + ts.tv_nsec = jiffies_to_usecs(do_div((*(uint64_t*)&timeout), HZ)) * 1000; + ts.tv_sec = timeout; + if (copy_to_user(tsp, &ts, sizeof(ts))) { + sticky: + /* + * If an application puts its timeval in read-only + * memory, we don't want the Linux-specific update to + * the timeval to cause a fault after the select has + * completed successfully. However, because we're not + * updating the timeval, we can't restart the system + * call. + */ + if (ret == -ERESTARTNOHAND && timeout >= 0) + ret = -EINTR; + } + } + + return ret; +} +#endif /* TIF_RESTORE_SIGMASK */ --- a/fs/compat.c +++ b/fs/compat.c @@ -53,6 +53,8 @@ #include <asm/mmu_context.h> #include <asm/ioctls.h> +extern void sigset_from_compat(sigset_t *set, compat_sigset_t *compat); + /* * Not all architectures have sys_utime, so implement this in terms * of sys_utimes. @@ -1607,36 +1609,14 @@ static void select_bits_free(void *bits, #define MAX_SELECT_SECONDS \ ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1) -asmlinkage long -compat_sys_select(int n, compat_ulong_t __user *inp, compat_ulong_t __user *outp, - compat_ulong_t __user *exp, struct compat_timeval __user *tvp) +int compat_core_sys_select(int n, compat_ulong_t __user *inp, + compat_ulong_t __user *outp, compat_ulong_t __user *exp, int64_t *timeout) { fd_set_bits fds; char *bits; - long timeout; int size, max_fdset, ret = -EINVAL; struct fdtable *fdt; - timeout = MAX_SCHEDULE_TIMEOUT; - if (tvp) { - time_t sec, usec; - - if (!access_ok(VERIFY_READ, tvp, sizeof(*tvp)) - || __get_user(sec, &tvp->tv_sec) - || __get_user(usec, &tvp->tv_usec)) { - ret = -EFAULT; - goto out_nofds; - } - - if (sec < 0 || usec < 0) - goto out_nofds; - - if ((unsigned long) sec < MAX_SELECT_SECONDS) { - timeout = ROUND_UP(usec, 1000000/HZ); - timeout += sec * (unsigned long) HZ; - } - } - if (n < 0) goto out_nofds; @@ -1673,19 +1653,7 @@ compat_sys_select(int n, compat_ulong_t zero_fd_set(n, fds.res_out); zero_fd_set(n, fds.res_ex); - ret = do_select(n, &fds, &timeout); - - if (tvp && !(current->personality & STICKY_TIMEOUTS)) { - time_t sec = 0, usec = 0; - if (timeout) { - sec = timeout / HZ; - usec = timeout % HZ; - usec *= (1000000/HZ); - } - if (put_user(sec, &tvp->tv_sec) || - put_user(usec, &tvp->tv_usec)) - ret = -EFAULT; - } + ret = do_select(n, &fds, timeout); if (ret < 0) goto out; @@ -1706,6 +1674,216 @@ out_nofds: return ret; } +asmlinkage long compat_sys_select(int n, compat_ulong_t __user *inp, + compat_ulong_t __user *outp, compat_ulong_t __user *exp, + struct compat_timeval __user *tvp) +{ + int64_t timeout = -1; + struct compat_timeval tv; + int ret; + + if (tvp) { + if (copy_from_user(&tv, tvp, sizeof(tv))) + return -EFAULT; + + if (tv.tv_sec < 0 || tv.tv_usec < 0) + return -EINVAL; + + /* Cast to uint64_t to make GCC stop complaining */ + if ((uint64_t)tv.tv_sec >= (uint64_t)MAX_INT64_SECONDS) + timeout = -1; /* infinite */ + else { + timeout = ROUND_UP(tv.tv_sec, 1000000/HZ); + timeout += tv.tv_sec * HZ; + } + } + + ret = compat_core_sys_select(n, inp, outp, exp, &timeout); + + if (tvp) { + if (current->personality & STICKY_TIMEOUTS) + goto sticky; + tv.tv_usec = jiffies_to_usecs(do_div((*(uint64_t*)&timeout), HZ)); + tv.tv_sec = timeout; + if (copy_to_user(tvp, &tv, sizeof(tv))) { + sticky: + /* + * If an application puts its timeval in read-only + * memory, we don't want the Linux-specific update to + * the timeval to cause a fault after the select has + * completed successfully. However, because we're not + * updating the timeval, we can't restart the system + * call. + */ + if (ret == -ERESTARTNOHAND) + ret = -EINTR; + } + } + + return ret; +} + +#ifdef TIF_RESTORE_SIGMASK +asmlinkage long compat_sys_pselect7(int n, compat_ulong_t __user *inp, + compat_ulong_t __user *outp, compat_ulong_t __user *exp, + struct compat_timespec __user *tsp, compat_sigset_t __user *sigmask, + compat_size_t sigsetsize) +{ + compat_sigset_t s32; + sigset_t ksigmask, sigsaved; + long timeout = MAX_SCHEDULE_TIMEOUT; + struct compat_timespec ts; + int ret; + + if (tsp) { + if (copy_from_user(&ts, tsp, sizeof(ts))) + return -EFAULT; + + if (ts.tv_sec < 0 || ts.tv_nsec < 0) + return -EINVAL; + } + + if (sigmask) { + if (sigsetsize != sizeof(compat_sigset_t)) + return -EINVAL; + if (copy_from_user(&s32, sigmask, sizeof(s32))) + return -EFAULT; + sigset_from_compat(&ksigmask, &s32); + + sigdelsetmask(&ksigmask, sigmask(SIGKILL)|sigmask(SIGSTOP)); + sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved); + } + + do { + if (tsp) { + if ((unsigned long)ts.tv_sec < MAX_SELECT_SECONDS) { + timeout = ROUND_UP(ts.tv_nsec, 1000000000/HZ); + timeout += ts.tv_sec * (unsigned long)HZ; + ts.tv_sec = 0; + ts.tv_nsec = 0; + } else { + ts.tv_sec -= MAX_SELECT_SECONDS; + timeout = MAX_SELECT_SECONDS * HZ; + } + } + + ret = compat_core_sys_select(n, inp, outp, exp, &timeout); + + } while (!ret && !timeout && tsp && (ts.tv_sec || ts.tv_nsec)); + + if (tsp && !(current->personality & STICKY_TIMEOUTS)) { + ts.tv_sec += timeout / HZ; + ts.tv_nsec += (timeout % HZ) * (1000000000/HZ); + if (ts.tv_nsec >= 1000000000) { + ts.tv_sec++; + ts.tv_nsec -= 1000000000; + } + (void)copy_to_user(tsp, &ts, sizeof(ts)); + } + + if (ret == -ERESTARTNOHAND) { + /* Don't restore the signal mask yet. Let do_signal() deliver the signal + on the way back to userspace, before the signal mask is restored. */ + if (sigmask) { + memcpy(¤t->saved_sigmask, &sigsaved, sizeof(sigsaved)); + set_thread_flag(TIF_RESTORE_SIGMASK); + } + } else if (sigmask) + sigprocmask(SIG_SETMASK, &sigsaved, NULL); + + return ret; +} + +asmlinkage long compat_sys_pselect6(int n, compat_ulong_t __user *inp, + compat_ulong_t __user *outp, compat_ulong_t __user *exp, + struct compat_timespec __user *tsp, void __user *sig) +{ + compat_size_t sigsetsize = 0; + compat_uptr_t up = 0; + + if (sig) { + if (!access_ok(VERIFY_READ, sig, + sizeof(compat_uptr_t)+sizeof(compat_size_t)) || + __get_user(up, (compat_uptr_t __user *)sig) || + __get_user(sigsetsize, + (compat_size_t __user *)(sig+sizeof(up)))) + return -EFAULT; + } + return compat_sys_pselect7(n, inp, outp, exp, tsp, compat_ptr(up), + sigsetsize); +} + +asmlinkage long compat_sys_ppoll(struct pollfd __user *ufds, + unsigned int nfds, struct compat_timespec __user *tsp, + const compat_sigset_t __user *sigmask, compat_size_t sigsetsize) +{ + compat_sigset_t s32; + sigset_t ksigmask, sigsaved; + struct compat_timespec ts; + int64_t timeout = -1; + int ret; + + if (tsp) { + if (copy_from_user(&ts, tsp, sizeof(ts))) + return -EFAULT; + + /* We assume that ts.tv_sec is always lower than + the number of seconds that can be expressed in + an int64_t. Otherwise the compiler bitches at us */ + timeout = ROUND_UP(ts.tv_sec, 1000000000/HZ); + timeout += ts.tv_sec * HZ; + } + + if (sigmask) { + if (sigsetsize |= sizeof(compat_sigset_t)) + return -EINVAL; + if (copy_from_user(&s32, sigmask, sizeof(s32))) + return -EFAULT; + sigset_from_compat(&ksigmask, &s32); + + sigdelsetmask(&ksigmask, sigmask(SIGKILL)|sigmask(SIGSTOP)); + sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved); + } + + ret = do_sys_poll(ufds, nfds, &timeout); + + /* We can restart this syscall, usually */ + if (ret == -EINTR) { + /* Don't restore the signal mask yet. Let do_signal() deliver the signal + on the way back to userspace, before the signal mask is restored. */ + if (sigmask) { + memcpy(¤t->saved_sigmask, &sigsaved, sizeof(sigsaved)); + set_thread_flag(TIF_RESTORE_SIGMASK); + } + ret = -ERESTARTNOHAND; + } else if (sigmask) + sigprocmask(SIG_SETMASK, &sigsaved, NULL); + + if (tsp && timeout >= 0) { + if (current->personality & STICKY_TIMEOUTS) + goto sticky; + /* Yes, we know it's actually an int64_t, but it's also positive. */ + ts.tv_nsec = jiffies_to_usecs(do_div((*(uint64_t*)&timeout), HZ)) * 1000; + ts.tv_sec = timeout; + if (copy_to_user(tsp, &ts, sizeof(ts))) { + sticky: + /* + * If an application puts its timeval in read-only + * memory, we don't want the Linux-specific update to + * the timeval to cause a fault after the select has + * completed successfully. However, because we're not + * updating the timeval, we can't restart the system + * call. + */ + if (ret == -ERESTARTNOHAND && timeout >= 0) + ret = -EINTR; + } + } + + return ret; +} +#endif /* TIF_RESTORE_SIGMASK */ + #if defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE) /* Stuff for NFS server syscalls... */ struct compat_nfsctl_svc { diff --git a/include/linux/poll.h b/include/linux/poll.h index f6da702..5b9c85c 100644 --- a/include/linux/poll.h +++ b/include/linux/poll.h @@ -92,7 +92,11 @@ void zero_fd_set(unsigned long nr, unsig memset(fdset, 0, FDS_BYTES(nr)); } -extern int do_select(int n, fd_set_bits *fds, long *timeout); +#define MAX_INT64_SECONDS (((int64_t)(~((uint64_t)0)>>1)/HZ)-1) + +extern int do_select(int n, fd_set_bits *fds, int64_t *timeout); +extern int do_sys_poll(struct pollfd __user * ufds, unsigned int nfds, + int64_t *timeout); #endif /* KERNEL */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 2038bd2..bf7db28 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -800,6 +800,7 @@ struct task_struct { struct sighand_struct *sighand; sigset_t blocked, real_blocked; + sigset_t saved_sigmask; /* To be restored with TIF_RESTORE_SIGMASK */ struct sigpending pending; unsigned long sas_ss_sp; -- dwmw2 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH] 1/3 Add pselect/ppoll() system calls @ 2005-11-29 0:57 ` David Woodhouse 0 siblings, 0 replies; 14+ messages in thread From: David Woodhouse @ 2005-11-29 0:57 UTC (permalink / raw) To: linux-kernel; +Cc: drepper, linuxppc-dev, akpm This patch adds the pselect() and ppoll() system calls. Most of this implementation is as it was in the -mm kernel for a period of time already before the difficulties with signal delivery became apparent. Signed-off-by: David Woodhouse <dwmw2@infradead.org> diff --git a/fs/select.c b/fs/select.c index f10a103..4e0da96 100644 --- a/fs/select.c +++ b/fs/select.c @@ -179,12 +179,11 @@ get_max: #define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR) #define POLLEX_SET (POLLPRI) -int do_select(int n, fd_set_bits *fds, long *timeout) +int do_select(int n, fd_set_bits *fds, int64_t *timeout) { struct poll_wqueues table; poll_table *wait; int retval, i; - long __timeout = *timeout; rcu_read_lock(); retval = max_select_fd(n, fds); @@ -196,11 +195,12 @@ int do_select(int n, fd_set_bits *fds, l poll_initwait(&table); wait = &table.pt; - if (!__timeout) + if (!*timeout) wait = NULL; retval = 0; for (;;) { unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp; + long __timeout; set_current_state(TASK_INTERRUPTIBLE); @@ -255,22 +255,32 @@ int do_select(int n, fd_set_bits *fds, l *rexp = res_ex; } wait = NULL; - if (retval || !__timeout || signal_pending(current)) + if (retval || !*timeout || signal_pending(current)) break; if(table.error) { retval = table.error; break; } + + if (*timeout < 0) { + /* Wait indefinitely */ + __timeout = MAX_SCHEDULE_TIMEOUT; + } else if (unlikely(*timeout >= (int64_t)MAX_SCHEDULE_TIMEOUT - 1)) { + /* Wait for longer than MAX_SCHEDULE_TIMEOUT. Do it in a loop */ + __timeout = MAX_SCHEDULE_TIMEOUT - 1; + *timeout -= __timeout; + } else { + __timeout = *timeout; + *timeout = 0; + } __timeout = schedule_timeout(__timeout); + if (*timeout >= 0) + *timeout += __timeout; } __set_current_state(TASK_RUNNING); poll_freewait(&table); - /* - * Up-to-date the caller timeout. - */ - *timeout = __timeout; return retval; } @@ -295,36 +305,14 @@ static void select_bits_free(void *bits, #define MAX_SELECT_SECONDS \ ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1) -asmlinkage long -sys_select(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, struct timeval __user *tvp) +static int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp, + fd_set __user *exp, int64_t *timeout) { fd_set_bits fds; char *bits; - long timeout; int ret, size, max_fdset; struct fdtable *fdt; - timeout = MAX_SCHEDULE_TIMEOUT; - if (tvp) { - time_t sec, usec; - - if (!access_ok(VERIFY_READ, tvp, sizeof(*tvp)) - || __get_user(sec, &tvp->tv_sec) - || __get_user(usec, &tvp->tv_usec)) { - ret = -EFAULT; - goto out_nofds; - } - - ret = -EINVAL; - if (sec < 0 || usec < 0) - goto out_nofds; - - if ((unsigned long) sec < MAX_SELECT_SECONDS) { - timeout = ROUND_UP(usec, 1000000/HZ); - timeout += sec * (unsigned long) HZ; - } - } - ret = -EINVAL; if (n < 0) goto out_nofds; @@ -362,18 +350,7 @@ sys_select(int n, fd_set __user *inp, fd zero_fd_set(n, fds.res_out); zero_fd_set(n, fds.res_ex); - ret = do_select(n, &fds, &timeout); - - if (tvp && !(current->personality & STICKY_TIMEOUTS)) { - time_t sec = 0, usec = 0; - if (timeout) { - sec = timeout / HZ; - usec = timeout % HZ; - usec *= (1000000/HZ); - } - put_user(sec, &tvp->tv_sec); - put_user(usec, &tvp->tv_usec); - } + ret = do_select(n, &fds, timeout); if (ret < 0) goto out; @@ -395,6 +372,150 @@ out_nofds: return ret; } +asmlinkage long sys_select(int n, fd_set __user *inp, fd_set __user *outp, + fd_set __user *exp, struct timeval __user *tvp) +{ + int64_t timeout = -1; + struct timeval tv; + int ret; + + if (tvp) { + if (copy_from_user(&tv, tvp, sizeof(tv))) + return -EFAULT; + + if (tv.tv_sec < 0 || tv.tv_usec < 0) + return -EINVAL; + + /* Cast to uint64_t to make GCC stop complaining */ + if ((uint64_t)tv.tv_sec >= (uint64_t)MAX_INT64_SECONDS) + timeout = -1; /* infinite */ + else { + timeout = ROUND_UP(tv.tv_sec, 1000000/HZ); + timeout += tv.tv_sec * HZ; + } + } + + ret = core_sys_select(n, inp, outp, exp, &timeout); + + if (tvp) { + if (current->personality & STICKY_TIMEOUTS) + goto sticky; + tv.tv_usec = jiffies_to_usecs(do_div((*(uint64_t*)&timeout), HZ)); + tv.tv_sec = timeout; + if (copy_to_user(tvp, &tv, sizeof(tv))) { + sticky: + /* + * If an application puts its timeval in read-only + * memory, we don't want the Linux-specific update to + * the timeval to cause a fault after the select has + * completed successfully. However, because we're not + * updating the timeval, we can't restart the system + * call. + */ + if (ret == -ERESTARTNOHAND) + ret = -EINTR; + } + } + + return ret; +} + +#ifdef TIF_RESTORE_SIGMASK +asmlinkage long sys_pselect7(int n, fd_set __user *inp, fd_set __user *outp, + fd_set __user *exp, struct timespec __user *tsp, + const sigset_t __user *sigmask, size_t sigsetsize) +{ + int64_t timeout = MAX_SCHEDULE_TIMEOUT; + sigset_t ksigmask, sigsaved; + struct timespec ts; + int ret; + + if (tsp) { + if (copy_from_user(&ts, tsp, sizeof(ts))) + return -EFAULT; + + if (ts.tv_sec < 0 || ts.tv_nsec < 0) + return -EINVAL; + + /* Cast to uint64_t to make GCC stop complaining */ + if ((uint64_t)ts.tv_sec >= (uint64_t)MAX_INT64_SECONDS) + timeout = -1; /* infinite */ + else { + timeout = ROUND_UP(ts.tv_sec, 1000000000/HZ); + timeout += ts.tv_sec * HZ; + } + } + + if (sigmask) { + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + if (copy_from_user(&ksigmask, sigmask, sizeof(ksigmask))) + return -EFAULT; + + sigdelsetmask(&ksigmask, sigmask(SIGKILL)|sigmask(SIGSTOP)); + sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved); + } + + ret = core_sys_select(n, inp, outp, exp, &timeout); + + if (tsp) { + if (current->personality & STICKY_TIMEOUTS) + goto sticky; + ts.tv_nsec = jiffies_to_usecs(do_div((*(uint64_t*)&timeout), HZ)) * 1000; + ts.tv_sec = timeout; + if (copy_to_user(tsp, &ts, sizeof(ts))) { + sticky: + /* + * If an application puts its timeval in read-only + * memory, we don't want the Linux-specific update to + * the timeval to cause a fault after the select has + * completed successfully. However, because we're not + * updating the timeval, we can't restart the system + * call. + */ + if (ret == -ERESTARTNOHAND) + ret = -EINTR; + } + } + + if (ret == -ERESTARTNOHAND) { + /* Don't restore the signal mask yet. Let do_signal() deliver the signal + on the way back to userspace, before the signal mask is restored. */ + if (sigmask) { + memcpy(¤t->saved_sigmask, &sigsaved, sizeof(sigsaved)); + set_thread_flag(TIF_RESTORE_SIGMASK); + } + } else if (sigmask) + sigprocmask(SIG_SETMASK, &sigsaved, NULL); + + return ret; +} + +/* + * Most architectures can't handle 7-argument syscalls. So we provide a + * 6-argument version where the sixth argument is a pointer to a structure + * which has a pointer to the sigset_t itself followed by a size_t containing + * the sigset size. + */ +asmlinkage long sys_pselect6(int n, fd_set __user *inp, fd_set __user *outp, + fd_set __user *exp, struct timespec __user *tsp, void __user *sig) +{ + size_t sigsetsize = 0; + sigset_t __user *up = NULL; + + if (sig) { + if (!access_ok(VERIFY_READ, sig, sizeof(void *)+sizeof(size_t)) + || __get_user(up, (sigset_t * __user *)sig) + || __get_user(sigsetsize, + (size_t * __user)(sig+sizeof(void *)))) + return -EFAULT; + } + + return sys_pselect7(n, inp, outp, exp, tsp, up, sigsetsize); +} +#endif /* TIF_RESTORE_SIGMASK */ + struct poll_list { struct poll_list *next; int len; @@ -436,16 +557,19 @@ static void do_pollfd(unsigned int num, } static int do_poll(unsigned int nfds, struct poll_list *list, - struct poll_wqueues *wait, long timeout) + struct poll_wqueues *wait, int64_t *timeout) { int count = 0; poll_table* pt = &wait->pt; - if (!timeout) + /* Optimise the no-wait case */ + if (!(*timeout)) pt = NULL; for (;;) { struct poll_list *walk; + long __timeout; + set_current_state(TASK_INTERRUPTIBLE); walk = list; while(walk != NULL) { @@ -453,18 +577,33 @@ static int do_poll(unsigned int nfds, s walk = walk->next; } pt = NULL; - if (count || !timeout || signal_pending(current)) + if (count || !*timeout || signal_pending(current)) break; count = wait->error; if (count) break; - timeout = schedule_timeout(timeout); + + if (*timeout < 0) { + /* Wait indefinitely */ + __timeout = MAX_SCHEDULE_TIMEOUT; + } else if (unlikely(*timeout >= (int64_t)MAX_SCHEDULE_TIMEOUT - 1)) { + /* Wait for longer than MAX_SCHEDULE_TIMEOUT. Do it in a loop */ + __timeout = MAX_SCHEDULE_TIMEOUT - 1; + *timeout -= __timeout; + } else { + __timeout = *timeout; + *timeout = 0; + } + + __timeout = schedule_timeout(__timeout); + if (*timeout >= 0) + *timeout += __timeout; } __set_current_state(TASK_RUNNING); return count; } -asmlinkage long sys_poll(struct pollfd __user * ufds, unsigned int nfds, long timeout) +int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, int64_t *timeout) { struct poll_wqueues table; int fdcount, err; @@ -482,14 +621,6 @@ asmlinkage long sys_poll(struct pollfd _ if (nfds > max_fdset && nfds > OPEN_MAX) return -EINVAL; - if (timeout) { - /* Careful about overflow in the intermediate values */ - if ((unsigned long) timeout < MAX_SCHEDULE_TIMEOUT / HZ) - timeout = (unsigned long)(timeout*HZ+999)/1000+1; - else /* Negative or overflow */ - timeout = MAX_SCHEDULE_TIMEOUT; - } - poll_initwait(&table); head = NULL; @@ -519,6 +650,7 @@ asmlinkage long sys_poll(struct pollfd _ } i -= pp->len; } + fdcount = do_poll(nfds, head, &table, timeout); /* OK, now copy the revents fields back to user space. */ @@ -547,3 +679,94 @@ out_fds: poll_freewait(&table); return err; } + +asmlinkage long sys_poll(struct pollfd __user *ufds, unsigned int nfds, + long timeout_msecs) +{ + int64_t timeout_jiffies = 0; + + if (timeout_msecs) { +#if HZ > 1000 + /* We can only overflow if HZ > 1000 */ + if (timeout_msecs / 1000 > (int64_t)0x7fffffffffffffffULL / (int64_t)HZ) + timeout_jiffies = -1; + else +#endif + timeout_jiffies = msecs_to_jiffies(timeout_msecs); + } + + return do_sys_poll(ufds, nfds, &timeout_jiffies); +} + +#ifdef TIF_RESTORE_SIGMASK +asmlinkage long sys_ppoll(struct pollfd __user *ufds, unsigned int nfds, + struct timespec __user *tsp, const sigset_t __user *sigmask, + size_t sigsetsize) +{ + sigset_t ksigmask, sigsaved; + struct timespec ts; + int64_t timeout = -1; + int ret; + + if (tsp) { + if (copy_from_user(&ts, tsp, sizeof(ts))) + return -EFAULT; + + /* Cast to uint64_t to make GCC stop complaining */ + if ((uint64_t)ts.tv_sec >= (uint64_t)MAX_INT64_SECONDS) + timeout = -1; /* infinite */ + else { + timeout = ROUND_UP(ts.tv_sec, 1000000000/HZ); + timeout += ts.tv_sec * HZ; + } + } + + if (sigmask) { + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + if (copy_from_user(&ksigmask, sigmask, sizeof(ksigmask))) + return -EFAULT; + + sigdelsetmask(&ksigmask, sigmask(SIGKILL)|sigmask(SIGSTOP)); + sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved); + } + + ret = do_sys_poll(ufds, nfds, &timeout); + + /* We can restart this syscall, usually */ + if (ret == -EINTR) { + /* Don't restore the signal mask yet. Let do_signal() deliver the signal + on the way back to userspace, before the signal mask is restored. */ + if (sigmask) { + memcpy(¤t->saved_sigmask, &sigsaved, sizeof(sigsaved)); + set_thread_flag(TIF_RESTORE_SIGMASK); + } + ret = -ERESTARTNOHAND; + } else if (sigmask) + sigprocmask(SIG_SETMASK, &sigsaved, NULL); + + if (tsp && timeout >= 0) { + if (current->personality & STICKY_TIMEOUTS) + goto sticky; + /* Yes, we know it's actually an int64_t, but it's also positive. */ + ts.tv_nsec = jiffies_to_usecs(do_div((*(uint64_t*)&timeout), HZ)) * 1000; + ts.tv_sec = timeout; + if (copy_to_user(tsp, &ts, sizeof(ts))) { + sticky: + /* + * If an application puts its timeval in read-only + * memory, we don't want the Linux-specific update to + * the timeval to cause a fault after the select has + * completed successfully. However, because we're not + * updating the timeval, we can't restart the system + * call. + */ + if (ret == -ERESTARTNOHAND && timeout >= 0) + ret = -EINTR; + } + } + + return ret; +} +#endif /* TIF_RESTORE_SIGMASK */ --- a/fs/compat.c +++ b/fs/compat.c @@ -53,6 +53,8 @@ #include <asm/mmu_context.h> #include <asm/ioctls.h> +extern void sigset_from_compat(sigset_t *set, compat_sigset_t *compat); + /* * Not all architectures have sys_utime, so implement this in terms * of sys_utimes. @@ -1607,36 +1609,14 @@ static void select_bits_free(void *bits, #define MAX_SELECT_SECONDS \ ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1) -asmlinkage long -compat_sys_select(int n, compat_ulong_t __user *inp, compat_ulong_t __user *outp, - compat_ulong_t __user *exp, struct compat_timeval __user *tvp) +int compat_core_sys_select(int n, compat_ulong_t __user *inp, + compat_ulong_t __user *outp, compat_ulong_t __user *exp, int64_t *timeout) { fd_set_bits fds; char *bits; - long timeout; int size, max_fdset, ret = -EINVAL; struct fdtable *fdt; - timeout = MAX_SCHEDULE_TIMEOUT; - if (tvp) { - time_t sec, usec; - - if (!access_ok(VERIFY_READ, tvp, sizeof(*tvp)) - || __get_user(sec, &tvp->tv_sec) - || __get_user(usec, &tvp->tv_usec)) { - ret = -EFAULT; - goto out_nofds; - } - - if (sec < 0 || usec < 0) - goto out_nofds; - - if ((unsigned long) sec < MAX_SELECT_SECONDS) { - timeout = ROUND_UP(usec, 1000000/HZ); - timeout += sec * (unsigned long) HZ; - } - } - if (n < 0) goto out_nofds; @@ -1673,19 +1653,7 @@ compat_sys_select(int n, compat_ulong_t zero_fd_set(n, fds.res_out); zero_fd_set(n, fds.res_ex); - ret = do_select(n, &fds, &timeout); - - if (tvp && !(current->personality & STICKY_TIMEOUTS)) { - time_t sec = 0, usec = 0; - if (timeout) { - sec = timeout / HZ; - usec = timeout % HZ; - usec *= (1000000/HZ); - } - if (put_user(sec, &tvp->tv_sec) || - put_user(usec, &tvp->tv_usec)) - ret = -EFAULT; - } + ret = do_select(n, &fds, timeout); if (ret < 0) goto out; @@ -1706,6 +1674,216 @@ out_nofds: return ret; } +asmlinkage long compat_sys_select(int n, compat_ulong_t __user *inp, + compat_ulong_t __user *outp, compat_ulong_t __user *exp, + struct compat_timeval __user *tvp) +{ + int64_t timeout = -1; + struct compat_timeval tv; + int ret; + + if (tvp) { + if (copy_from_user(&tv, tvp, sizeof(tv))) + return -EFAULT; + + if (tv.tv_sec < 0 || tv.tv_usec < 0) + return -EINVAL; + + /* Cast to uint64_t to make GCC stop complaining */ + if ((uint64_t)tv.tv_sec >= (uint64_t)MAX_INT64_SECONDS) + timeout = -1; /* infinite */ + else { + timeout = ROUND_UP(tv.tv_sec, 1000000/HZ); + timeout += tv.tv_sec * HZ; + } + } + + ret = compat_core_sys_select(n, inp, outp, exp, &timeout); + + if (tvp) { + if (current->personality & STICKY_TIMEOUTS) + goto sticky; + tv.tv_usec = jiffies_to_usecs(do_div((*(uint64_t*)&timeout), HZ)); + tv.tv_sec = timeout; + if (copy_to_user(tvp, &tv, sizeof(tv))) { + sticky: + /* + * If an application puts its timeval in read-only + * memory, we don't want the Linux-specific update to + * the timeval to cause a fault after the select has + * completed successfully. However, because we're not + * updating the timeval, we can't restart the system + * call. + */ + if (ret == -ERESTARTNOHAND) + ret = -EINTR; + } + } + + return ret; +} + +#ifdef TIF_RESTORE_SIGMASK +asmlinkage long compat_sys_pselect7(int n, compat_ulong_t __user *inp, + compat_ulong_t __user *outp, compat_ulong_t __user *exp, + struct compat_timespec __user *tsp, compat_sigset_t __user *sigmask, + compat_size_t sigsetsize) +{ + compat_sigset_t s32; + sigset_t ksigmask, sigsaved; + long timeout = MAX_SCHEDULE_TIMEOUT; + struct compat_timespec ts; + int ret; + + if (tsp) { + if (copy_from_user(&ts, tsp, sizeof(ts))) + return -EFAULT; + + if (ts.tv_sec < 0 || ts.tv_nsec < 0) + return -EINVAL; + } + + if (sigmask) { + if (sigsetsize != sizeof(compat_sigset_t)) + return -EINVAL; + if (copy_from_user(&s32, sigmask, sizeof(s32))) + return -EFAULT; + sigset_from_compat(&ksigmask, &s32); + + sigdelsetmask(&ksigmask, sigmask(SIGKILL)|sigmask(SIGSTOP)); + sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved); + } + + do { + if (tsp) { + if ((unsigned long)ts.tv_sec < MAX_SELECT_SECONDS) { + timeout = ROUND_UP(ts.tv_nsec, 1000000000/HZ); + timeout += ts.tv_sec * (unsigned long)HZ; + ts.tv_sec = 0; + ts.tv_nsec = 0; + } else { + ts.tv_sec -= MAX_SELECT_SECONDS; + timeout = MAX_SELECT_SECONDS * HZ; + } + } + + ret = compat_core_sys_select(n, inp, outp, exp, &timeout); + + } while (!ret && !timeout && tsp && (ts.tv_sec || ts.tv_nsec)); + + if (tsp && !(current->personality & STICKY_TIMEOUTS)) { + ts.tv_sec += timeout / HZ; + ts.tv_nsec += (timeout % HZ) * (1000000000/HZ); + if (ts.tv_nsec >= 1000000000) { + ts.tv_sec++; + ts.tv_nsec -= 1000000000; + } + (void)copy_to_user(tsp, &ts, sizeof(ts)); + } + + if (ret == -ERESTARTNOHAND) { + /* Don't restore the signal mask yet. Let do_signal() deliver the signal + on the way back to userspace, before the signal mask is restored. */ + if (sigmask) { + memcpy(¤t->saved_sigmask, &sigsaved, sizeof(sigsaved)); + set_thread_flag(TIF_RESTORE_SIGMASK); + } + } else if (sigmask) + sigprocmask(SIG_SETMASK, &sigsaved, NULL); + + return ret; +} + +asmlinkage long compat_sys_pselect6(int n, compat_ulong_t __user *inp, + compat_ulong_t __user *outp, compat_ulong_t __user *exp, + struct compat_timespec __user *tsp, void __user *sig) +{ + compat_size_t sigsetsize = 0; + compat_uptr_t up = 0; + + if (sig) { + if (!access_ok(VERIFY_READ, sig, + sizeof(compat_uptr_t)+sizeof(compat_size_t)) || + __get_user(up, (compat_uptr_t __user *)sig) || + __get_user(sigsetsize, + (compat_size_t __user *)(sig+sizeof(up)))) + return -EFAULT; + } + return compat_sys_pselect7(n, inp, outp, exp, tsp, compat_ptr(up), + sigsetsize); +} + +asmlinkage long compat_sys_ppoll(struct pollfd __user *ufds, + unsigned int nfds, struct compat_timespec __user *tsp, + const compat_sigset_t __user *sigmask, compat_size_t sigsetsize) +{ + compat_sigset_t s32; + sigset_t ksigmask, sigsaved; + struct compat_timespec ts; + int64_t timeout = -1; + int ret; + + if (tsp) { + if (copy_from_user(&ts, tsp, sizeof(ts))) + return -EFAULT; + + /* We assume that ts.tv_sec is always lower than + the number of seconds that can be expressed in + an int64_t. Otherwise the compiler bitches at us */ + timeout = ROUND_UP(ts.tv_sec, 1000000000/HZ); + timeout += ts.tv_sec * HZ; + } + + if (sigmask) { + if (sigsetsize |= sizeof(compat_sigset_t)) + return -EINVAL; + if (copy_from_user(&s32, sigmask, sizeof(s32))) + return -EFAULT; + sigset_from_compat(&ksigmask, &s32); + + sigdelsetmask(&ksigmask, sigmask(SIGKILL)|sigmask(SIGSTOP)); + sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved); + } + + ret = do_sys_poll(ufds, nfds, &timeout); + + /* We can restart this syscall, usually */ + if (ret == -EINTR) { + /* Don't restore the signal mask yet. Let do_signal() deliver the signal + on the way back to userspace, before the signal mask is restored. */ + if (sigmask) { + memcpy(¤t->saved_sigmask, &sigsaved, sizeof(sigsaved)); + set_thread_flag(TIF_RESTORE_SIGMASK); + } + ret = -ERESTARTNOHAND; + } else if (sigmask) + sigprocmask(SIG_SETMASK, &sigsaved, NULL); + + if (tsp && timeout >= 0) { + if (current->personality & STICKY_TIMEOUTS) + goto sticky; + /* Yes, we know it's actually an int64_t, but it's also positive. */ + ts.tv_nsec = jiffies_to_usecs(do_div((*(uint64_t*)&timeout), HZ)) * 1000; + ts.tv_sec = timeout; + if (copy_to_user(tsp, &ts, sizeof(ts))) { + sticky: + /* + * If an application puts its timeval in read-only + * memory, we don't want the Linux-specific update to + * the timeval to cause a fault after the select has + * completed successfully. However, because we're not + * updating the timeval, we can't restart the system + * call. + */ + if (ret == -ERESTARTNOHAND && timeout >= 0) + ret = -EINTR; + } + } + + return ret; +} +#endif /* TIF_RESTORE_SIGMASK */ + #if defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE) /* Stuff for NFS server syscalls... */ struct compat_nfsctl_svc { diff --git a/include/linux/poll.h b/include/linux/poll.h index f6da702..5b9c85c 100644 --- a/include/linux/poll.h +++ b/include/linux/poll.h @@ -92,7 +92,11 @@ void zero_fd_set(unsigned long nr, unsig memset(fdset, 0, FDS_BYTES(nr)); } -extern int do_select(int n, fd_set_bits *fds, long *timeout); +#define MAX_INT64_SECONDS (((int64_t)(~((uint64_t)0)>>1)/HZ)-1) + +extern int do_select(int n, fd_set_bits *fds, int64_t *timeout); +extern int do_sys_poll(struct pollfd __user * ufds, unsigned int nfds, + int64_t *timeout); #endif /* KERNEL */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 2038bd2..bf7db28 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -800,6 +800,7 @@ struct task_struct { struct sighand_struct *sighand; sigset_t blocked, real_blocked; + sigset_t saved_sigmask; /* To be restored with TIF_RESTORE_SIGMASK */ struct sigpending pending; unsigned long sas_ss_sp; -- dwmw2 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH] 2/3 Add TIF_RESTORE_SIGMASK support for arch/powerpc [not found] <1133225007.31573.86.camel@baythorne.infradead.org> @ 2005-11-29 0:57 ` David Woodhouse 2005-11-29 0:57 ` David Woodhouse 2005-11-29 0:57 ` David Woodhouse 2 siblings, 0 replies; 14+ messages in thread From: David Woodhouse @ 2005-11-29 0:57 UTC (permalink / raw) To: linux-kernel; +Cc: akpm, linuxppc-dev, drepper This patch implements the TIF_RESTORE_SIGMASK flag in the new arch/powerpc kernel, for both 32-bit and 64-bit system call paths. Mostly tested on 32-bit so far. Signed-off-by: David Woodhouse <dwmw2@infradead.org> diff --git a/arch/powerpc/kernel/entry_32.S b/arch/powerpc/kernel/entry_32.S index 036b71d..6aa7386 100644 --- a/arch/powerpc/kernel/entry_32.S +++ b/arch/powerpc/kernel/entry_32.S @@ -227,7 +227,7 @@ ret_from_syscall: MTMSRD(r10) lwz r9,TI_FLAGS(r12) li r8,-_LAST_ERRNO - andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL) + andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL|_TIF_RESTORE_SIGMASK) bne- syscall_exit_work cmplw 0,r3,r8 blt+ syscall_exit_cont @@ -357,7 +357,7 @@ save_user_nvgprs_cont: lwz r5,_MSR(r1) andi. r5,r5,MSR_PR beq ret_from_except - andi. r0,r9,_TIF_SIGPENDING + andi. r0,r9,_TIF_SIGPENDING|_TIF_RESTORE_SIGMASK beq ret_from_except b do_user_signal 8: @@ -683,7 +683,7 @@ user_exc_return: /* r10 contains MSR_KE /* Check current_thread_info()->flags */ rlwinm r9,r1,0,0,(31-THREAD_SHIFT) lwz r9,TI_FLAGS(r9) - andi. r0,r9,(_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL) + andi. r0,r9,(_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL|_TIF_RESTORE_SIGMASK) bne do_work restore_user: @@ -917,7 +917,7 @@ recheck: lwz r9,TI_FLAGS(r9) andi. r0,r9,_TIF_NEED_RESCHED bne- do_resched - andi. r0,r9,_TIF_SIGPENDING + andi. r0,r9,_TIF_SIGPENDING|_TIF_RESTORE_SIGMASK beq restore_user do_user_signal: /* r10 contains MSR_KERNEL here */ ori r10,r10,MSR_EE diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index 2eb6f54..22e592d 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -160,7 +160,7 @@ syscall_exit: mtmsrd r10,1 ld r9,TI_FLAGS(r12) li r11,-_LAST_ERRNO - andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP|_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL|_TIF_SAVE_NVGPRS|_TIF_NOERROR) + andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP|_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL|_TIF_SAVE_NVGPRS|_TIF_NOERROR|_TIF_RESTORE_SIGMASK) bne- syscall_exit_work cmpld r3,r11 ld r5,_CCR(r1) --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c @@ -1177,7 +1131,7 @@ int do_signal(sigset_t *oldset, struct p { siginfo_t info; struct k_sigaction ka; - unsigned int frame, newsp; + unsigned int newsp; int signr, ret; #ifdef CONFIG_PPC32 @@ -1188,11 +1142,11 @@ int do_signal(sigset_t *oldset, struct p } #endif - if (!oldset) + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + oldset = ¤t->saved_sigmask; + else if (!oldset) oldset = ¤t->blocked; - newsp = frame = 0; - signr = get_signal_to_deliver(&info, &ka, regs, NULL); #ifdef CONFIG_PPC32 no_signal: @@ -1222,8 +1176,14 @@ no_signal: } } - if (signr == 0) + if (signr == 0) { + /* No signal to deliver -- put the saved sigmask back */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) { + clear_thread_flag(TIF_RESTORE_SIGMASK); + sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); + } return 0; /* no signals delivered */ + } if ((ka.sa.sa_flags & SA_ONSTACK) && current->sas_ss_size && !on_sig_stack(regs->gpr[1])) @@ -1256,6 +1216,10 @@ no_signal: sigaddset(¤t->blocked, signr); recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); + /* A signal was successfully delivered; the saved sigmask is in + its frame, and we can clear the TIF_RESTORE_SIGMASK flag */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + clear_thread_flag(TIF_RESTORE_SIGMASK); } return ret; diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index 5462bef..7a6f0f7 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c @@ -554,11 +518,15 @@ int do_signal(sigset_t *oldset, struct p if (test_thread_flag(TIF_32BIT)) return do_signal32(oldset, regs); - if (!oldset) + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + oldset = ¤t->saved_sigmask; + else if (!oldset) oldset = ¤t->blocked; signr = get_signal_to_deliver(&info, &ka, regs, NULL); if (signr > 0) { + int ret; + /* Whee! Actually deliver the signal. */ if (TRAP(regs) == 0x0C00) syscall_restart(regs, &ka); @@ -571,7 +539,14 @@ int do_signal(sigset_t *oldset, struct p if (current->thread.dabr) set_dabr(current->thread.dabr); - return handle_signal(signr, &ka, &info, oldset, regs); + ret = handle_signal(signr, &ka, &info, oldset, regs); + + /* If a signal was successfully delivered, the saved sigmask is in + its frame, and we can clear the TIF_RESTORE_SIGMASK flag */ + if (ret && test_thread_flag(TIF_RESTORE_SIGMASK)) + clear_thread_flag(TIF_RESTORE_SIGMASK); + + return ret; } if (TRAP(regs) == 0x0C00) { /* System Call! */ @@ -587,6 +562,11 @@ int do_signal(sigset_t *oldset, struct p regs->result = 0; } } + /* No signal to deliver -- put the saved sigmask back */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) { + clear_thread_flag(TIF_RESTORE_SIGMASK); + sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); + } return 0; } diff --git a/arch/powerpc/kernel/systbl.S b/arch/powerpc/kernel/systbl.S index 989f628..70bfff2 100644 --- a/arch/powerpc/kernel/systbl.S +++ b/arch/powerpc/kernel/systbl.S @@ -321,3 +321,5 @@ SYSCALL(inotify_add_watch) SYSCALL(inotify_rm_watch) SYSCALL(spu_run) SYSCALL(spu_create) +COMPAT_SYS(pselect6) +COMPAT_SYS(ppoll) diff --git a/fs/compat.c b/fs/compat.c index 8186341..51747b9 100644 --- a/include/asm-powerpc/thread_info.h +++ b/include/asm-powerpc/thread_info.h @@ -125,6 +125,7 @@ static inline struct thread_info *curren #define TIF_RESTOREALL 12 /* Restore all regs (implies NOERROR) */ #define TIF_SAVE_NVGPRS 13 /* Save r14-r31 in signal frame */ #define TIF_NOERROR 14 /* Force successful syscall return */ +#define TIF_RESTORE_SIGMASK 15 /* Restore signal mask in do_signal */ /* as above, but as bit values */ #define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE) @@ -141,10 +142,12 @@ static inline struct thread_info *curren #define _TIF_RESTOREALL (1<<TIF_RESTOREALL) #define _TIF_SAVE_NVGPRS (1<<TIF_SAVE_NVGPRS) #define _TIF_NOERROR (1<<TIF_NOERROR) +#define _TIF_RESTORE_SIGMASK (1<<TIF_RESTORE_SIGMASK) #define _TIF_SYSCALL_T_OR_A (_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SECCOMP) #define _TIF_USER_WORK_MASK (_TIF_NOTIFY_RESUME | _TIF_SIGPENDING | \ - _TIF_NEED_RESCHED | _TIF_RESTOREALL) + _TIF_NEED_RESCHED | _TIF_RESTOREALL | \ + _TIF_RESTORE_SIGMASK) #define _TIF_PERSYSCALL_MASK (_TIF_RESTOREALL|_TIF_NOERROR|_TIF_SAVE_NVGPRS) #endif /* __KERNEL__ */ diff --git a/include/asm-powerpc/unistd.h b/include/asm-powerpc/unistd.h index 19eaac3..a40cdff 100644 --- a/include/asm-powerpc/unistd.h +++ b/include/asm-powerpc/unistd.h @@ -298,8 +298,10 @@ #define __NR_inotify_rm_watch 277 #define __NR_spu_run 278 #define __NR_spu_create 279 +#define __NR_pselect6 280 +#define __NR_ppoll 281 -#define __NR_syscalls 280 +#define __NR_syscalls 282 #ifdef __KERNEL__ #define __NR__exit __NR_exit -- dwmw2 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH] 2/3 Add TIF_RESTORE_SIGMASK support for arch/powerpc @ 2005-11-29 0:57 ` David Woodhouse 0 siblings, 0 replies; 14+ messages in thread From: David Woodhouse @ 2005-11-29 0:57 UTC (permalink / raw) To: linux-kernel; +Cc: drepper, linuxppc-dev, akpm This patch implements the TIF_RESTORE_SIGMASK flag in the new arch/powerpc kernel, for both 32-bit and 64-bit system call paths. Mostly tested on 32-bit so far. Signed-off-by: David Woodhouse <dwmw2@infradead.org> diff --git a/arch/powerpc/kernel/entry_32.S b/arch/powerpc/kernel/entry_32.S index 036b71d..6aa7386 100644 --- a/arch/powerpc/kernel/entry_32.S +++ b/arch/powerpc/kernel/entry_32.S @@ -227,7 +227,7 @@ ret_from_syscall: MTMSRD(r10) lwz r9,TI_FLAGS(r12) li r8,-_LAST_ERRNO - andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL) + andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL|_TIF_RESTORE_SIGMASK) bne- syscall_exit_work cmplw 0,r3,r8 blt+ syscall_exit_cont @@ -357,7 +357,7 @@ save_user_nvgprs_cont: lwz r5,_MSR(r1) andi. r5,r5,MSR_PR beq ret_from_except - andi. r0,r9,_TIF_SIGPENDING + andi. r0,r9,_TIF_SIGPENDING|_TIF_RESTORE_SIGMASK beq ret_from_except b do_user_signal 8: @@ -683,7 +683,7 @@ user_exc_return: /* r10 contains MSR_KE /* Check current_thread_info()->flags */ rlwinm r9,r1,0,0,(31-THREAD_SHIFT) lwz r9,TI_FLAGS(r9) - andi. r0,r9,(_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL) + andi. r0,r9,(_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL|_TIF_RESTORE_SIGMASK) bne do_work restore_user: @@ -917,7 +917,7 @@ recheck: lwz r9,TI_FLAGS(r9) andi. r0,r9,_TIF_NEED_RESCHED bne- do_resched - andi. r0,r9,_TIF_SIGPENDING + andi. r0,r9,_TIF_SIGPENDING|_TIF_RESTORE_SIGMASK beq restore_user do_user_signal: /* r10 contains MSR_KERNEL here */ ori r10,r10,MSR_EE diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index 2eb6f54..22e592d 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -160,7 +160,7 @@ syscall_exit: mtmsrd r10,1 ld r9,TI_FLAGS(r12) li r11,-_LAST_ERRNO - andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP|_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL|_TIF_SAVE_NVGPRS|_TIF_NOERROR) + andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP|_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL|_TIF_SAVE_NVGPRS|_TIF_NOERROR|_TIF_RESTORE_SIGMASK) bne- syscall_exit_work cmpld r3,r11 ld r5,_CCR(r1) --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c @@ -1177,7 +1131,7 @@ int do_signal(sigset_t *oldset, struct p { siginfo_t info; struct k_sigaction ka; - unsigned int frame, newsp; + unsigned int newsp; int signr, ret; #ifdef CONFIG_PPC32 @@ -1188,11 +1142,11 @@ int do_signal(sigset_t *oldset, struct p } #endif - if (!oldset) + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + oldset = ¤t->saved_sigmask; + else if (!oldset) oldset = ¤t->blocked; - newsp = frame = 0; - signr = get_signal_to_deliver(&info, &ka, regs, NULL); #ifdef CONFIG_PPC32 no_signal: @@ -1222,8 +1176,14 @@ no_signal: } } - if (signr == 0) + if (signr == 0) { + /* No signal to deliver -- put the saved sigmask back */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) { + clear_thread_flag(TIF_RESTORE_SIGMASK); + sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); + } return 0; /* no signals delivered */ + } if ((ka.sa.sa_flags & SA_ONSTACK) && current->sas_ss_size && !on_sig_stack(regs->gpr[1])) @@ -1256,6 +1216,10 @@ no_signal: sigaddset(¤t->blocked, signr); recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); + /* A signal was successfully delivered; the saved sigmask is in + its frame, and we can clear the TIF_RESTORE_SIGMASK flag */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + clear_thread_flag(TIF_RESTORE_SIGMASK); } return ret; diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index 5462bef..7a6f0f7 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c @@ -554,11 +518,15 @@ int do_signal(sigset_t *oldset, struct p if (test_thread_flag(TIF_32BIT)) return do_signal32(oldset, regs); - if (!oldset) + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + oldset = ¤t->saved_sigmask; + else if (!oldset) oldset = ¤t->blocked; signr = get_signal_to_deliver(&info, &ka, regs, NULL); if (signr > 0) { + int ret; + /* Whee! Actually deliver the signal. */ if (TRAP(regs) == 0x0C00) syscall_restart(regs, &ka); @@ -571,7 +539,14 @@ int do_signal(sigset_t *oldset, struct p if (current->thread.dabr) set_dabr(current->thread.dabr); - return handle_signal(signr, &ka, &info, oldset, regs); + ret = handle_signal(signr, &ka, &info, oldset, regs); + + /* If a signal was successfully delivered, the saved sigmask is in + its frame, and we can clear the TIF_RESTORE_SIGMASK flag */ + if (ret && test_thread_flag(TIF_RESTORE_SIGMASK)) + clear_thread_flag(TIF_RESTORE_SIGMASK); + + return ret; } if (TRAP(regs) == 0x0C00) { /* System Call! */ @@ -587,6 +562,11 @@ int do_signal(sigset_t *oldset, struct p regs->result = 0; } } + /* No signal to deliver -- put the saved sigmask back */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) { + clear_thread_flag(TIF_RESTORE_SIGMASK); + sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); + } return 0; } diff --git a/arch/powerpc/kernel/systbl.S b/arch/powerpc/kernel/systbl.S index 989f628..70bfff2 100644 --- a/arch/powerpc/kernel/systbl.S +++ b/arch/powerpc/kernel/systbl.S @@ -321,3 +321,5 @@ SYSCALL(inotify_add_watch) SYSCALL(inotify_rm_watch) SYSCALL(spu_run) SYSCALL(spu_create) +COMPAT_SYS(pselect6) +COMPAT_SYS(ppoll) diff --git a/fs/compat.c b/fs/compat.c index 8186341..51747b9 100644 --- a/include/asm-powerpc/thread_info.h +++ b/include/asm-powerpc/thread_info.h @@ -125,6 +125,7 @@ static inline struct thread_info *curren #define TIF_RESTOREALL 12 /* Restore all regs (implies NOERROR) */ #define TIF_SAVE_NVGPRS 13 /* Save r14-r31 in signal frame */ #define TIF_NOERROR 14 /* Force successful syscall return */ +#define TIF_RESTORE_SIGMASK 15 /* Restore signal mask in do_signal */ /* as above, but as bit values */ #define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE) @@ -141,10 +142,12 @@ static inline struct thread_info *curren #define _TIF_RESTOREALL (1<<TIF_RESTOREALL) #define _TIF_SAVE_NVGPRS (1<<TIF_SAVE_NVGPRS) #define _TIF_NOERROR (1<<TIF_NOERROR) +#define _TIF_RESTORE_SIGMASK (1<<TIF_RESTORE_SIGMASK) #define _TIF_SYSCALL_T_OR_A (_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SECCOMP) #define _TIF_USER_WORK_MASK (_TIF_NOTIFY_RESUME | _TIF_SIGPENDING | \ - _TIF_NEED_RESCHED | _TIF_RESTOREALL) + _TIF_NEED_RESCHED | _TIF_RESTOREALL | \ + _TIF_RESTORE_SIGMASK) #define _TIF_PERSYSCALL_MASK (_TIF_RESTOREALL|_TIF_NOERROR|_TIF_SAVE_NVGPRS) #endif /* __KERNEL__ */ diff --git a/include/asm-powerpc/unistd.h b/include/asm-powerpc/unistd.h index 19eaac3..a40cdff 100644 --- a/include/asm-powerpc/unistd.h +++ b/include/asm-powerpc/unistd.h @@ -298,8 +298,10 @@ #define __NR_inotify_rm_watch 277 #define __NR_spu_run 278 #define __NR_spu_create 279 +#define __NR_pselect6 280 +#define __NR_ppoll 281 -#define __NR_syscalls 280 +#define __NR_syscalls 282 #ifdef __KERNEL__ #define __NR__exit __NR_exit -- dwmw2 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH] 3/3 Generic sys_rt_sigsuspend [not found] <1133225007.31573.86.camel@baythorne.infradead.org> @ 2005-11-29 0:57 ` David Woodhouse 2005-11-29 0:57 ` David Woodhouse 2005-11-29 0:57 ` David Woodhouse 2 siblings, 0 replies; 14+ messages in thread From: David Woodhouse @ 2005-11-29 0:57 UTC (permalink / raw) To: linux-kernel; +Cc: akpm, linuxppc-dev, drepper The TIF_RESTORE_SIGMASK flag allows us to have a generic implementation of sys_rt_sigsuspend() instead of duplicating it for each architecture. This provides such an implementation and makes arch/powerpc use it. It also tidies up the ppc32 sys_sigsuspend() to use TIF_RESTORE_SIGMASK. Signed-off-by: David Woodhouse <dwmw2@infradead.org> --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c @@ -252,8 +252,7 @@ int do_signal(sigset_t *oldset, struct p /* * Atomically swap in the new signal mask, and wait for a signal. */ -long sys_sigsuspend(old_sigset_t mask, int p2, int p3, int p4, int p6, int p7, - struct pt_regs *regs) +long sys_sigsuspend(old_sigset_t mask) { sigset_t saveset; @@ -264,55 +263,10 @@ long sys_sigsuspend(old_sigset_t mask, i recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); - regs->result = -EINTR; - regs->gpr[3] = EINTR; - regs->ccr |= 0x10000000; - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - if (do_signal(&saveset, regs)) { - set_thread_flag(TIF_RESTOREALL); - return 0; - } - } -} - -long sys_rt_sigsuspend( -#ifdef CONFIG_PPC64 - compat_sigset_t __user *unewset, -#else - sigset_t __user *unewset, -#endif - size_t sigsetsize, int p3, int p4, - int p6, int p7, struct pt_regs *regs) -{ - sigset_t saveset, newset; - - /* XXX: Don't preclude handling different sized sigset_t's. */ - if (sigsetsize != sizeof(sigset_t)) - return -EINVAL; - - if (get_sigset_t(&newset, unewset)) - return -EFAULT; - sigdelsetmask(&newset, ~_BLOCKABLE); - - spin_lock_irq(¤t->sighand->siglock); - saveset = current->blocked; - current->blocked = newset; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - - regs->result = -EINTR; - regs->gpr[3] = EINTR; - regs->ccr |= 0x10000000; - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - if (do_signal(&saveset, regs)) { - set_thread_flag(TIF_RESTOREALL); - return 0; - } - } + current->state = TASK_INTERRUPTIBLE; + schedule(); + set_thread_flag(TIF_RESTORE_SIGMASK); + return -ERESTARTNOHAND; } #ifdef CONFIG_PPC32 diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index 5462bef..7a6f0f7 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c @@ -67,42 +67,6 @@ struct rt_sigframe { char abigap[288]; } __attribute__ ((aligned (16))); - -/* - * Atomically swap in the new signal mask, and wait for a signal. - */ -long sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize, int p3, int p4, - int p6, int p7, struct pt_regs *regs) -{ - sigset_t saveset, newset; - - /* XXX: Don't preclude handling different sized sigset_t's. */ - if (sigsetsize != sizeof(sigset_t)) - return -EINVAL; - - if (copy_from_user(&newset, unewset, sizeof(newset))) - return -EFAULT; - sigdelsetmask(&newset, ~_BLOCKABLE); - - spin_lock_irq(¤t->sighand->siglock); - saveset = current->blocked; - current->blocked = newset; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - - regs->result = -EINTR; - regs->gpr[3] = EINTR; - regs->ccr |= 0x10000000; - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - if (do_signal(&saveset, regs)) { - set_thread_flag(TIF_RESTOREALL); - return 0; - } - } -} - long sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, unsigned long r5, unsigned long r6, unsigned long r7, unsigned long r8, struct pt_regs *regs) --- a/include/asm-powerpc/unistd.h +++ b/include/asm-powerpc/unistd.h @@ -444,11 +446,13 @@ type name(type1 arg1, type2 arg2, type3 #define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPROCMASK #define __ARCH_WANT_SYS_RT_SIGACTION +#define __ARCH_WANT_SYS_RT_SIGSUSPEND #ifdef CONFIG_PPC32 #define __ARCH_WANT_OLD_STAT #endif #ifdef CONFIG_PPC64 #define __ARCH_WANT_COMPAT_SYS_TIME +#define __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND #endif /* --- a/kernel/compat.c +++ b/kernel/compat.c @@ -855,3 +855,31 @@ asmlinkage long compat_sys_stime(compat_ } #endif /* __ARCH_WANT_COMPAT_SYS_TIME */ + +#ifdef __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND +long compat_sys_rt_sigsuspend(compat_sigset_t __user *unewset, compat_size_t sigsetsize) +{ + sigset_t saveset, newset; + compat_sigset_t newset32; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&newset32, unewset, sizeof(compat_sigset_t))) + return -EFAULT; + sigset_from_compat(&newset, &newset32); + sigdelsetmask(&newset, sigmask(SIGKILL)|sigmask(SIGSTOP)); + + spin_lock_irq(¤t->sighand->siglock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + current->state = TASK_INTERRUPTIBLE; + schedule(); + set_thread_flag(TIF_RESTORE_SIGMASK); + return -ERESTARTNOHAND; +} +#endif /* __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND */ diff --git a/kernel/signal.c b/kernel/signal.c index d7611f1..5e4c240 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2623,6 +2623,32 @@ sys_pause(void) #endif +#ifdef __ARCH_WANT_SYS_RT_SIGSUSPEND +long sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize) +{ + sigset_t saveset, newset; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&newset, unewset, sizeof(newset))) + return -EFAULT; + sigdelsetmask(&newset, sigmask(SIGKILL)|sigmask(SIGSTOP)); + + spin_lock_irq(¤t->sighand->siglock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + current->state = TASK_INTERRUPTIBLE; + schedule(); + set_thread_flag(TIF_RESTORE_SIGMASK); + return -ERESTARTNOHAND; +} +#endif /* __ARCH_WANT_SYS_RT_SIGSUSPEND */ + void __init signals_init(void) { sigqueue_cachep = -- dwmw2 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH] 3/3 Generic sys_rt_sigsuspend @ 2005-11-29 0:57 ` David Woodhouse 0 siblings, 0 replies; 14+ messages in thread From: David Woodhouse @ 2005-11-29 0:57 UTC (permalink / raw) To: linux-kernel; +Cc: drepper, linuxppc-dev, akpm The TIF_RESTORE_SIGMASK flag allows us to have a generic implementation of sys_rt_sigsuspend() instead of duplicating it for each architecture. This provides such an implementation and makes arch/powerpc use it. It also tidies up the ppc32 sys_sigsuspend() to use TIF_RESTORE_SIGMASK. Signed-off-by: David Woodhouse <dwmw2@infradead.org> --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c @@ -252,8 +252,7 @@ int do_signal(sigset_t *oldset, struct p /* * Atomically swap in the new signal mask, and wait for a signal. */ -long sys_sigsuspend(old_sigset_t mask, int p2, int p3, int p4, int p6, int p7, - struct pt_regs *regs) +long sys_sigsuspend(old_sigset_t mask) { sigset_t saveset; @@ -264,55 +263,10 @@ long sys_sigsuspend(old_sigset_t mask, i recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); - regs->result = -EINTR; - regs->gpr[3] = EINTR; - regs->ccr |= 0x10000000; - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - if (do_signal(&saveset, regs)) { - set_thread_flag(TIF_RESTOREALL); - return 0; - } - } -} - -long sys_rt_sigsuspend( -#ifdef CONFIG_PPC64 - compat_sigset_t __user *unewset, -#else - sigset_t __user *unewset, -#endif - size_t sigsetsize, int p3, int p4, - int p6, int p7, struct pt_regs *regs) -{ - sigset_t saveset, newset; - - /* XXX: Don't preclude handling different sized sigset_t's. */ - if (sigsetsize != sizeof(sigset_t)) - return -EINVAL; - - if (get_sigset_t(&newset, unewset)) - return -EFAULT; - sigdelsetmask(&newset, ~_BLOCKABLE); - - spin_lock_irq(¤t->sighand->siglock); - saveset = current->blocked; - current->blocked = newset; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - - regs->result = -EINTR; - regs->gpr[3] = EINTR; - regs->ccr |= 0x10000000; - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - if (do_signal(&saveset, regs)) { - set_thread_flag(TIF_RESTOREALL); - return 0; - } - } + current->state = TASK_INTERRUPTIBLE; + schedule(); + set_thread_flag(TIF_RESTORE_SIGMASK); + return -ERESTARTNOHAND; } #ifdef CONFIG_PPC32 diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index 5462bef..7a6f0f7 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c @@ -67,42 +67,6 @@ struct rt_sigframe { char abigap[288]; } __attribute__ ((aligned (16))); - -/* - * Atomically swap in the new signal mask, and wait for a signal. - */ -long sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize, int p3, int p4, - int p6, int p7, struct pt_regs *regs) -{ - sigset_t saveset, newset; - - /* XXX: Don't preclude handling different sized sigset_t's. */ - if (sigsetsize != sizeof(sigset_t)) - return -EINVAL; - - if (copy_from_user(&newset, unewset, sizeof(newset))) - return -EFAULT; - sigdelsetmask(&newset, ~_BLOCKABLE); - - spin_lock_irq(¤t->sighand->siglock); - saveset = current->blocked; - current->blocked = newset; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - - regs->result = -EINTR; - regs->gpr[3] = EINTR; - regs->ccr |= 0x10000000; - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - if (do_signal(&saveset, regs)) { - set_thread_flag(TIF_RESTOREALL); - return 0; - } - } -} - long sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, unsigned long r5, unsigned long r6, unsigned long r7, unsigned long r8, struct pt_regs *regs) --- a/include/asm-powerpc/unistd.h +++ b/include/asm-powerpc/unistd.h @@ -444,11 +446,13 @@ type name(type1 arg1, type2 arg2, type3 #define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPROCMASK #define __ARCH_WANT_SYS_RT_SIGACTION +#define __ARCH_WANT_SYS_RT_SIGSUSPEND #ifdef CONFIG_PPC32 #define __ARCH_WANT_OLD_STAT #endif #ifdef CONFIG_PPC64 #define __ARCH_WANT_COMPAT_SYS_TIME +#define __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND #endif /* --- a/kernel/compat.c +++ b/kernel/compat.c @@ -855,3 +855,31 @@ asmlinkage long compat_sys_stime(compat_ } #endif /* __ARCH_WANT_COMPAT_SYS_TIME */ + +#ifdef __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND +long compat_sys_rt_sigsuspend(compat_sigset_t __user *unewset, compat_size_t sigsetsize) +{ + sigset_t saveset, newset; + compat_sigset_t newset32; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&newset32, unewset, sizeof(compat_sigset_t))) + return -EFAULT; + sigset_from_compat(&newset, &newset32); + sigdelsetmask(&newset, sigmask(SIGKILL)|sigmask(SIGSTOP)); + + spin_lock_irq(¤t->sighand->siglock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + current->state = TASK_INTERRUPTIBLE; + schedule(); + set_thread_flag(TIF_RESTORE_SIGMASK); + return -ERESTARTNOHAND; +} +#endif /* __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND */ diff --git a/kernel/signal.c b/kernel/signal.c index d7611f1..5e4c240 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2623,6 +2623,32 @@ sys_pause(void) #endif +#ifdef __ARCH_WANT_SYS_RT_SIGSUSPEND +long sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize) +{ + sigset_t saveset, newset; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&newset, unewset, sizeof(newset))) + return -EFAULT; + sigdelsetmask(&newset, sigmask(SIGKILL)|sigmask(SIGSTOP)); + + spin_lock_irq(¤t->sighand->siglock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + current->state = TASK_INTERRUPTIBLE; + schedule(); + set_thread_flag(TIF_RESTORE_SIGMASK); + return -ERESTARTNOHAND; +} +#endif /* __ARCH_WANT_SYS_RT_SIGSUSPEND */ + void __init signals_init(void) { sigqueue_cachep = -- dwmw2 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH] 3/3 Generic sys_rt_sigsuspend 2005-11-29 0:57 ` David Woodhouse @ 2005-11-29 5:18 ` Mika Penttilä -1 siblings, 0 replies; 14+ messages in thread From: Mika Penttilä @ 2005-11-29 5:18 UTC (permalink / raw) To: David Woodhouse; +Cc: akpm, linuxppc-dev, linux-kernel, drepper David Woodhouse wrote: >The TIF_RESTORE_SIGMASK flag allows us to have a generic implementation >of sys_rt_sigsuspend() instead of duplicating it for each architecture. >This provides such an implementation and makes arch/powerpc use it. > >It also tidies up the ppc32 sys_sigsuspend() to use TIF_RESTORE_SIGMASK. > >Signed-off-by: David Woodhouse <dwmw2@infradead.org> > > > >+#ifdef __ARCH_WANT_SYS_RT_SIGSUSPEND >+long sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize) >+{ >+ sigset_t saveset, newset; >+ >+ /* XXX: Don't preclude handling different sized sigset_t's. */ >+ if (sigsetsize != sizeof(sigset_t)) >+ return -EINVAL; >+ >+ if (copy_from_user(&newset, unewset, sizeof(newset))) >+ return -EFAULT; >+ sigdelsetmask(&newset, sigmask(SIGKILL)|sigmask(SIGSTOP)); >+ >+ spin_lock_irq(¤t->sighand->siglock); >+ saveset = current->blocked; >+ current->blocked = newset; >+ recalc_sigpending(); >+ spin_unlock_irq(¤t->sighand->siglock); >+ >+ current->state = TASK_INTERRUPTIBLE; >+ schedule(); >+ set_thread_flag(TIF_RESTORE_SIGMASK); >+ return -ERESTARTNOHAND; >+} >+#endif /* __ARCH_WANT_SYS_RT_SIGSUSPEND */ >+ > > You are not setting saved_sigmask here. And shouldn't it return -EINTR? Thanks, Mika ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH] 3/3 Generic sys_rt_sigsuspend @ 2005-11-29 5:18 ` Mika Penttilä 0 siblings, 0 replies; 14+ messages in thread From: Mika Penttilä @ 2005-11-29 5:18 UTC (permalink / raw) To: David Woodhouse; +Cc: linux-kernel, drepper, linuxppc-dev, akpm David Woodhouse wrote: >The TIF_RESTORE_SIGMASK flag allows us to have a generic implementation >of sys_rt_sigsuspend() instead of duplicating it for each architecture. >This provides such an implementation and makes arch/powerpc use it. > >It also tidies up the ppc32 sys_sigsuspend() to use TIF_RESTORE_SIGMASK. > >Signed-off-by: David Woodhouse <dwmw2@infradead.org> > > > >+#ifdef __ARCH_WANT_SYS_RT_SIGSUSPEND >+long sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize) >+{ >+ sigset_t saveset, newset; >+ >+ /* XXX: Don't preclude handling different sized sigset_t's. */ >+ if (sigsetsize != sizeof(sigset_t)) >+ return -EINVAL; >+ >+ if (copy_from_user(&newset, unewset, sizeof(newset))) >+ return -EFAULT; >+ sigdelsetmask(&newset, sigmask(SIGKILL)|sigmask(SIGSTOP)); >+ >+ spin_lock_irq(¤t->sighand->siglock); >+ saveset = current->blocked; >+ current->blocked = newset; >+ recalc_sigpending(); >+ spin_unlock_irq(¤t->sighand->siglock); >+ >+ current->state = TASK_INTERRUPTIBLE; >+ schedule(); >+ set_thread_flag(TIF_RESTORE_SIGMASK); >+ return -ERESTARTNOHAND; >+} >+#endif /* __ARCH_WANT_SYS_RT_SIGSUSPEND */ >+ > > You are not setting saved_sigmask here. And shouldn't it return -EINTR? Thanks, Mika ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH] 3/3 Generic sys_rt_sigsuspend 2005-11-29 5:18 ` Mika Penttilä @ 2005-11-29 10:42 ` David Woodhouse -1 siblings, 0 replies; 14+ messages in thread From: David Woodhouse @ 2005-11-29 10:42 UTC (permalink / raw) To: Mika Penttilä; +Cc: akpm, linuxppc-dev, linux-kernel, drepper On Tue, 2005-11-29 at 07:18 +0200, Mika Penttilä wrote: > You are not setting saved_sigmask here. Oops; well spotted. Thanks. > And shouldn't it return -EINTR? I believe not. The previous versions would loop until do_signal() returned non-zero; i.e. until a signal was actually delivered. By returning -ERESTARTNOHAND we achieve the same effect. If there's a signal delivered, that gets magically converted to -EINTR, but if there's no signal delivered, the syscall gets restarted. diff -u b/kernel/compat.c b/kernel/compat.c --- b/kernel/compat.c +++ b/kernel/compat.c @@ -859,7 +859,7 @@ #ifdef __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND long compat_sys_rt_sigsuspend(compat_sigset_t __user *unewset, compat_size_t sigsetsize) { - sigset_t saveset, newset; + sigset_t newset; compat_sigset_t newset32; /* XXX: Don't preclude handling different sized sigset_t's. */ @@ -872,7 +872,7 @@ sigdelsetmask(&newset, sigmask(SIGKILL)|sigmask(SIGSTOP)); spin_lock_irq(¤t->sighand->siglock); - saveset = current->blocked; + current->saved_sigmask = current->blocked; current->blocked = newset; recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); diff -u b/kernel/signal.c b/kernel/signal.c --- b/kernel/signal.c +++ b/kernel/signal.c @@ -2626,7 +2626,7 @@ #ifdef __ARCH_WANT_SYS_RT_SIGSUSPEND long sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize) { - sigset_t saveset, newset; + sigset_t newset; /* XXX: Don't preclude handling different sized sigset_t's. */ if (sigsetsize != sizeof(sigset_t)) @@ -2637,7 +2637,7 @@ sigdelsetmask(&newset, sigmask(SIGKILL)|sigmask(SIGSTOP)); spin_lock_irq(¤t->sighand->siglock); - saveset = current->blocked; + current->saved_sigmask = current->blocked; current->blocked = newset; recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); -- dwmw2 ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH] 3/3 Generic sys_rt_sigsuspend @ 2005-11-29 10:42 ` David Woodhouse 0 siblings, 0 replies; 14+ messages in thread From: David Woodhouse @ 2005-11-29 10:42 UTC (permalink / raw) To: Mika Penttilä; +Cc: linux-kernel, drepper, linuxppc-dev, akpm On Tue, 2005-11-29 at 07:18 +0200, Mika Penttilä wrote: > You are not setting saved_sigmask here. Oops; well spotted. Thanks. > And shouldn't it return -EINTR? I believe not. The previous versions would loop until do_signal() returned non-zero; i.e. until a signal was actually delivered. By returning -ERESTARTNOHAND we achieve the same effect. If there's a signal delivered, that gets magically converted to -EINTR, but if there's no signal delivered, the syscall gets restarted. diff -u b/kernel/compat.c b/kernel/compat.c --- b/kernel/compat.c +++ b/kernel/compat.c @@ -859,7 +859,7 @@ #ifdef __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND long compat_sys_rt_sigsuspend(compat_sigset_t __user *unewset, compat_size_t sigsetsize) { - sigset_t saveset, newset; + sigset_t newset; compat_sigset_t newset32; /* XXX: Don't preclude handling different sized sigset_t's. */ @@ -872,7 +872,7 @@ sigdelsetmask(&newset, sigmask(SIGKILL)|sigmask(SIGSTOP)); spin_lock_irq(¤t->sighand->siglock); - saveset = current->blocked; + current->saved_sigmask = current->blocked; current->blocked = newset; recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); diff -u b/kernel/signal.c b/kernel/signal.c --- b/kernel/signal.c +++ b/kernel/signal.c @@ -2626,7 +2626,7 @@ #ifdef __ARCH_WANT_SYS_RT_SIGSUSPEND long sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize) { - sigset_t saveset, newset; + sigset_t newset; /* XXX: Don't preclude handling different sized sigset_t's. */ if (sigsetsize != sizeof(sigset_t)) @@ -2637,7 +2637,7 @@ sigdelsetmask(&newset, sigmask(SIGKILL)|sigmask(SIGSTOP)); spin_lock_irq(¤t->sighand->siglock); - saveset = current->blocked; + current->saved_sigmask = current->blocked; current->blocked = newset; recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); -- dwmw2 ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH] 3/3 Generic sys_rt_sigsuspend 2005-11-29 10:42 ` David Woodhouse @ 2005-11-29 15:53 ` Daniel Jacobowitz -1 siblings, 0 replies; 14+ messages in thread From: Daniel Jacobowitz @ 2005-11-29 15:53 UTC (permalink / raw) To: David Woodhouse Cc: drepper, linuxppc-dev, Mika Penttilä, akpm, linux-kernel On Tue, Nov 29, 2005 at 10:42:03AM +0000, David Woodhouse wrote: > I believe not. The previous versions would loop until do_signal() > returned non-zero; i.e. until a signal was actually delivered. > By returning -ERESTARTNOHAND we achieve the same effect. If there's a > signal delivered, that gets magically converted to -EINTR, but if > there's no signal delivered, the syscall gets restarted. And, crazy coincidence, I think this will fix the recently reported ptrace attach bug. Right now if you ptrace a process stuck in sigsuspend, you can't easily force it to return to userspace. I'll test that if these patches are merged. -- Daniel Jacobowitz CodeSourcery, LLC ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH] 3/3 Generic sys_rt_sigsuspend @ 2005-11-29 15:53 ` Daniel Jacobowitz 0 siblings, 0 replies; 14+ messages in thread From: Daniel Jacobowitz @ 2005-11-29 15:53 UTC (permalink / raw) To: David Woodhouse Cc: Mika Penttilä, linux-kernel, drepper, linuxppc-dev, akpm On Tue, Nov 29, 2005 at 10:42:03AM +0000, David Woodhouse wrote: > I believe not. The previous versions would loop until do_signal() > returned non-zero; i.e. until a signal was actually delivered. > By returning -ERESTARTNOHAND we achieve the same effect. If there's a > signal delivered, that gets magically converted to -EINTR, but if > there's no signal delivered, the syscall gets restarted. And, crazy coincidence, I think this will fix the recently reported ptrace attach bug. Right now if you ptrace a process stuck in sigsuspend, you can't easily force it to return to userspace. I'll test that if these patches are merged. -- Daniel Jacobowitz CodeSourcery, LLC ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH] 3/3 Generic sys_rt_sigsuspend 2005-11-29 15:53 ` Daniel Jacobowitz @ 2005-11-29 16:09 ` David Woodhouse -1 siblings, 0 replies; 14+ messages in thread From: David Woodhouse @ 2005-11-29 16:09 UTC (permalink / raw) To: Daniel Jacobowitz Cc: drepper, linuxppc-dev, Mika Penttilä, akpm, linux-kernel On Tue, 2005-11-29 at 10:53 -0500, Daniel Jacobowitz wrote: > And, crazy coincidence, I think this will fix the recently reported > ptrace attach bug. Right now if you ptrace a process stuck in > sigsuspend, you can't easily force it to return to userspace. > I'll test that if these patches are merged. That seems to be true. What I get with my patches is... # strace -p `pidof sigsusptest` Process 1954 attached - interrupt to quit rt_sigsuspend([]) = ? ERESTARTNOHAND (To be restarted) --- SIGALRM (Alarm clock) @ 0 (0) --- sigreturn() = ? (mask now []) fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(4, 64), ...}) = 0 ioctl(1, TCGETS, {B115200 opost isig icanon echo ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x3001f000 write(1, "r is -1\n", 8) = 8 munmap(0x3001f000, 4096) = 0 exit_group(8) = ? Process 1954 detached ... whereas without them I get not only a failure to attach, until there's a signal, but an unexplained SIGSEGV too... # strace -p `pidof sigsusptest` Process 3105 attached - interrupt to quit --- SIGALRM (Alarm clock) @ 0 (0) --- rt_sigsuspend([]) = 14 rt_sigsuspend([] <unfinished ...> --- SIGSEGV (Segmentation fault) @ 0 (0) --- Process 3105 detached -- dwmw2 ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH] 3/3 Generic sys_rt_sigsuspend @ 2005-11-29 16:09 ` David Woodhouse 0 siblings, 0 replies; 14+ messages in thread From: David Woodhouse @ 2005-11-29 16:09 UTC (permalink / raw) To: Daniel Jacobowitz Cc: Mika Penttilä, linux-kernel, drepper, linuxppc-dev, akpm On Tue, 2005-11-29 at 10:53 -0500, Daniel Jacobowitz wrote: > And, crazy coincidence, I think this will fix the recently reported > ptrace attach bug. Right now if you ptrace a process stuck in > sigsuspend, you can't easily force it to return to userspace. > I'll test that if these patches are merged. That seems to be true. What I get with my patches is... # strace -p `pidof sigsusptest` Process 1954 attached - interrupt to quit rt_sigsuspend([]) = ? ERESTARTNOHAND (To be restarted) --- SIGALRM (Alarm clock) @ 0 (0) --- sigreturn() = ? (mask now []) fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(4, 64), ...}) = 0 ioctl(1, TCGETS, {B115200 opost isig icanon echo ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x3001f000 write(1, "r is -1\n", 8) = 8 munmap(0x3001f000, 4096) = 0 exit_group(8) = ? Process 1954 detached ... whereas without them I get not only a failure to attach, until there's a signal, but an unexplained SIGSEGV too... # strace -p `pidof sigsusptest` Process 3105 attached - interrupt to quit --- SIGALRM (Alarm clock) @ 0 (0) --- rt_sigsuspend([]) = 14 rt_sigsuspend([] <unfinished ...> --- SIGSEGV (Segmentation fault) @ 0 (0) --- Process 3105 detached -- dwmw2 ^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2005-11-29 16:34 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <1133225007.31573.86.camel@baythorne.infradead.org>
2005-11-29 0:57 ` [PATCH] 1/3 Add pselect/ppoll() system calls David Woodhouse
2005-11-29 0:57 ` David Woodhouse
2005-11-29 0:57 ` [PATCH] 2/3 Add TIF_RESTORE_SIGMASK support for arch/powerpc David Woodhouse
2005-11-29 0:57 ` David Woodhouse
2005-11-29 0:57 ` [PATCH] 3/3 Generic sys_rt_sigsuspend David Woodhouse
2005-11-29 0:57 ` David Woodhouse
2005-11-29 5:18 ` Mika Penttilä
2005-11-29 5:18 ` Mika Penttilä
2005-11-29 10:42 ` David Woodhouse
2005-11-29 10:42 ` David Woodhouse
2005-11-29 15:53 ` Daniel Jacobowitz
2005-11-29 15:53 ` Daniel Jacobowitz
2005-11-29 16:09 ` David Woodhouse
2005-11-29 16:09 ` David Woodhouse
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.