summaryrefslogtreecommitdiffstats
path: root/fs/select.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-12-16 05:34:03 +0000
committerRalf Baechle <ralf@linux-mips.org>1997-12-16 05:34:03 +0000
commit967c65a99059fd459b956c1588ce0ba227912c4e (patch)
tree8224d013ff5d255420713d05610c7efebd204d2a /fs/select.c
parente20c1cc1656a66a2773bca4591a895cbc12696ff (diff)
Merge with Linux 2.1.72, part 1.
Diffstat (limited to 'fs/select.c')
-rw-r--r--fs/select.c268
1 files changed, 94 insertions, 174 deletions
diff --git a/fs/select.c b/fs/select.c
index d804d58b3..4c88f0437 100644
--- a/fs/select.c
+++ b/fs/select.c
@@ -24,10 +24,10 @@
#include <linux/malloc.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
+#include <linux/poll.h>
#include <asm/uaccess.h>
#include <asm/system.h>
-#include <asm/poll.h>
#define ROUND_UP(x,y) (((x)+(y)-1)/(y))
#define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)
@@ -39,9 +39,10 @@
* understand what I'm doing here, then you understand how the linux
* sleep/wakeup mechanism works.
*
- * Two very simple procedures, poll_wait() and free_wait() make all the work.
- * poll_wait() is an inline-function defined in <linux/sched.h>, as all select/poll
- * functions have to call it to add an entry to the poll table.
+ * Two very simple procedures, poll_wait() and free_wait() make all the
+ * work. poll_wait() is an inline-function defined in <linux/sched.h>,
+ * as all select/poll functions have to call it to add an entry to the
+ * poll table.
*/
/*
@@ -61,26 +62,6 @@ static void free_wait(poll_table * p)
}
}
-/*
- * For the kernel fd_set we use a fixed set-size for allocation purposes.
- * This set-size doesn't necessarily bear any relation to the size the user
- * uses, but should preferably obviously be larger than any possible user
- * size (NR_OPEN bits).
- *
- * We need 6 bitmaps (in/out/ex for both incoming and outgoing), and we
- * allocate one page for all the bitmaps. Thus we have 8*PAGE_SIZE bits,
- * to be divided by 6. And we'd better make sure we round to a full
- * long-word (in fact, we'll round to 64 bytes).
- */
-#define KFDS_64BLOCK ((PAGE_SIZE/(6*64))*64)
-#define KFDS_NR (KFDS_64BLOCK*8 > NR_OPEN ? NR_OPEN : KFDS_64BLOCK*8)
-typedef unsigned long kernel_fd_set[KFDS_NR/(8*sizeof(unsigned long))];
-
-typedef struct {
- kernel_fd_set in, out, ex;
- kernel_fd_set res_in, res_out, res_ex;
-} fd_set_buffer;
-
#define __IN(in) (in)
#define __OUT(in) (in + sizeof(kernel_fd_set)/sizeof(unsigned long))
#define __EX(in) (in + 2*sizeof(kernel_fd_set)/sizeof(unsigned long))
@@ -141,11 +122,28 @@ get_max:
#define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR)
#define POLLEX_SET (POLLPRI)
-static int do_select(int n, fd_set_buffer *fds, poll_table *wait)
+int do_select(int n, fd_set_buffer *fds, unsigned long timeout)
{
+ poll_table wait_table, *wait;
int retval;
int i;
+ lock_kernel();
+
+ 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;
+ }
+ wait_table.nr = 0;
+ wait_table.entry = entry;
+ wait = &wait_table;
+ }
+
retval = max_select_fd(n, fds);
if (retval < 0)
goto out;
@@ -154,33 +152,36 @@ static int do_select(int n, fd_set_buffer *fds, poll_table *wait)
for (;;) {
struct file ** fd = current->files->fd;
current->state = TASK_INTERRUPTIBLE;
- for (i = 0 ; i < n ; i++,fd++) {
+ for (i = 0 ; i < n ; i++, fd++) {
unsigned long bit = BIT(i);
unsigned long *in = MEM(i,fds->in);
+ unsigned long mask;
+ struct file *file;
- if (bit & BITS(in)) {
- struct file * file = *fd;
- unsigned int mask = POLLNVAL;
- if (file) {
- mask = DEFAULT_POLLMASK;
- if (file->f_op && file->f_op->poll)
- mask = file->f_op->poll(file, wait);
- }
- if ((mask & POLLIN_SET) && ISSET(bit, __IN(in))) {
- SET(bit, __RES_IN(in));
- retval++;
- wait = NULL;
- }
- if ((mask & POLLOUT_SET) && ISSET(bit, __OUT(in))) {
- SET(bit, __RES_OUT(in));
- retval++;
- wait = NULL;
- }
- if ((mask & POLLEX_SET) && ISSET(bit, __EX(in))) {
- SET(bit, __RES_EX(in));
- retval++;
- wait = NULL;
- }
+ if (!(bit & BITS(in)))
+ continue;
+
+ file = *fd;
+ mask = POLLNVAL;
+ if (file) {
+ mask = DEFAULT_POLLMASK;
+ if (file->f_op && file->f_op->poll)
+ mask = file->f_op->poll(file, wait);
+ }
+ if ((mask & POLLIN_SET) && ISSET(bit, __IN(in))) {
+ SET(bit, __RES_IN(in));
+ retval++;
+ wait = NULL;
+ }
+ if ((mask & POLLOUT_SET) && ISSET(bit, __OUT(in))) {
+ SET(bit, __RES_OUT(in));
+ retval++;
+ wait = NULL;
+ }
+ if ((mask & POLLEX_SET) && ISSET(bit, __EX(in))) {
+ SET(bit, __RES_EX(in));
+ retval++;
+ wait = NULL;
}
}
wait = NULL;
@@ -189,82 +190,18 @@ static int do_select(int n, fd_set_buffer *fds, poll_table *wait)
schedule();
}
current->state = TASK_RUNNING;
-out:
- return retval;
-}
-
-/*
- * We do a VERIFY_WRITE here even though we are only reading this time:
- * we'll write to it eventually..
- *
- * Use "unsigned long" accesses to let user-mode fd_set's be long-aligned.
- */
-static int __get_fd_set(unsigned long nr, unsigned long * fs_pointer, unsigned long * fdset)
-{
- /* round up nr to nearest "unsigned long" */
- nr = (nr + 8*sizeof(unsigned long)-1) / (8*sizeof(unsigned long));
- if (fs_pointer) {
- int error = verify_area(VERIFY_WRITE,fs_pointer,
- nr*sizeof(unsigned long));
- if (!error) {
- while (nr) {
- __get_user(*fdset, fs_pointer);
- nr--;
- fs_pointer++;
- fdset++;
- }
- }
- return error;
- }
- while (nr) {
- *fdset = 0;
- nr--;
- fdset++;
- }
- return 0;
-}
-static void __set_fd_set(long nr, unsigned long * fs_pointer, unsigned long * fdset)
-{
- if (!fs_pointer)
- return;
- while (nr >= 0) {
- __put_user(*fdset, fs_pointer);
- nr -= 8 * sizeof(unsigned long);
- fdset++;
- fs_pointer++;
+out:
+ if (timeout) {
+ free_wait(&wait_table);
+ free_page((unsigned long) wait_table.entry);
}
+out_nowait:
+ current->timeout = 0;
+ unlock_kernel();
+ return retval;
}
-/* We can do long accesses here, kernel fdsets are always long-aligned */
-static inline void __zero_fd_set(long nr, unsigned long * fdset)
-{
- while (nr >= 0) {
- *fdset = 0;
- nr -= 8 * sizeof(unsigned long);
- fdset++;
- }
-}
-
-/*
- * Note a few subtleties: we use "long" for the dummy, not int, and we do a
- * subtract by 1 on the nr of file descriptors. The former is better for
- * machines with long > int, and the latter allows us to test the bit count
- * against "zero or positive", which can mostly be just a sign bit test..
- *
- * Unfortunately this scheme falls apart on big endian machines where
- * sizeof(long) > sizeof(int) (ie. V9 Sparc). -DaveM
- */
-
-#define get_fd_set(nr,fsp,fdp) \
-__get_fd_set(nr, (unsigned long *) (fsp), (unsigned long *) (fdp))
-
-#define set_fd_set(nr,fsp,fdp) \
-__set_fd_set((nr)-1, (unsigned long *) (fsp), (unsigned long *) (fdp))
-
-#define zero_fd_set(nr,fdp) \
-__zero_fd_set((nr)-1, (unsigned long *) (fdp))
-
/*
* We can actually return ERESTARTSYS instead of EINTR, but I'd
* like to be certain this leads to no problems. So I return
@@ -273,64 +210,50 @@ __zero_fd_set((nr)-1, (unsigned long *) (fdp))
* Update: ERESTARTSYS breaks at least the xview clock binary, so
* I'm trying ERESTARTNOHAND which restart only when you want to.
*/
-asmlinkage int sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp)
+asmlinkage int
+sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp)
{
- int error;
fd_set_buffer *fds;
unsigned long timeout;
- poll_table wait_table, *wait;
+ int ret;
- lock_kernel();
timeout = ~0UL;
if (tvp) {
- error = -EFAULT;
- if (!access_ok(VERIFY_WRITE, tvp, sizeof(*tvp)))
- goto out_nowait;
- error = __get_user(timeout, &tvp->tv_usec);
- if (error)
- goto out_nowait;
- timeout = ROUND_UP(timeout,(1000000/HZ));
- {
- unsigned long tmp;
- error = __get_user(tmp, &tvp->tv_sec);
- if (error)
- goto out_nowait;
- timeout += tmp * (unsigned long) HZ;
- }
+ time_t sec, usec;
+
+ if ((ret = verify_area(VERIFY_READ, tvp, sizeof(*tvp)))
+ || (ret = __get_user(sec, &tvp->tv_sec))
+ || (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;
}
- error = -ENOMEM;
- wait = NULL;
- current->timeout = timeout;
- if (timeout) {
- struct poll_table_entry *entry;
- entry = (struct poll_table_entry *) __get_free_page(GFP_KERNEL);
- if (!entry)
- goto out_nowait;
- wait_table.nr = 0;
- wait_table.entry = entry;
- wait = &wait_table;
- }
+ ret = -ENOMEM;
fds = (fd_set_buffer *) __get_free_page(GFP_KERNEL);
if (!fds)
goto out_nofds;
- error = -EINVAL;
+ ret = -EINVAL;
if (n < 0)
goto out;
if (n > KFDS_NR)
n = KFDS_NR;
- if ((error = get_fd_set(n, inp, &fds->in)) ||
- (error = get_fd_set(n, outp, &fds->out)) ||
- (error = get_fd_set(n, exp, &fds->ex))) goto out;
- zero_fd_set(n, &fds->res_in);
- zero_fd_set(n, &fds->res_out);
- zero_fd_set(n, &fds->res_ex);
- error = do_select(n, fds, wait);
+ if ((ret = get_fd_set(n, inp, fds->in)) ||
+ (ret = get_fd_set(n, outp, fds->out)) ||
+ (ret = get_fd_set(n, exp, fds->ex)))
+ goto out;
+ zero_fd_set(n, fds->res_in);
+ 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)) {
unsigned long timeout = current->timeout - jiffies - 1;
- unsigned long sec = 0, usec = 0;
+ time_t sec = 0, usec = 0;
if ((long) timeout > 0) {
sec = timeout / HZ;
usec = timeout % HZ;
@@ -340,27 +263,24 @@ asmlinkage int sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct
put_user(usec, &tvp->tv_usec);
}
current->timeout = 0;
- if (error < 0)
+
+ if (ret < 0)
goto out;
- if (!error) {
- error = -ERESTARTNOHAND;
+ if (!ret) {
+ ret = -ERESTARTNOHAND;
if (signal_pending(current))
goto out;
- error = 0;
+ ret = 0;
}
- set_fd_set(n, inp, &fds->res_in);
- set_fd_set(n, outp, &fds->res_out);
- set_fd_set(n, exp, &fds->res_ex);
+
+ set_fd_set(n, inp, fds->res_in);
+ set_fd_set(n, outp, fds->res_out);
+ set_fd_set(n, exp, fds->res_ex);
+
out:
free_page((unsigned long) fds);
out_nofds:
- if (wait) {
- free_wait(&wait_table);
- free_page((unsigned long) wait->entry);
- }
-out_nowait:
- unlock_kernel();
- return error;
+ return ret;
}
static int do_poll(unsigned int nfds, struct pollfd *fds, poll_table *wait)