* [PATCH] [1/6] Add pselect/ppoll system call implementation
[not found] <1134732739.7104.54.camel@pmac.infradead.org>
@ 2005-12-16 11:43 ` David Woodhouse
2005-12-16 11:44 ` [PATCH] [2/6] TIF_RESTORE_SIGMASK support for arch/powerpc David Woodhouse
` (4 subsequent siblings)
5 siblings, 0 replies; 18+ messages in thread
From: David Woodhouse @ 2005-12-16 11:43 UTC (permalink / raw)
To: akpm; +Cc: dhowells, linux-kernel
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>
fs/compat.c | 252 +++++++++++++++++++++++++++++++------
fs/select.c | 337 +++++++++++++++++++++++++++++++++++++++++---------
include/linux/poll.h | 6
include/linux/sched.h | 1
4 files changed, 501 insertions(+), 95 deletions(-)
diff -uNrp linux-2.6.15-rc5-mm3/fs/compat.c linux-2.6.15-rc5-mm3-pselect1/fs/compat.c
--- linux-2.6.15-rc5-mm3/fs/compat.c 2005-12-16 10:56:15.000000000 +0000
+++ linux-2.6.15-rc5-mm3-pselect1/fs/compat.c 2005-12-16 11:01:04.000000000 +0000
@@ -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.
@@ -1621,36 +1623,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;
@@ -1687,19 +1667,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;
@@ -1720,6 +1688,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 -uNrp linux-2.6.15-rc5-mm3/fs/select.c linux-2.6.15-rc5-mm3-pselect1/fs/select.c
--- linux-2.6.15-rc5-mm3/fs/select.c 2005-10-28 01:02:08.000000000 +0100
+++ linux-2.6.15-rc5-mm3-pselect1/fs/select.c 2005-12-16 11:01:04.000000000 +0000
@@ -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 */
diff -uNrp linux-2.6.15-rc5-mm3/include/linux/poll.h linux-2.6.15-rc5-mm3-pselect1/include/linux/poll.h
--- linux-2.6.15-rc5-mm3/include/linux/poll.h 2005-10-28 01:02:08.000000000 +0100
+++ linux-2.6.15-rc5-mm3-pselect1/include/linux/poll.h 2005-12-16 11:01:04.000000000 +0000
@@ -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 -uNrp linux-2.6.15-rc5-mm3/include/linux/sched.h linux-2.6.15-rc5-mm3-pselect1/include/linux/sched.h
--- linux-2.6.15-rc5-mm3/include/linux/sched.h 2005-12-16 10:56:18.000000000 +0000
+++ linux-2.6.15-rc5-mm3-pselect1/include/linux/sched.h 2005-12-16 11:01:04.000000000 +0000
@@ -816,6 +816,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 [flat|nested] 18+ messages in thread
* [PATCH] [2/6] TIF_RESTORE_SIGMASK support for arch/powerpc
[not found] <1134732739.7104.54.camel@pmac.infradead.org>
2005-12-16 11:43 ` [PATCH] [1/6] Add pselect/ppoll system call implementation David Woodhouse
@ 2005-12-16 11:44 ` David Woodhouse
2005-12-16 11:44 ` [PATCH] [3/6] Generic sys_rt_sigsuspend() David Woodhouse
` (3 subsequent siblings)
5 siblings, 0 replies; 18+ messages in thread
From: David Woodhouse @ 2005-12-16 11:44 UTC (permalink / raw)
To: akpm; +Cc: dhowells, linux-kernel
This patch implements the TIF_RESTORE_SIGMASK flag in the new
arch/powerpc kernel, for both 32-bit and 64-bit system call paths.
Signed-Off-By: David Woodhouse <dwmw2@infradead.org>
arch/powerpc/kernel/entry_32.S | 8 ++++----
arch/powerpc/kernel/entry_64.S | 2 +-
arch/powerpc/kernel/signal_32.c | 20 +++++++++++++++-----
arch/powerpc/kernel/signal_64.c | 20 ++++++++++++++++++--
arch/powerpc/kernel/systbl.S | 2 ++
include/asm-powerpc/thread_info.h | 5 ++++-
include/asm-powerpc/unistd.h | 4 +++-
7 files changed, 47 insertions(+), 14 deletions(-)
diff -rup linux-2.6.15-rc5-mm3-pselect1/arch/powerpc/kernel/entry_32.S linux-2.6.15-rc5-mm3-pselect2/arch/powerpc/kernel/entry_32.S
--- linux-2.6.15-rc5-mm3-pselect1/arch/powerpc/kernel/entry_32.S 2005-12-16 10:56:09.000000000 +0000
+++ linux-2.6.15-rc5-mm3-pselect2/arch/powerpc/kernel/entry_32.S 2005-12-16 11:02:43.000000000 +0000
@@ -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 -rup linux-2.6.15-rc5-mm3-pselect1/arch/powerpc/kernel/entry_64.S linux-2.6.15-rc5-mm3-pselect2/arch/powerpc/kernel/entry_64.S
--- linux-2.6.15-rc5-mm3-pselect1/arch/powerpc/kernel/entry_64.S 2005-12-16 10:56:09.000000000 +0000
+++ linux-2.6.15-rc5-mm3-pselect2/arch/powerpc/kernel/entry_64.S 2005-12-16 11:02:43.000000000 +0000
@@ -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)
diff -rup linux-2.6.15-rc5-mm3-pselect1/arch/powerpc/kernel/signal_32.c linux-2.6.15-rc5-mm3-pselect2/arch/powerpc/kernel/signal_32.c
--- linux-2.6.15-rc5-mm3-pselect1/arch/powerpc/kernel/signal_32.c 2005-12-16 10:56:09.000000000 +0000
+++ linux-2.6.15-rc5-mm3-pselect2/arch/powerpc/kernel/signal_32.c 2005-12-16 11:02:43.000000000 +0000
@@ -1177,7 +1177,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 +1188,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 +1222,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 +1262,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 -rup linux-2.6.15-rc5-mm3-pselect1/arch/powerpc/kernel/signal_64.c linux-2.6.15-rc5-mm3-pselect2/arch/powerpc/kernel/signal_64.c
--- linux-2.6.15-rc5-mm3-pselect1/arch/powerpc/kernel/signal_64.c 2005-12-16 10:56:09.000000000 +0000
+++ linux-2.6.15-rc5-mm3-pselect2/arch/powerpc/kernel/signal_64.c 2005-12-16 11:02:43.000000000 +0000
@@ -554,11 +554,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 +575,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 +598,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 -rup linux-2.6.15-rc5-mm3-pselect1/arch/powerpc/kernel/systbl.S linux-2.6.15-rc5-mm3-pselect2/arch/powerpc/kernel/systbl.S
--- linux-2.6.15-rc5-mm3-pselect1/arch/powerpc/kernel/systbl.S 2005-12-16 10:56:09.000000000 +0000
+++ linux-2.6.15-rc5-mm3-pselect2/arch/powerpc/kernel/systbl.S 2005-12-16 11:04:04.000000000 +0000
@@ -323,3 +323,5 @@ SYSCALL(spu_run)
SYSCALL(spu_create)
SYSCALL(migrate_pages)
SYSCALL(unshare)
+COMPAT_SYS(pselect6)
+COMPAT_SYS(ppoll)
diff -rup linux-2.6.15-rc5-mm3-pselect1/include/asm-powerpc/thread_info.h linux-2.6.15-rc5-mm3-pselect2/include/asm-powerpc/thread_info.h
--- linux-2.6.15-rc5-mm3-pselect1/include/asm-powerpc/thread_info.h 2005-12-16 10:56:18.000000000 +0000
+++ linux-2.6.15-rc5-mm3-pselect2/include/asm-powerpc/thread_info.h 2005-12-16 11:02:43.000000000 +0000
@@ -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 -rup linux-2.6.15-rc5-mm3-pselect1/include/asm-powerpc/unistd.h linux-2.6.15-rc5-mm3-pselect2/include/asm-powerpc/unistd.h
--- linux-2.6.15-rc5-mm3-pselect1/include/asm-powerpc/unistd.h 2005-12-16 10:56:18.000000000 +0000
+++ linux-2.6.15-rc5-mm3-pselect2/include/asm-powerpc/unistd.h 2005-12-16 11:03:30.000000000 +0000
@@ -300,8 +300,10 @@
#define __NR_spu_create 279
#define __NR_migrate_pages 280
#define __NR_unshare 281
+#define __NR_pselect6 282
+#define __NR_ppoll 283
-#define __NR_syscalls 282
+#define __NR_syscalls 284
#ifdef __KERNEL__
#define __NR__exit __NR_exit
--
dwmw2
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH] [3/6] Generic sys_rt_sigsuspend()
[not found] <1134732739.7104.54.camel@pmac.infradead.org>
2005-12-16 11:43 ` [PATCH] [1/6] Add pselect/ppoll system call implementation David Woodhouse
2005-12-16 11:44 ` [PATCH] [2/6] TIF_RESTORE_SIGMASK support for arch/powerpc David Woodhouse
@ 2005-12-16 11:44 ` David Woodhouse
2005-12-16 11:44 ` [PATCH] [4/6] Handle TIF_RESTORE_SIGMASK for FRV David Woodhouse
` (2 subsequent siblings)
5 siblings, 0 replies; 18+ messages in thread
From: David Woodhouse @ 2005-12-16 11:44 UTC (permalink / raw)
To: akpm; +Cc: dhowells, linux-kernel
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>
arch/powerpc/kernel/signal_32.c | 56 +++-------------------------------------
arch/powerpc/kernel/signal_64.c | 36 -------------------------
include/asm-powerpc/unistd.h | 2 +
kernel/compat.c | 28 ++++++++++++++++++++
kernel/signal.c | 26 ++++++++++++++++++
5 files changed, 61 insertions(+), 87 deletions(-)
diff -rup linux-2.6.15-rc5-mm3-pselect2/arch/powerpc/kernel/signal_32.c linux-2.6.15-rc5-mm3-pselect3/arch/powerpc/kernel/signal_32.c
--- linux-2.6.15-rc5-mm3-pselect2/arch/powerpc/kernel/signal_32.c 2005-12-16 11:02:43.000000000 +0000
+++ linux-2.6.15-rc5-mm3-pselect3/arch/powerpc/kernel/signal_32.c 2005-12-16 11:07:20.000000000 +0000
@@ -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 -rup linux-2.6.15-rc5-mm3-pselect2/arch/powerpc/kernel/signal_64.c linux-2.6.15-rc5-mm3-pselect3/arch/powerpc/kernel/signal_64.c
--- linux-2.6.15-rc5-mm3-pselect2/arch/powerpc/kernel/signal_64.c 2005-12-16 11:02:43.000000000 +0000
+++ linux-2.6.15-rc5-mm3-pselect3/arch/powerpc/kernel/signal_64.c 2005-12-16 11:07:20.000000000 +0000
@@ -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)
diff -rup linux-2.6.15-rc5-mm3-pselect2/include/asm-powerpc/unistd.h linux-2.6.15-rc5-mm3-pselect3/include/asm-powerpc/unistd.h
--- linux-2.6.15-rc5-mm3-pselect2/include/asm-powerpc/unistd.h 2005-12-16 11:03:30.000000000 +0000
+++ linux-2.6.15-rc5-mm3-pselect3/include/asm-powerpc/unistd.h 2005-12-16 11:07:20.000000000 +0000
@@ -448,11 +448,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
/*
diff -rup linux-2.6.15-rc5-mm3-pselect2/kernel/compat.c linux-2.6.15-rc5-mm3-pselect3/kernel/compat.c
--- linux-2.6.15-rc5-mm3-pselect2/kernel/compat.c 2005-12-16 10:56:18.000000000 +0000
+++ linux-2.6.15-rc5-mm3-pselect3/kernel/compat.c 2005-12-16 11:07:20.000000000 +0000
@@ -871,3 +871,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 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);
+ current->saved_sigmask = 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 -rup linux-2.6.15-rc5-mm3-pselect2/kernel/signal.c linux-2.6.15-rc5-mm3-pselect3/kernel/signal.c
--- linux-2.6.15-rc5-mm3-pselect2/kernel/signal.c 2005-12-16 10:56:19.000000000 +0000
+++ linux-2.6.15-rc5-mm3-pselect3/kernel/signal.c 2005-12-16 11:07:20.000000000 +0000
@@ -2720,6 +2720,32 @@ sys_pause(void)
#endif
+#ifdef __ARCH_WANT_SYS_RT_SIGSUSPEND
+long sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize)
+{
+ sigset_t 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);
+ current->saved_sigmask = 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 [flat|nested] 18+ messages in thread
* [PATCH] [4/6] Handle TIF_RESTORE_SIGMASK for FRV
[not found] <1134732739.7104.54.camel@pmac.infradead.org>
` (2 preceding siblings ...)
2005-12-16 11:44 ` [PATCH] [3/6] Generic sys_rt_sigsuspend() David Woodhouse
@ 2005-12-16 11:44 ` David Woodhouse
2005-12-16 11:45 ` [PATCH] [5/6] Handle TIF_RESTORE_SIGMASK for i386 David Woodhouse
2005-12-16 11:45 ` [PATCH] [6/6] Add pselect/ppoll system calls on i386 David Woodhouse
5 siblings, 0 replies; 18+ messages in thread
From: David Woodhouse @ 2005-12-16 11:44 UTC (permalink / raw)
To: akpm; +Cc: dhowells, linux-kernel
From: David Howells <dhowells@redhat.com>
The attached patch handles TIF_RESTORE_SIGMASK as added by David Woodhouse's
patch entitled:
[PATCH] 2/6 TIF_RESTORE_SIGMASK support for arch/powerpc
[PATCH] 3/6 Generic sys_rt_sigsuspend
It does the following:
(1) Declares TIF_RESTORE_SIGMASK for FRV.
(2) Invokes it over to do_signal() when TIF_RESTORE_SIGMASK is set.
(3) Makes do_signal() support TIF_RESTORE_SIGMASK, using the signal mask saved
in current->saved_sigmask.
(4) Discards sys_rt_sigsuspend() from the arch, using the generic one instead.
(5) Makes sys_sigsuspend() save the signal mask and set TIF_RESTORE_SIGMASK
rather than attempting to fudge the return registers.
(6) Makes sys_sigsuspend() return -ERESTARTNOHAND rather than looping
intrinsically.
(7) Makes setup_frame(), setup_rt_frame() and handle_signal() return 0 or
-EFAULT rather than true/false to be consistent with the rest of the
kernel.
Due to the fact do_signal() is then only called from one place:
(8) Make do_signal() no longer have a return value is it was just being
ignored; force_sig() takes care of this.
(9) Discards the old sigmask argument to do_signal() as it's no longer
necessary.
This patch depends on the FRV signalling patches as well as the
sys_rt_sigsuspend patch.
Signed-Off-By: David Howells <dhowells@redhat.com>
Signed-Off-By: David Woodhouse <dwmw2@infradead.org>
arch/frv/kernel/signal.c | 120 +++++++++++++++---------------------------
include/asm-frv/thread_info.h | 2
include/asm-frv/unistd.h | 1
3 files changed, 47 insertions(+), 76 deletions(-)
diff -rup linux-2.6.15-rc5-mm3-pselect3/arch/frv/kernel/signal.c linux-2.6.15-rc5-mm3-pselect4/arch/frv/kernel/signal.c
--- linux-2.6.15-rc5-mm3-pselect3/arch/frv/kernel/signal.c 2005-12-16 10:56:08.000000000 +0000
+++ linux-2.6.15-rc5-mm3-pselect4/arch/frv/kernel/signal.c 2005-12-16 11:09:23.000000000 +0000
@@ -35,74 +35,22 @@ struct fdpic_func_descriptor {
unsigned long GOT;
};
-static int do_signal(sigset_t *oldset);
-
/*
* Atomically swap in the new signal mask, and wait for a signal.
*/
asmlinkage int sys_sigsuspend(int history0, int history1, old_sigset_t mask)
{
- sigset_t saveset;
-
mask &= _BLOCKABLE;
spin_lock_irq(¤t->sighand->siglock);
- saveset = current->blocked;
+ current->saved_sigmask = current->blocked;
siginitset(¤t->blocked, mask);
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
- __frame->gr8 = -EINTR;
- while (1) {
- current->state = TASK_INTERRUPTIBLE;
- schedule();
- if (do_signal(&saveset))
- /* return the signal number as the return value of this function
- * - this is an utterly evil hack. syscalls should not invoke do_signal()
- * as entry.S sets regs->gr8 to the return value of the system call
- * - we can't just use sigpending() as we'd have to discard SIG_IGN signals
- * and call waitpid() if SIGCHLD needed discarding
- * - this only works on the i386 because it passes arguments to the signal
- * handler on the stack, and the return value in EAX is effectively
- * discarded
- */
- return __frame->gr8;
- }
-}
-
-asmlinkage int 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, ~_BLOCKABLE);
-
- spin_lock_irq(¤t->sighand->siglock);
- saveset = current->blocked;
- current->blocked = newset;
- recalc_sigpending();
- spin_unlock_irq(¤t->sighand->siglock);
-
- __frame->gr8 = -EINTR;
- while (1) {
- current->state = TASK_INTERRUPTIBLE;
- schedule();
- if (do_signal(&saveset))
- /* return the signal number as the return value of this function
- * - this is an utterly evil hack. syscalls should not invoke do_signal()
- * as entry.S sets regs->gr8 to the return value of the system call
- * - we can't just use sigpending() as we'd have to discard SIG_IGN signals
- * and call waitpid() if SIGCHLD needed discarding
- * - this only works on the i386 because it passes arguments to the signal
- * handler on the stack, and the return value in EAX is effectively
- * discarded
- */
- return __frame->gr8;
- }
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ set_thread_flag(TIF_RESTORE_SIGMASK);
+ return -ERESTARTNOHAND;
}
asmlinkage int sys_sigaction(int sig,
@@ -372,11 +320,11 @@ static int setup_frame(int sig, struct k
frame->pretcode);
#endif
- return 1;
+ return 0;
give_sigsegv:
force_sig(SIGSEGV, current);
- return 0;
+ return -EFAULT;
} /* end setup_frame() */
@@ -471,11 +419,11 @@ static int setup_rt_frame(int sig, struc
frame->pretcode);
#endif
- return 1;
+ return 0;
give_sigsegv:
force_sig(SIGSEGV, current);
- return 0;
+ return -EFAULT;
} /* end setup_rt_frame() */
@@ -516,7 +464,7 @@ static int handle_signal(unsigned long s
else
ret = setup_frame(sig, ka, oldset);
- if (ret) {
+ if (ret == 0) {
spin_lock_irq(¤t->sighand->siglock);
sigorsets(¤t->blocked, ¤t->blocked,
&ka->sa.sa_mask);
@@ -536,10 +484,11 @@ static int handle_signal(unsigned long s
* want to handle. Thus you cannot kill init even with a SIGKILL even by
* mistake.
*/
-static int do_signal(sigset_t *oldset)
+static void do_signal(void)
{
struct k_sigaction ka;
siginfo_t info;
+ sigset_t *oldset;
int signr;
/*
@@ -549,43 +498,62 @@ static int do_signal(sigset_t *oldset)
* if so.
*/
if (!user_mode(__frame))
- return 1;
+ return;
if (try_to_freeze())
goto no_signal;
- if (!oldset)
+ if (test_thread_flag(TIF_RESTORE_SIGMASK))
+ oldset = ¤t->saved_sigmask;
+ else
oldset = ¤t->blocked;
signr = get_signal_to_deliver(&info, &ka, __frame, NULL);
- if (signr > 0)
- return handle_signal(signr, &info, &ka, oldset);
+ if (signr > 0) {
+ if (handle_signal(signr, &info, &ka, oldset) == 0) {
+ /* a signal was successfully delivered; the saved
+ * sigmask will have been stored in the signal frame,
+ * and will be restored by sigreturn, so we can simply
+ * clear the TIF_RESTORE_SIGMASK flag */
+ if (test_thread_flag(TIF_RESTORE_SIGMASK))
+ clear_thread_flag(TIF_RESTORE_SIGMASK);
+ }
+
+ return;
+ }
no_signal:
/* Did we come from a system call? */
if (__frame->syscallno >= 0) {
/* Restart the system call - no handlers present */
- if (__frame->gr8 == -ERESTARTNOHAND ||
- __frame->gr8 == -ERESTARTSYS ||
- __frame->gr8 == -ERESTARTNOINTR) {
+ switch (__frame->gr8) {
+ case -ERESTARTNOHAND:
+ case -ERESTARTSYS:
+ case -ERESTARTNOINTR:
__frame->gr8 = __frame->orig_gr8;
__frame->pc -= 4;
- }
+ break;
- if (__frame->gr8 == -ERESTART_RESTARTBLOCK){
+ case -ERESTART_RESTARTBLOCK:
__frame->gr8 = __NR_restart_syscall;
__frame->pc -= 4;
+ break;
}
}
- return 0;
+ /* if there's no signal to deliver, we just 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);
+ }
} /* end do_signal() */
/*****************************************************************************/
/*
* notification of userspace execution resumption
- * - triggered by current->work.notify_resume
+ * - triggered by the TIF_WORK_MASK flags
*/
asmlinkage void do_notify_resume(__u32 thread_info_flags)
{
@@ -594,7 +562,7 @@ asmlinkage void do_notify_resume(__u32 t
clear_thread_flag(TIF_SINGLESTEP);
/* deal with pending signal delivery */
- if (thread_info_flags & _TIF_SIGPENDING)
- do_signal(NULL);
+ if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK))
+ do_signal();
} /* end do_notify_resume() */
diff -rup linux-2.6.15-rc5-mm3-pselect3/include/asm-frv/thread_info.h linux-2.6.15-rc5-mm3-pselect4/include/asm-frv/thread_info.h
--- linux-2.6.15-rc5-mm3-pselect3/include/asm-frv/thread_info.h 2005-12-16 10:51:25.000000000 +0000
+++ linux-2.6.15-rc5-mm3-pselect4/include/asm-frv/thread_info.h 2005-12-16 11:09:23.000000000 +0000
@@ -131,6 +131,7 @@ register struct thread_info *__current_t
#define TIF_NEED_RESCHED 3 /* rescheduling necessary */
#define TIF_SINGLESTEP 4 /* restore singlestep on return to user mode */
#define TIF_IRET 5 /* return with iret */
+#define TIF_RESTORE_SIGMASK 6 /* restore signal mask in do_signal() */
#define TIF_POLLING_NRFLAG 16 /* true if poll_idle() is polling TIF_NEED_RESCHED */
#define TIF_MEMDIE 17 /* OOM killer killed process */
@@ -140,6 +141,7 @@ register struct thread_info *__current_t
#define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED)
#define _TIF_SINGLESTEP (1 << TIF_SINGLESTEP)
#define _TIF_IRET (1 << TIF_IRET)
+#define _TIF_RESTORE_SIGMASK (1 << TIF_RESTORE_SIGMASK)
#define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG)
#define _TIF_WORK_MASK 0x0000FFFE /* work to do on interrupt/exception return */
diff -rup linux-2.6.15-rc5-mm3-pselect3/include/asm-frv/unistd.h linux-2.6.15-rc5-mm3-pselect4/include/asm-frv/unistd.h
--- linux-2.6.15-rc5-mm3-pselect3/include/asm-frv/unistd.h 2005-10-28 01:02:08.000000000 +0100
+++ linux-2.6.15-rc5-mm3-pselect4/include/asm-frv/unistd.h 2005-12-16 11:09:23.000000000 +0000
@@ -486,6 +486,7 @@ static inline pid_t wait(int * wait_stat
/* #define __ARCH_WANT_SYS_SIGPENDING */
#define __ARCH_WANT_SYS_SIGPROCMASK
#define __ARCH_WANT_SYS_RT_SIGACTION
+#define __ARCH_WANT_SYS_RT_SIGSUSPEND
#endif
/*
--
dwmw2
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH] [5/6] Handle TIF_RESTORE_SIGMASK for i386
[not found] <1134732739.7104.54.camel@pmac.infradead.org>
` (3 preceding siblings ...)
2005-12-16 11:44 ` [PATCH] [4/6] Handle TIF_RESTORE_SIGMASK for FRV David Woodhouse
@ 2005-12-16 11:45 ` David Woodhouse
2005-12-16 11:45 ` [PATCH] [6/6] Add pselect/ppoll system calls on i386 David Woodhouse
5 siblings, 0 replies; 18+ messages in thread
From: David Woodhouse @ 2005-12-16 11:45 UTC (permalink / raw)
To: akpm; +Cc: dhowells, linux-kernel
From: David Howells <dhowells@redhat.com>
The attached patch handles TIF_RESTORE_SIGMASK as added by David Woodhouse's
patch entitled:
[PATCH] 2/6 TIF_RESTORE_SIGMASK support for arch/powerpc
[PATCH] 3/6 Generic sys_rt_sigsuspend
It does the following:
(1) Declares TIF_RESTORE_SIGMASK for i386.
(2) Invokes it over to do_signal() when TIF_RESTORE_SIGMASK is set.
(3) Makes do_signal() support TIF_RESTORE_SIGMASK, using the signal mask saved
in current->saved_sigmask.
(4) Discards sys_rt_sigsuspend() from the arch, using the generic one instead.
(5) Makes sys_sigsuspend() save the signal mask and set TIF_RESTORE_SIGMASK
rather than attempting to fudge the return registers.
(6) Makes sys_sigsuspend() return -ERESTARTNOHAND rather than looping
intrinsically.
(7) Makes setup_frame(), setup_rt_frame() and handle_signal() return 0 or
-EFAULT rather than true/false to be consistent with the rest of the
kernel.
Due to the fact do_signal() is then only called from one place:
(8) Makes do_signal() no longer have a return value is it was just being
ignored; force_sig() takes care of this.
(9) Discards the old sigmask argument to do_signal() as it's no longer
necessary.
(10) Makes do_signal() static.
(11) Marks the second argument to do_notify_resume() as unused. The unused
argument should remain in the middle as the arguments are passed in as
registers, and the ordering is specific in entry.S
Given the way do_signal() is now no longer called from sys_{,rt_}sigsuspend(),
they no longer need access to the exception frame, and so can just take
arguments normally.
This patch depends on sys_rt_sigsuspend patch.
Signed-Off-By: David Howells <dhowells@redhat.com>
Signed-Off-By: David Woodhouse <dwmw2@infradead.org>
arch/i386/kernel/signal.c | 109 ++++++++++++++++++-----------------------
include/asm-i386/signal.h | 1
include/asm-i386/thread_info.h | 2
include/asm-i386/unistd.h | 1
4 files changed, 51 insertions(+), 62 deletions(-)
diff -rup linux-2.6.15-rc5-mm3-pselect4/arch/i386/kernel/signal.c linux-2.6.15-rc5-mm3-pselect5/arch/i386/kernel/signal.c
--- linux-2.6.15-rc5-mm3-pselect4/arch/i386/kernel/signal.c 2005-10-28 01:02:08.000000000 +0100
+++ linux-2.6.15-rc5-mm3-pselect5/arch/i386/kernel/signal.c 2005-12-16 11:09:59.000000000 +0000
@@ -37,51 +37,17 @@
asmlinkage int
sys_sigsuspend(int history0, int history1, old_sigset_t mask)
{
- struct pt_regs * regs = (struct pt_regs *) &history0;
- sigset_t saveset;
-
mask &= _BLOCKABLE;
spin_lock_irq(¤t->sighand->siglock);
- saveset = current->blocked;
+ current->saved_sigmask = current->blocked;
siginitset(¤t->blocked, mask);
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
- regs->eax = -EINTR;
- while (1) {
- current->state = TASK_INTERRUPTIBLE;
- schedule();
- if (do_signal(regs, &saveset))
- return -EINTR;
- }
-}
-
-asmlinkage int
-sys_rt_sigsuspend(struct pt_regs regs)
-{
- sigset_t saveset, newset;
-
- /* XXX: Don't preclude handling different sized sigset_t's. */
- if (regs.ecx != sizeof(sigset_t))
- return -EINVAL;
-
- if (copy_from_user(&newset, (sigset_t __user *)regs.ebx, 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.eax = -EINTR;
- while (1) {
- current->state = TASK_INTERRUPTIBLE;
- schedule();
- if (do_signal(®s, &saveset))
- return -EINTR;
- }
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ set_thread_flag(TIF_RESTORE_SIGMASK);
+ return -ERESTARTNOHAND;
}
asmlinkage int
@@ -433,11 +399,11 @@ static int setup_frame(int sig, struct k
current->comm, current->pid, frame, regs->eip, frame->pretcode);
#endif
- return 1;
+ return 0;
give_sigsegv:
force_sigsegv(sig, current);
- return 0;
+ return -EFAULT;
}
static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
@@ -527,11 +493,11 @@ static int setup_rt_frame(int sig, struc
current->comm, current->pid, frame, regs->eip, frame->pretcode);
#endif
- return 1;
+ return 0;
give_sigsegv:
force_sigsegv(sig, current);
- return 0;
+ return -EFAULT;
}
/*
@@ -581,7 +547,7 @@ handle_signal(unsigned long sig, siginfo
else
ret = setup_frame(sig, ka, oldset, regs);
- if (ret) {
+ if (ret == 0) {
spin_lock_irq(¤t->sighand->siglock);
sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask);
if (!(ka->sa.sa_flags & SA_NODEFER))
@@ -598,11 +564,12 @@ handle_signal(unsigned long sig, siginfo
* want to handle. Thus you cannot kill init even with a SIGKILL even by
* mistake.
*/
-int fastcall do_signal(struct pt_regs *regs, sigset_t *oldset)
+static void fastcall do_signal(struct pt_regs *regs)
{
siginfo_t info;
int signr;
struct k_sigaction ka;
+ sigset_t *oldset;
/*
* We want the common case to go fast, which
@@ -613,12 +580,14 @@ int fastcall do_signal(struct pt_regs *r
* CS suffices.
*/
if (!user_mode(regs))
- return 1;
+ return;
if (try_to_freeze())
goto no_signal;
- if (!oldset)
+ if (test_thread_flag(TIF_RESTORE_SIGMASK))
+ oldset = ¤t->saved_sigmask;
+ else
oldset = ¤t->blocked;
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
@@ -628,38 +597,55 @@ int fastcall do_signal(struct pt_regs *r
* have been cleared if the watchpoint triggered
* inside the kernel.
*/
- if (unlikely(current->thread.debugreg[7])) {
+ if (unlikely(current->thread.debugreg[7]))
set_debugreg(current->thread.debugreg[7], 7);
- }
/* Whee! Actually deliver the signal. */
- return handle_signal(signr, &info, &ka, oldset, regs);
+ if (handle_signal(signr, &info, &ka, oldset, regs) == 0) {
+ /* a signal was successfully delivered; the saved
+ * sigmask will have been stored in the signal frame,
+ * and will be restored by sigreturn, so we can simply
+ * clear the TIF_RESTORE_SIGMASK flag */
+ if (test_thread_flag(TIF_RESTORE_SIGMASK))
+ clear_thread_flag(TIF_RESTORE_SIGMASK);
+ }
+
+ return;
}
- no_signal:
+no_signal:
/* Did we come from a system call? */
if (regs->orig_eax >= 0) {
/* Restart the system call - no handlers present */
- if (regs->eax == -ERESTARTNOHAND ||
- regs->eax == -ERESTARTSYS ||
- regs->eax == -ERESTARTNOINTR) {
+ switch (regs->eax) {
+ case -ERESTARTNOHAND:
+ case -ERESTARTSYS:
+ case -ERESTARTNOINTR:
regs->eax = regs->orig_eax;
regs->eip -= 2;
- }
- if (regs->eax == -ERESTART_RESTARTBLOCK){
+ break;
+
+ case -ERESTART_RESTARTBLOCK:
regs->eax = __NR_restart_syscall;
regs->eip -= 2;
+ break;
}
}
- return 0;
+
+ /* if there's no signal to deliver, we just 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);
+ }
}
/*
* notification of userspace execution resumption
- * - triggered by current->work.notify_resume
+ * - triggered by the TIF_WORK_MASK flags
*/
__attribute__((regparm(3)))
-void do_notify_resume(struct pt_regs *regs, sigset_t *oldset,
+void do_notify_resume(struct pt_regs *regs, void *_unused,
__u32 thread_info_flags)
{
/* Pending single-step? */
@@ -667,9 +653,10 @@ void do_notify_resume(struct pt_regs *re
regs->eflags |= TF_MASK;
clear_thread_flag(TIF_SINGLESTEP);
}
+
/* deal with pending signal delivery */
- if (thread_info_flags & _TIF_SIGPENDING)
- do_signal(regs,oldset);
+ if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK))
+ do_signal(regs);
clear_thread_flag(TIF_IRET);
}
diff -rup linux-2.6.15-rc5-mm3-pselect4/include/asm-i386/signal.h linux-2.6.15-rc5-mm3-pselect5/include/asm-i386/signal.h
--- linux-2.6.15-rc5-mm3-pselect4/include/asm-i386/signal.h 2005-12-16 10:51:25.000000000 +0000
+++ linux-2.6.15-rc5-mm3-pselect5/include/asm-i386/signal.h 2005-12-16 11:09:59.000000000 +0000
@@ -218,7 +218,6 @@ static __inline__ int sigfindinword(unsi
}
struct pt_regs;
-extern int FASTCALL(do_signal(struct pt_regs *regs, sigset_t *oldset));
#define ptrace_signal_deliver(regs, cookie) \
do { \
diff -rup linux-2.6.15-rc5-mm3-pselect4/include/asm-i386/thread_info.h linux-2.6.15-rc5-mm3-pselect5/include/asm-i386/thread_info.h
--- linux-2.6.15-rc5-mm3-pselect4/include/asm-i386/thread_info.h 2005-10-28 01:02:08.000000000 +0100
+++ linux-2.6.15-rc5-mm3-pselect5/include/asm-i386/thread_info.h 2005-12-16 11:09:59.000000000 +0000
@@ -142,6 +142,7 @@ register unsigned long current_stack_poi
#define TIF_SYSCALL_EMU 6 /* syscall emulation active */
#define TIF_SYSCALL_AUDIT 7 /* syscall auditing active */
#define TIF_SECCOMP 8 /* secure computing */
+#define TIF_RESTORE_SIGMASK 9 /* restore signal mask in do_signal() */
#define TIF_POLLING_NRFLAG 16 /* true if poll_idle() is polling TIF_NEED_RESCHED */
#define TIF_MEMDIE 17
@@ -154,6 +155,7 @@ register unsigned long current_stack_poi
#define _TIF_SYSCALL_EMU (1<<TIF_SYSCALL_EMU)
#define _TIF_SYSCALL_AUDIT (1<<TIF_SYSCALL_AUDIT)
#define _TIF_SECCOMP (1<<TIF_SECCOMP)
+#define _TIF_RESTORE_SIGMASK (1<<TIF_RESTORE_SIGMASK)
#define _TIF_POLLING_NRFLAG (1<<TIF_POLLING_NRFLAG)
/* work to do on interrupt/exception return */
diff -rup linux-2.6.15-rc5-mm3-pselect4/include/asm-i386/unistd.h linux-2.6.15-rc5-mm3-pselect5/include/asm-i386/unistd.h
--- linux-2.6.15-rc5-mm3-pselect4/include/asm-i386/unistd.h 2005-12-16 10:56:18.000000000 +0000
+++ linux-2.6.15-rc5-mm3-pselect5/include/asm-i386/unistd.h 2005-12-16 11:09:59.000000000 +0000
@@ -420,6 +420,7 @@ __syscall_return(type,__res); \
#define __ARCH_WANT_SYS_SIGPENDING
#define __ARCH_WANT_SYS_SIGPROCMASK
#define __ARCH_WANT_SYS_RT_SIGACTION
+#define __ARCH_WANT_SYS_RT_SIGSUSPEND
#endif
#ifdef __KERNEL_SYSCALLS__
--
dwmw2
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH] [6/6] Add pselect/ppoll system calls on i386
[not found] <1134732739.7104.54.camel@pmac.infradead.org>
` (4 preceding siblings ...)
2005-12-16 11:45 ` [PATCH] [5/6] Handle TIF_RESTORE_SIGMASK for i386 David Woodhouse
@ 2005-12-16 11:45 ` David Woodhouse
5 siblings, 0 replies; 18+ messages in thread
From: David Woodhouse @ 2005-12-16 11:45 UTC (permalink / raw)
To: akpm; +Cc: dhowells, linux-kernel
This patch adds the sys_pselect6() and sys_poll() calls to the i386 syscall
table.
Signed-Off-By: David Woodhouse <dwmw2@infradead.org>
arch/i386/kernel/syscall_table.S | 2 ++
arch/i386/kernel/syscall_table.S~ |only
include/asm-i386/unistd.h | 4 +++-
include/asm-i386/unistd.h~ |only
4 files changed, 5 insertions(+), 1 deletion(-)
diff -urp linux-2.6.15-rc5-mm3-pselect5/arch/i386/kernel/syscall_table.S linux-2.6.15-rc5-mm3-pselect6/arch/i386/kernel/syscall_table.S
--- linux-2.6.15-rc5-mm3-pselect5/arch/i386/kernel/syscall_table.S 2005-12-16 10:56:09.000000000 +0000
+++ linux-2.6.15-rc5-mm3-pselect6/arch/i386/kernel/syscall_table.S 2005-12-16 11:14:19.000000000 +0000
@@ -297,3 +297,5 @@ ENTRY(sys_call_table)
.long sys_preadv /* 295 */
.long sys_pwritev
.long sys_unshare
+ .long sys_pselect6
+ .long sys_ppoll
Only in linux-2.6.15-rc5-mm3-pselect6/arch/i386/kernel: syscall_table.S~
diff -urp linux-2.6.15-rc5-mm3-pselect5/include/asm-i386/unistd.h linux-2.6.15-rc5-mm3-pselect6/include/asm-i386/unistd.h
--- linux-2.6.15-rc5-mm3-pselect5/include/asm-i386/unistd.h 2005-12-16 11:09:59.000000000 +0000
+++ linux-2.6.15-rc5-mm3-pselect6/include/asm-i386/unistd.h 2005-12-16 11:13:51.000000000 +0000
@@ -303,8 +303,10 @@
#define __NR_preadv 295
#define __NR_pwritev 296
#define __NR_unshare 297
+#define __NR_pselect6 298
+#define __NR_ppoll 299
-#define NR_syscalls 298
+#define NR_syscalls 300
/*
* user-visible error numbers are in the range -1 - -128: see
Only in linux-2.6.15-rc5-mm3-pselect6/include/asm-i386: unistd.h~
--
dwmw2
^ permalink raw reply [flat|nested] 18+ messages in thread