Commit 8ff3e8e8 authored by Arjan van de Ven's avatar Arjan van de Ven

select: switch select() and poll() over to hrtimers

With lots of help, input and cleanups from Thomas Gleixner

This patch switches select() and poll() over to hrtimers.

The core of the patch is replacing the "s64 timeout" with a
"struct timespec end_time" in all the plumbing.

But most of the diffstat comes from using the just introduced helpers:
	poll_select_set_timeout
	poll_select_copy_remaining
	timespec_add_safe
which make manipulating the timespec easier and less error-prone.
Signed-off-by: default avatarArjan van de Ven <arjan@linux.intel.com>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent be5dad20
...@@ -1568,7 +1568,8 @@ int compat_set_fd_set(unsigned long nr, compat_ulong_t __user *ufdset, ...@@ -1568,7 +1568,8 @@ int compat_set_fd_set(unsigned long nr, compat_ulong_t __user *ufdset,
((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1) ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1)
int compat_core_sys_select(int n, compat_ulong_t __user *inp, int compat_core_sys_select(int n, compat_ulong_t __user *inp,
compat_ulong_t __user *outp, compat_ulong_t __user *exp, s64 *timeout) compat_ulong_t __user *outp, compat_ulong_t __user *exp,
struct timespec *end_time)
{ {
fd_set_bits fds; fd_set_bits fds;
void *bits; void *bits;
...@@ -1615,7 +1616,7 @@ int compat_core_sys_select(int n, compat_ulong_t __user *inp, ...@@ -1615,7 +1616,7 @@ int compat_core_sys_select(int n, compat_ulong_t __user *inp,
zero_fd_set(n, fds.res_out); zero_fd_set(n, fds.res_out);
zero_fd_set(n, fds.res_ex); zero_fd_set(n, fds.res_ex);
ret = do_select(n, &fds, timeout); ret = do_select(n, &fds, end_time);
if (ret < 0) if (ret < 0)
goto out; goto out;
...@@ -1641,7 +1642,7 @@ asmlinkage long compat_sys_select(int n, compat_ulong_t __user *inp, ...@@ -1641,7 +1642,7 @@ asmlinkage long compat_sys_select(int n, compat_ulong_t __user *inp,
compat_ulong_t __user *outp, compat_ulong_t __user *exp, compat_ulong_t __user *outp, compat_ulong_t __user *exp,
struct compat_timeval __user *tvp) struct compat_timeval __user *tvp)
{ {
s64 timeout = -1; struct timespec end_time, *to = NULL;
struct compat_timeval tv; struct compat_timeval tv;
int ret; int ret;
...@@ -1649,43 +1650,14 @@ asmlinkage long compat_sys_select(int n, compat_ulong_t __user *inp, ...@@ -1649,43 +1650,14 @@ asmlinkage long compat_sys_select(int n, compat_ulong_t __user *inp,
if (copy_from_user(&tv, tvp, sizeof(tv))) if (copy_from_user(&tv, tvp, sizeof(tv)))
return -EFAULT; return -EFAULT;
if (tv.tv_sec < 0 || tv.tv_usec < 0) to = &end_time;
if (poll_select_set_timeout(to, tv.tv_sec,
tv.tv_usec * NSEC_PER_USEC))
return -EINVAL; return -EINVAL;
/* Cast to u64 to make GCC stop complaining */
if ((u64)tv.tv_sec >= (u64)MAX_INT64_SECONDS)
timeout = -1; /* infinite */
else {
timeout = DIV_ROUND_UP(tv.tv_usec, 1000000/HZ);
timeout += tv.tv_sec * HZ;
}
} }
ret = compat_core_sys_select(n, inp, outp, exp, &timeout); ret = compat_core_sys_select(n, inp, outp, exp, to);
ret = poll_select_copy_remaining(&end_time, tvp, 1, ret);
if (tvp) {
struct compat_timeval rtv;
if (current->personality & STICKY_TIMEOUTS)
goto sticky;
rtv.tv_usec = jiffies_to_usecs(do_div((*(u64*)&timeout), HZ));
rtv.tv_sec = timeout;
if (compat_timeval_compare(&rtv, &tv) >= 0)
rtv = tv;
if (copy_to_user(tvp, &rtv, sizeof(rtv))) {
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; return ret;
} }
...@@ -1698,15 +1670,16 @@ asmlinkage long compat_sys_pselect7(int n, compat_ulong_t __user *inp, ...@@ -1698,15 +1670,16 @@ asmlinkage long compat_sys_pselect7(int n, compat_ulong_t __user *inp,
{ {
compat_sigset_t ss32; compat_sigset_t ss32;
sigset_t ksigmask, sigsaved; sigset_t ksigmask, sigsaved;
s64 timeout = MAX_SCHEDULE_TIMEOUT;
struct compat_timespec ts; struct compat_timespec ts;
struct timespec end_time, *to = NULL;
int ret; int ret;
if (tsp) { if (tsp) {
if (copy_from_user(&ts, tsp, sizeof(ts))) if (copy_from_user(&ts, tsp, sizeof(ts)))
return -EFAULT; return -EFAULT;
if (ts.tv_sec < 0 || ts.tv_nsec < 0) to = &end_time;
if (poll_select_set_timeout(to, ts.tv_sec, ts.tv_nsec))
return -EINVAL; return -EINVAL;
} }
...@@ -1721,51 +1694,8 @@ asmlinkage long compat_sys_pselect7(int n, compat_ulong_t __user *inp, ...@@ -1721,51 +1694,8 @@ asmlinkage long compat_sys_pselect7(int n, compat_ulong_t __user *inp,
sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved); sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved);
} }
do { ret = compat_core_sys_select(n, inp, outp, exp, to);
if (tsp) { ret = poll_select_copy_remaining(&end_time, tsp, 0, ret);
if ((unsigned long)ts.tv_sec < MAX_SELECT_SECONDS) {
timeout = DIV_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) {
struct compat_timespec rts;
if (current->personality & STICKY_TIMEOUTS)
goto sticky;
rts.tv_sec = timeout / HZ;
rts.tv_nsec = (timeout % HZ) * (NSEC_PER_SEC/HZ);
if (rts.tv_nsec >= NSEC_PER_SEC) {
rts.tv_sec++;
rts.tv_nsec -= NSEC_PER_SEC;
}
if (compat_timespec_compare(&rts, &ts) >= 0)
rts = ts;
if (copy_to_user(tsp, &rts, sizeof(rts))) {
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) { if (ret == -ERESTARTNOHAND) {
/* /*
...@@ -1810,18 +1740,16 @@ asmlinkage long compat_sys_ppoll(struct pollfd __user *ufds, ...@@ -1810,18 +1740,16 @@ asmlinkage long compat_sys_ppoll(struct pollfd __user *ufds,
compat_sigset_t ss32; compat_sigset_t ss32;
sigset_t ksigmask, sigsaved; sigset_t ksigmask, sigsaved;
struct compat_timespec ts; struct compat_timespec ts;
s64 timeout = -1; struct timespec end_time, *to = NULL;
int ret; int ret;
if (tsp) { if (tsp) {
if (copy_from_user(&ts, tsp, sizeof(ts))) if (copy_from_user(&ts, tsp, sizeof(ts)))
return -EFAULT; return -EFAULT;
/* We assume that ts.tv_sec is always lower than to = &end_time;
the number of seconds that can be expressed in if (poll_select_set_timeout(to, ts.tv_sec, ts.tv_nsec))
an s64. Otherwise the compiler bitches at us */ return -EINVAL;
timeout = DIV_ROUND_UP(ts.tv_nsec, 1000000000/HZ);
timeout += ts.tv_sec * HZ;
} }
if (sigmask) { if (sigmask) {
...@@ -1835,7 +1763,7 @@ asmlinkage long compat_sys_ppoll(struct pollfd __user *ufds, ...@@ -1835,7 +1763,7 @@ asmlinkage long compat_sys_ppoll(struct pollfd __user *ufds,
sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved); sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved);
} }
ret = do_sys_poll(ufds, nfds, &timeout); ret = do_sys_poll(ufds, nfds, to);
/* We can restart this syscall, usually */ /* We can restart this syscall, usually */
if (ret == -EINTR) { if (ret == -EINTR) {
...@@ -1853,31 +1781,7 @@ asmlinkage long compat_sys_ppoll(struct pollfd __user *ufds, ...@@ -1853,31 +1781,7 @@ asmlinkage long compat_sys_ppoll(struct pollfd __user *ufds,
} else if (sigmask) } else if (sigmask)
sigprocmask(SIG_SETMASK, &sigsaved, NULL); sigprocmask(SIG_SETMASK, &sigsaved, NULL);
if (tsp && timeout >= 0) { ret = poll_select_copy_remaining(&end_time, tsp, 0, ret);
struct compat_timespec rts;
if (current->personality & STICKY_TIMEOUTS)
goto sticky;
/* Yes, we know it's actually an s64, but it's also positive. */
rts.tv_nsec = jiffies_to_usecs(do_div((*(u64*)&timeout), HZ)) *
1000;
rts.tv_sec = timeout;
if (compat_timespec_compare(&rts, &ts) >= 0)
rts = ts;
if (copy_to_user(tsp, &rts, sizeof(rts))) {
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; return ret;
} }
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <linux/fdtable.h> #include <linux/fdtable.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
#include <linux/hrtimer.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
...@@ -203,8 +204,6 @@ static int poll_select_copy_remaining(struct timespec *end_time, void __user *p, ...@@ -203,8 +204,6 @@ static int poll_select_copy_remaining(struct timespec *end_time, void __user *p,
return ret; return ret;
} }
#define FDS_IN(fds, n) (fds->in + n) #define FDS_IN(fds, n) (fds->in + n)
#define FDS_OUT(fds, n) (fds->out + n) #define FDS_OUT(fds, n) (fds->out + n)
#define FDS_EX(fds, n) (fds->ex + n) #define FDS_EX(fds, n) (fds->ex + n)
...@@ -257,11 +256,12 @@ static int max_select_fd(unsigned long n, fd_set_bits *fds) ...@@ -257,11 +256,12 @@ static int max_select_fd(unsigned long n, fd_set_bits *fds)
#define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR) #define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR)
#define POLLEX_SET (POLLPRI) #define POLLEX_SET (POLLPRI)
int do_select(int n, fd_set_bits *fds, s64 *timeout) int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
{ {
ktime_t expire, *to = NULL;
struct poll_wqueues table; struct poll_wqueues table;
poll_table *wait; poll_table *wait;
int retval, i; int retval, i, timed_out = 0;
rcu_read_lock(); rcu_read_lock();
retval = max_select_fd(n, fds); retval = max_select_fd(n, fds);
...@@ -273,12 +273,14 @@ int do_select(int n, fd_set_bits *fds, s64 *timeout) ...@@ -273,12 +273,14 @@ int do_select(int n, fd_set_bits *fds, s64 *timeout)
poll_initwait(&table); poll_initwait(&table);
wait = &table.pt; wait = &table.pt;
if (!*timeout) if (end_time && !end_time->tv_sec && !end_time->tv_nsec) {
wait = NULL; wait = NULL;
timed_out = 1;
}
retval = 0; retval = 0;
for (;;) { for (;;) {
unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp; unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp;
long __timeout;
set_current_state(TASK_INTERRUPTIBLE); set_current_state(TASK_INTERRUPTIBLE);
...@@ -334,27 +336,25 @@ int do_select(int n, fd_set_bits *fds, s64 *timeout) ...@@ -334,27 +336,25 @@ int do_select(int n, fd_set_bits *fds, s64 *timeout)
cond_resched(); cond_resched();
} }
wait = NULL; wait = NULL;
if (retval || !*timeout || signal_pending(current)) if (retval || timed_out || signal_pending(current))
break; break;
if (table.error) { if (table.error) {
retval = table.error; retval = table.error;
break; break;
} }
if (*timeout < 0) { /*
/* Wait indefinitely */ * If this is the first loop and we have a timeout
__timeout = MAX_SCHEDULE_TIMEOUT; * given, then we convert to ktime_t and set the to
} else if (unlikely(*timeout >= (s64)MAX_SCHEDULE_TIMEOUT - 1)) { * pointer to the expiry value.
/* Wait for longer than MAX_SCHEDULE_TIMEOUT. Do it in a loop */ */
__timeout = MAX_SCHEDULE_TIMEOUT - 1; if (end_time && !to) {
*timeout -= __timeout; expire = timespec_to_ktime(*end_time);
} else { to = &expire;
__timeout = *timeout;
*timeout = 0;
} }
__timeout = schedule_timeout(__timeout);
if (*timeout >= 0) if (!schedule_hrtimeout(to, HRTIMER_MODE_ABS))
*timeout += __timeout; timed_out = 1;
} }
__set_current_state(TASK_RUNNING); __set_current_state(TASK_RUNNING);
...@@ -375,7 +375,7 @@ int do_select(int n, fd_set_bits *fds, s64 *timeout) ...@@ -375,7 +375,7 @@ int do_select(int n, fd_set_bits *fds, s64 *timeout)
((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1) ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1)
int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp, int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp,
fd_set __user *exp, s64 *timeout) fd_set __user *exp, struct timespec *end_time)
{ {
fd_set_bits fds; fd_set_bits fds;
void *bits; void *bits;
...@@ -426,7 +426,7 @@ int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp, ...@@ -426,7 +426,7 @@ int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp,
zero_fd_set(n, fds.res_out); zero_fd_set(n, fds.res_out);
zero_fd_set(n, fds.res_ex); zero_fd_set(n, fds.res_ex);
ret = do_select(n, &fds, timeout); ret = do_select(n, &fds, end_time);
if (ret < 0) if (ret < 0)
goto out; goto out;
...@@ -452,7 +452,7 @@ int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp, ...@@ -452,7 +452,7 @@ int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp,
asmlinkage long sys_select(int n, fd_set __user *inp, fd_set __user *outp, asmlinkage long sys_select(int n, fd_set __user *inp, fd_set __user *outp,
fd_set __user *exp, struct timeval __user *tvp) fd_set __user *exp, struct timeval __user *tvp)
{ {
s64 timeout = -1; struct timespec end_time, *to = NULL;
struct timeval tv; struct timeval tv;
int ret; int ret;
...@@ -460,43 +460,14 @@ asmlinkage long sys_select(int n, fd_set __user *inp, fd_set __user *outp, ...@@ -460,43 +460,14 @@ asmlinkage long sys_select(int n, fd_set __user *inp, fd_set __user *outp,
if (copy_from_user(&tv, tvp, sizeof(tv))) if (copy_from_user(&tv, tvp, sizeof(tv)))
return -EFAULT; return -EFAULT;
if (tv.tv_sec < 0 || tv.tv_usec < 0) to = &end_time;
if (poll_select_set_timeout(to, tv.tv_sec,
tv.tv_usec * NSEC_PER_USEC))
return -EINVAL; return -EINVAL;
/* Cast to u64 to make GCC stop complaining */
if ((u64)tv.tv_sec >= (u64)MAX_INT64_SECONDS)
timeout = -1; /* infinite */
else {
timeout = DIV_ROUND_UP(tv.tv_usec, USEC_PER_SEC/HZ);
timeout += tv.tv_sec * HZ;
}
} }
ret = core_sys_select(n, inp, outp, exp, &timeout); ret = core_sys_select(n, inp, outp, exp, to);
ret = poll_select_copy_remaining(&end_time, tvp, 1, ret);
if (tvp) {
struct timeval rtv;
if (current->personality & STICKY_TIMEOUTS)
goto sticky;
rtv.tv_usec = jiffies_to_usecs(do_div((*(u64*)&timeout), HZ));
rtv.tv_sec = timeout;
if (timeval_compare(&rtv, &tv) >= 0)
rtv = tv;
if (copy_to_user(tvp, &rtv, sizeof(rtv))) {
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; return ret;
} }
...@@ -506,25 +477,17 @@ asmlinkage long sys_pselect7(int n, fd_set __user *inp, fd_set __user *outp, ...@@ -506,25 +477,17 @@ asmlinkage long sys_pselect7(int n, fd_set __user *inp, fd_set __user *outp,
fd_set __user *exp, struct timespec __user *tsp, fd_set __user *exp, struct timespec __user *tsp,
const sigset_t __user *sigmask, size_t sigsetsize) const sigset_t __user *sigmask, size_t sigsetsize)
{ {
s64 timeout = MAX_SCHEDULE_TIMEOUT;
sigset_t ksigmask, sigsaved; sigset_t ksigmask, sigsaved;
struct timespec ts; struct timespec ts, end_time, *to = NULL;
int ret; int ret;
if (tsp) { if (tsp) {
if (copy_from_user(&ts, tsp, sizeof(ts))) if (copy_from_user(&ts, tsp, sizeof(ts)))
return -EFAULT; return -EFAULT;
if (ts.tv_sec < 0 || ts.tv_nsec < 0) to = &end_time;
if (poll_select_set_timeout(to, ts.tv_sec, ts.tv_nsec))
return -EINVAL; return -EINVAL;
/* Cast to u64 to make GCC stop complaining */
if ((u64)ts.tv_sec >= (u64)MAX_INT64_SECONDS)
timeout = -1; /* infinite */
else {
timeout = DIV_ROUND_UP(ts.tv_nsec, NSEC_PER_SEC/HZ);
timeout += ts.tv_sec * HZ;
}
} }
if (sigmask) { if (sigmask) {
...@@ -538,32 +501,8 @@ asmlinkage long sys_pselect7(int n, fd_set __user *inp, fd_set __user *outp, ...@@ -538,32 +501,8 @@ asmlinkage long sys_pselect7(int n, fd_set __user *inp, fd_set __user *outp,
sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved); sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved);
} }
ret = core_sys_select(n, inp, outp, exp, &timeout); ret = core_sys_select(n, inp, outp, exp, &end_time);
ret = poll_select_copy_remaining(&end_time, tsp, 0, ret);
if (tsp) {
struct timespec rts;
if (current->personality & STICKY_TIMEOUTS)
goto sticky;
rts.tv_nsec = jiffies_to_usecs(do_div((*(u64*)&timeout), HZ)) *
1000;
rts.tv_sec = timeout;
if (timespec_compare(&rts, &ts) >= 0)
rts = ts;
if (copy_to_user(tsp, &rts, sizeof(rts))) {
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) { if (ret == -ERESTARTNOHAND) {
/* /*
...@@ -649,18 +588,20 @@ static inline unsigned int do_pollfd(struct pollfd *pollfd, poll_table *pwait) ...@@ -649,18 +588,20 @@ static inline unsigned int do_pollfd(struct pollfd *pollfd, poll_table *pwait)
} }
static int do_poll(unsigned int nfds, struct poll_list *list, static int do_poll(unsigned int nfds, struct poll_list *list,
struct poll_wqueues *wait, s64 *timeout) struct poll_wqueues *wait, struct timespec *end_time)
{ {
int count = 0;
poll_table* pt = &wait->pt; poll_table* pt = &wait->pt;
ktime_t expire, *to = NULL;
int timed_out = 0, count = 0;
/* Optimise the no-wait case */ /* Optimise the no-wait case */
if (!(*timeout)) if (end_time && !end_time->tv_sec && !end_time->tv_nsec) {
pt = NULL; pt = NULL;
timed_out = 1;
}
for (;;) { for (;;) {
struct poll_list *walk; struct poll_list *walk;
long __timeout;
set_current_state(TASK_INTERRUPTIBLE); set_current_state(TASK_INTERRUPTIBLE);
for (walk = list; walk != NULL; walk = walk->next) { for (walk = list; walk != NULL; walk = walk->next) {
...@@ -692,27 +633,21 @@ static int do_poll(unsigned int nfds, struct poll_list *list, ...@@ -692,27 +633,21 @@ static int do_poll(unsigned int nfds, struct poll_list *list,
if (signal_pending(current)) if (signal_pending(current))
count = -EINTR; count = -EINTR;
} }
if (count || !*timeout) if (count || timed_out)
break; break;
if (*timeout < 0) { /*
/* Wait indefinitely */ * If this is the first loop and we have a timeout
__timeout = MAX_SCHEDULE_TIMEOUT; * given, then we convert to ktime_t and set the to
} else if (unlikely(*timeout >= (s64)MAX_SCHEDULE_TIMEOUT-1)) { * pointer to the expiry value.
/* */
* Wait for longer than MAX_SCHEDULE_TIMEOUT. Do it in if (end_time && !to) {
* a loop expire = timespec_to_ktime(*end_time);
*/ to = &expire;
__timeout = MAX_SCHEDULE_TIMEOUT - 1;
*timeout -= __timeout;
} else {
__timeout = *timeout;
*timeout = 0;
} }
__timeout = schedule_timeout(__timeout); if (!schedule_hrtimeout(to, HRTIMER_MODE_ABS))
if (*timeout >= 0) timed_out = 1;
*timeout += __timeout;
} }
__set_current_state(TASK_RUNNING); __set_current_state(TASK_RUNNING);
return count; return count;
...@@ -721,7 +656,8 @@ static int do_poll(unsigned int nfds, struct poll_list *list, ...@@ -721,7 +656,8 @@ static int do_poll(unsigned int nfds, struct poll_list *list,
#define N_STACK_PPS ((sizeof(stack_pps) - sizeof(struct poll_list)) / \ #define N_STACK_PPS ((sizeof(stack_pps) - sizeof(struct poll_list)) / \
sizeof(struct pollfd)) sizeof(struct pollfd))
int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout) int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds,
struct timespec *end_time)
{ {
struct poll_wqueues table; struct poll_wqueues table;
int err = -EFAULT, fdcount, len, size; int err = -EFAULT, fdcount, len, size;
...@@ -761,7 +697,7 @@ int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout) ...@@ -761,7 +697,7 @@ int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout)
} }
poll_initwait(&table); poll_initwait(&table);
fdcount = do_poll(nfds, head, &table, timeout); fdcount = do_poll(nfds, head, &table, end_time);
poll_freewait(&table); poll_freewait(&table);
for (walk = head; walk; walk = walk->next) { for (walk = head; walk; walk = walk->next) {
...@@ -787,16 +723,21 @@ int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout) ...@@ -787,16 +723,21 @@ int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout)
static long do_restart_poll(struct restart_block *restart_block) static long do_restart_poll(struct restart_block *restart_block)
{ {
struct pollfd __user *ufds = (struct pollfd __user*)restart_block->arg0; struct pollfd __user *ufds = restart_block->poll.ufds;
int nfds = restart_block->arg1; int nfds = restart_block->poll.nfds;
s64 timeout = ((s64)restart_block->arg3<<32) | (s64)restart_block->arg2; struct timespec *to = NULL, end_time;
int ret; int ret;
ret = do_sys_poll(ufds, nfds, &timeout); if (restart_block->poll.has_timeout) {
end_time.tv_sec = restart_block->poll.tv_sec;
end_time.tv_nsec = restart_block->poll.tv_nsec;
to = &end_time;
}
ret = do_sys_poll(ufds, nfds, to);
if (ret == -EINTR) { if (ret == -EINTR) {
restart_block->fn = do_restart_poll; restart_block->fn = do_restart_poll;
restart_block->arg2 = timeout & 0xFFFFFFFF;
restart_block->arg3 = (u64)timeout >> 32;
ret = -ERESTART_RESTARTBLOCK; ret = -ERESTART_RESTARTBLOCK;
} }
return ret; return ret;
...@@ -805,31 +746,32 @@ static long do_restart_poll(struct restart_block *restart_block) ...@@ -805,31 +746,32 @@ static long do_restart_poll(struct restart_block *restart_block)
asmlinkage long sys_poll(struct pollfd __user *ufds, unsigned int nfds, asmlinkage long sys_poll(struct pollfd __user *ufds, unsigned int nfds,
long timeout_msecs) long timeout_msecs)
{ {
s64 timeout_jiffies; struct timespec end_time, *to = NULL;
int ret; int ret;
if (timeout_msecs > 0) { if (timeout_msecs >= 0) {
#if HZ > 1000 to = &end_time;
/* We can only overflow if HZ > 1000 */ poll_select_set_timeout(to, timeout_msecs / MSEC_PER_SEC,
if (timeout_msecs / 1000 > (s64)0x7fffffffffffffffULL / (s64)HZ) NSEC_PER_MSEC * (timeout_msecs % MSEC_PER_SEC));
timeout_jiffies = -1;
else
#endif
timeout_jiffies = msecs_to_jiffies(timeout_msecs) + 1;
} else {
/* Infinite (< 0) or no (0) timeout */
timeout_jiffies = timeout_msecs;
} }
ret = do_sys_poll(ufds, nfds, &timeout_jiffies); ret = do_sys_poll(ufds, nfds, to);
if (ret == -EINTR) { if (ret == -EINTR) {
struct restart_block *restart_block; struct restart_block *restart_block;
restart_block = &current_thread_info()->restart_block; restart_block = &current_thread_info()->restart_block;
restart_block->fn = do_restart_poll; restart_block->fn = do_restart_poll;
restart_block->arg0 = (unsigned long)ufds; restart_block->poll.ufds = ufds;
restart_block->arg1 = nfds; restart_block->poll.nfds = nfds;
restart_block->arg2 = timeout_jiffies & 0xFFFFFFFF;
restart_block->arg3 = (u64)timeout_jiffies >> 32; if (timeout_msecs >= 0) {
restart_block->poll.tv_sec = end_time.tv_sec;
restart_block->poll.tv_nsec = end_time.tv_nsec;
restart_block->poll.has_timeout = 1;
} else
restart_block->poll.has_timeout = 0;
ret = -ERESTART_RESTARTBLOCK; ret = -ERESTART_RESTARTBLOCK;
} }
return ret; return ret;
...@@ -841,21 +783,16 @@ asmlinkage long sys_ppoll(struct pollfd __user *ufds, unsigned int nfds, ...@@ -841,21 +783,16 @@ asmlinkage long sys_ppoll(struct pollfd __user *ufds, unsigned int nfds,
size_t sigsetsize) size_t sigsetsize)
{ {
sigset_t ksigmask, sigsaved; sigset_t ksigmask, sigsaved;
struct timespec ts; struct timespec ts, end_time, *to = NULL;
s64 timeout = -1;
int ret; int ret;
if (tsp) { if (tsp) {
if (copy_from_user(&ts, tsp, sizeof(ts))) if (copy_from_user(&ts, tsp, sizeof(ts)))
return -EFAULT; return -EFAULT;
/* Cast to u64 to make GCC stop complaining */ to = &end_time;
if ((u64)ts.tv_sec >= (u64)MAX_INT64_SECONDS) if (poll_select_set_timeout(to, ts.tv_sec, ts.tv_nsec))
timeout = -1; /* infinite */ return -EINVAL;
else {
timeout = DIV_ROUND_UP(ts.tv_nsec, NSEC_PER_SEC/HZ);
timeout += ts.tv_sec * HZ;
}
} }
if (sigmask) { if (sigmask) {
...@@ -869,7 +806,7 @@ asmlinkage long sys_ppoll(struct pollfd __user *ufds, unsigned int nfds, ...@@ -869,7 +806,7 @@ asmlinkage long sys_ppoll(struct pollfd __user *ufds, unsigned int nfds,
sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved); sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved);
} }
ret = do_sys_poll(ufds, nfds, &timeout); ret = do_sys_poll(ufds, nfds, to);
/* We can restart this syscall, usually */ /* We can restart this syscall, usually */
if (ret == -EINTR) { if (ret == -EINTR) {
...@@ -887,31 +824,7 @@ asmlinkage long sys_ppoll(struct pollfd __user *ufds, unsigned int nfds, ...@@ -887,31 +824,7 @@ asmlinkage long sys_ppoll(struct pollfd __user *ufds, unsigned int nfds,
} else if (sigmask) } else if (sigmask)
sigprocmask(SIG_SETMASK, &sigsaved, NULL); sigprocmask(SIG_SETMASK, &sigsaved, NULL);
if (tsp && timeout >= 0) { ret = poll_select_copy_remaining(&end_time, tsp, 0, ret);
struct timespec rts;
if (current->personality & STICKY_TIMEOUTS)
goto sticky;
/* Yes, we know it's actually an s64, but it's also positive. */
rts.tv_nsec = jiffies_to_usecs(do_div((*(u64*)&timeout), HZ)) *
1000;
rts.tv_sec = timeout;
if (timespec_compare(&rts, &ts) >= 0)
rts = ts;
if (copy_to_user(tsp, &rts, sizeof(rts))) {
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; return ret;
} }
......
...@@ -114,11 +114,11 @@ void zero_fd_set(unsigned long nr, unsigned long *fdset) ...@@ -114,11 +114,11 @@ void zero_fd_set(unsigned long nr, unsigned long *fdset)
#define MAX_INT64_SECONDS (((s64)(~((u64)0)>>1)/HZ)-1) #define MAX_INT64_SECONDS (((s64)(~((u64)0)>>1)/HZ)-1)
extern int do_select(int n, fd_set_bits *fds, s64 *timeout); extern int do_select(int n, fd_set_bits *fds, struct timespec *end_time);
extern int do_sys_poll(struct pollfd __user * ufds, unsigned int nfds, extern int do_sys_poll(struct pollfd __user * ufds, unsigned int nfds,
s64 *timeout); struct timespec *end_time);
extern int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp, extern int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp,
fd_set __user *exp, s64 *timeout); fd_set __user *exp, struct timespec *end_time);
extern int poll_select_set_timeout(struct timespec *to, long sec, long nsec); extern int poll_select_set_timeout(struct timespec *to, long sec, long nsec);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment