diff options
Diffstat (limited to 'fs/select.c')
-rw-r--r-- | fs/select.c | 86 |
1 files changed, 37 insertions, 49 deletions
diff --git a/fs/select.c b/fs/select.c index 7ab56a4b8..38742c84e 100644 --- a/fs/select.c +++ b/fs/select.c @@ -10,25 +10,12 @@ * parameter to reflect time remaining. */ -#include <linux/types.h> -#include <linux/time.h> -#include <linux/fs.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/string.h> -#include <linux/stat.h> -#include <linux/signal.h> -#include <linux/errno.h> -#include <linux/personality.h> -#include <linux/mm.h> #include <linux/malloc.h> -#include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/poll.h> #include <linux/file.h> #include <asm/uaccess.h> -#include <asm/system.h> #define ROUND_UP(x,y) (((x)+(y)-1)/(y)) #define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM) @@ -124,28 +111,25 @@ get_max: #define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR) #define POLLEX_SET (POLLPRI) -int do_select(int n, fd_set_buffer *fds, unsigned long timeout) +int do_select(int n, fd_set_buffer *fds, long *timeout) { poll_table wait_table, *wait; - int retval; - int i; - - lock_kernel(); + int retval, i; + long __timeout = *timeout; wait = NULL; - current->timeout = timeout; - if (timeout) { - struct poll_table_entry *entry = (struct poll_table_entry *) - __get_free_page(GFP_KERNEL); - if (!entry) { - retval = -ENOMEM; - goto out_nowait; - } + if (__timeout) { + struct poll_table_entry *entry = (struct poll_table_entry *) __get_free_page(GFP_KERNEL); + if (!entry) + return -ENOMEM; + wait_table.nr = 0; wait_table.entry = entry; wait = &wait_table; } + lock_kernel(); + retval = max_select_fd(n, fds); if (retval < 0) goto out; @@ -190,18 +174,22 @@ int do_select(int n, fd_set_buffer *fds, unsigned long timeout) } } wait = NULL; - if (retval || !current->timeout || signal_pending(current)) + if (retval || !__timeout || signal_pending(current)) break; - schedule(); + __timeout = schedule_timeout(__timeout); } current->state = TASK_RUNNING; out: - if (timeout) { + if (*timeout) { free_wait(&wait_table); free_page((unsigned long) wait_table.entry); } -out_nowait: + + /* + * Up-to-date the caller timeout. + */ + *timeout = __timeout; unlock_kernel(); return retval; } @@ -214,14 +202,17 @@ out_nowait: * Update: ERESTARTSYS breaks at least the xview clock binary, so * I'm trying ERESTARTNOHAND which restart only when you want to. */ +#define MAX_SELECT_SECONDS \ + ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1) + asmlinkage int sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp) { fd_set_buffer *fds; - unsigned long timeout; + long timeout; int ret; - timeout = ~0UL; + timeout = MAX_SCHEDULE_TIMEOUT; if (tvp) { time_t sec, usec; @@ -230,10 +221,10 @@ sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp) || (ret = __get_user(usec, &tvp->tv_usec))) goto out_nofds; - timeout = ROUND_UP(usec, 1000000/HZ); - timeout += sec * (unsigned long) HZ; - if (timeout) - timeout += jiffies + 1; + if ((unsigned long) sec < MAX_SELECT_SECONDS) { + timeout = ROUND_UP(usec, 1000000/HZ); + timeout += sec * (unsigned long) HZ; + } } ret = -ENOMEM; @@ -253,12 +244,11 @@ sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp) zero_fd_set(n, fds->res_out); zero_fd_set(n, fds->res_ex); - ret = do_select(n, fds, timeout); + ret = do_select(n, fds, &timeout); if (tvp && !(current->personality & STICKY_TIMEOUTS)) { - unsigned long timeout = current->timeout - jiffies - 1; time_t sec = 0, usec = 0; - if ((long) timeout > 0) { + if (timeout) { sec = timeout / HZ; usec = timeout % HZ; usec *= (1000000/HZ); @@ -266,7 +256,6 @@ sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp) put_user(sec, &tvp->tv_sec); put_user(usec, &tvp->tv_usec); } - current->timeout = 0; if (ret < 0) goto out; @@ -287,7 +276,8 @@ out_nofds: return ret; } -static int do_poll(unsigned int nfds, struct pollfd *fds, poll_table *wait) +static int do_poll(unsigned int nfds, struct pollfd *fds, poll_table *wait, + long timeout) { int count = 0; @@ -321,15 +311,15 @@ static int do_poll(unsigned int nfds, struct pollfd *fds, poll_table *wait) } wait = NULL; - if (count || !current->timeout || signal_pending(current)) + if (count || !timeout || signal_pending(current)) break; - schedule(); + timeout = schedule_timeout(timeout); } current->state = TASK_RUNNING; return count; } -asmlinkage int sys_poll(struct pollfd * ufds, unsigned int nfds, int timeout) +asmlinkage int sys_poll(struct pollfd * ufds, unsigned int nfds, long timeout) { int i, fdcount, err, size; struct pollfd * fds, *fds1; @@ -342,9 +332,9 @@ asmlinkage int sys_poll(struct pollfd * ufds, unsigned int nfds, int timeout) goto out; if (timeout < 0) - timeout = 0x7fffffff; + timeout = MAX_SCHEDULE_TIMEOUT; else if (timeout) - timeout = ((unsigned long)timeout*HZ+999)/1000+jiffies+1; + timeout = (timeout*HZ+999)/1000+1; err = -ENOMEM; if (timeout) { @@ -366,9 +356,7 @@ asmlinkage int sys_poll(struct pollfd * ufds, unsigned int nfds, int timeout) if (copy_from_user(fds, ufds, size)) goto out_fds; - current->timeout = timeout; - fdcount = do_poll(nfds, fds, wait); - current->timeout = 0; + fdcount = do_poll(nfds, fds, wait, timeout); /* OK, now copy the revents fields back to user space. */ fds1 = fds; |