From beb116954b9b7f3bb56412b2494b562f02b864b1 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Tue, 7 Jan 1997 02:33:00 +0000 Subject: Import of Linux/MIPS 2.1.14 --- fs/select.c | 332 ++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 201 insertions(+), 131 deletions(-) (limited to 'fs/select.c') diff --git a/fs/select.c b/fs/select.c index 0b277f9d6..5ffb84f86 100644 --- a/fs/select.c +++ b/fs/select.c @@ -22,7 +22,7 @@ #include #include -#include +#include #include #define ROUND_UP(x,y) (((x)+(y)-1)/(y)) @@ -35,7 +35,7 @@ * sleep/wakeup mechanism works. * * Two very simple procedures, select_wait() and free_wait() make all the work. - * select_wait() is a inline-function defined in , as all select + * select_wait() is an inline-function defined in , as all select * functions have to call it to add an entry to the select table. */ @@ -67,129 +67,212 @@ static void free_wait(select_table * p) * and we aren't going to sleep on the select_table. -- jrs */ -static int check(int flag, select_table * wait, struct file * file) +static inline int __check( + int (*select) (struct inode *, struct file *, int, select_table *), + struct inode *inode, + struct file *file, + int flag, + select_table * wait) { - struct inode * inode; - struct file_operations *fops; - int (*select) (struct inode *, struct file *, int, select_table *); - - inode = file->f_inode; - if ((fops = file->f_op) && (select = fops->select)) - return select(inode, file, flag, wait) - || (wait && select(inode, file, flag, NULL)); - if (flag != SEL_EX) - return 1; - return 0; + return select(inode, file, flag, wait) || + (wait && select(inode, file, flag, NULL)); } -static int do_select(int n, fd_set *in, fd_set *out, fd_set *ex, - fd_set *res_in, fd_set *res_out, fd_set *res_ex) +#define check(flag,wait,file) \ +(((file)->f_op && (file)->f_op->select) ? \ + __check((file)->f_op->select,(file)->f_inode,file,flag,wait) \ + : \ + (flag != SEL_EX)) + +/* + * Due to kernel stack usage, we use a _limited_ fd_set type here, and once + * we really start supporting >256 file descriptors we'll probably have to + * allocate the kernel fd_set copies dynamically.. (The kernel select routines + * are careful to touch only the defined low bits of any fd_set pointer, this + * is important for performance too). + */ +typedef unsigned long limited_fd_set[NR_OPEN/(8*(sizeof(unsigned long)))]; + +typedef struct { + limited_fd_set in, out, ex; + limited_fd_set res_in, res_out, res_ex; +} fd_set_buffer; + +#define __IN(in) (in) +#define __OUT(in) (in + sizeof(limited_fd_set)/sizeof(unsigned long)) +#define __EX(in) (in + 2*sizeof(limited_fd_set)/sizeof(unsigned long)) +#define __RES_IN(in) (in + 3*sizeof(limited_fd_set)/sizeof(unsigned long)) +#define __RES_OUT(in) (in + 4*sizeof(limited_fd_set)/sizeof(unsigned long)) +#define __RES_EX(in) (in + 5*sizeof(limited_fd_set)/sizeof(unsigned long)) + +#define BITS(in) (*__IN(in)|*__OUT(in)|*__EX(in)) + +static int max_select_fd(unsigned long n, fd_set_buffer *fds) { - int count; - select_table wait_table, *wait; - struct select_table_entry *entry; + unsigned long *open_fds, *in; unsigned long set; - int i,j; - int max = -1; + int max; - for (j = 0 ; j < __FDSET_LONGS ; j++) { - i = j << 5; - if (i >= n) - break; - set = in->fds_bits[j] | out->fds_bits[j] | ex->fds_bits[j]; - for ( ; set ; i++,set >>= 1) { - if (i >= n) - goto end_check; - if (!(set & 1)) - continue; - if (!current->files->fd[i]) - return -EBADF; - if (!current->files->fd[i]->f_inode) - return -EBADF; - max = i; + /* handle last in-complete long-word first */ + set = ~(~0UL << (n & (__NFDBITS-1))); + n /= __NFDBITS; + open_fds = current->files->open_fds.fds_bits+n; + in = fds->in+n; + max = 0; + if (set) { + set &= BITS(in); + if (set) { + if (!(set & ~*open_fds)) + goto get_max; + return -EBADF; } } -end_check: - n = max + 1; + while (n) { + in--; + open_fds--; + n--; + set = BITS(in); + if (!set) + continue; + if (set & ~*open_fds) + return -EBADF; + if (max) + continue; +get_max: + do { + max++; + set >>= 1; + } while (set); + max += n * __NFDBITS; + } + + return max; +} + +#define BIT(i) (1UL << ((i)&(__NFDBITS-1))) +#define MEM(i,m) ((m)+(unsigned)(i)/__NFDBITS) +#define ISSET(i,m) (((i)&*(m)) != 0) +#define SET(i,m) (*(m) |= (i)) + +static int do_select(int n, fd_set_buffer *fds) +{ + int retval; + select_table wait_table, *wait; + struct select_table_entry *entry; + int i; + + retval = max_select_fd(n, fds); + if (retval < 0) + goto out; + n = retval; + retval = -ENOMEM; if(!(entry = (struct select_table_entry*) __get_free_page(GFP_KERNEL))) - return -ENOMEM; - FD_ZERO(res_in); - FD_ZERO(res_out); - FD_ZERO(res_ex); - count = 0; + goto out; + retval = 0; wait_table.nr = 0; wait_table.entry = entry; wait = &wait_table; -repeat: - current->state = TASK_INTERRUPTIBLE; - for (i = 0 ; i < n ; i++) { - if (FD_ISSET(i,in) && check(SEL_IN,wait,current->files->fd[i])) { - FD_SET(i, res_in); - count++; - wait = NULL; - } - if (FD_ISSET(i,out) && check(SEL_OUT,wait,current->files->fd[i])) { - FD_SET(i, res_out); - count++; - wait = NULL; - } - if (FD_ISSET(i,ex) && check(SEL_EX,wait,current->files->fd[i])) { - FD_SET(i, res_ex); - count++; - wait = NULL; + for (;;) { + struct file ** fd = current->files->fd; + current->state = TASK_INTERRUPTIBLE; + for (i = 0 ; i < n ; i++,fd++) { + unsigned long bit = BIT(i); + unsigned long *in = MEM(i,fds->in); + if (ISSET(bit,__IN(in)) && check(SEL_IN,wait,*fd)) { + SET(bit, __RES_IN(in)); + retval++; + wait = NULL; + } + if (ISSET(bit,__OUT(in)) && check(SEL_OUT,wait,*fd)) { + SET(bit, __RES_OUT(in)); + retval++; + wait = NULL; + } + if (ISSET(bit,__EX(in)) && check(SEL_EX,wait,*fd)) { + SET(bit, __RES_EX(in)); + retval++; + wait = NULL; + } } - } - wait = NULL; - if (!count && current->timeout && !(current->signal & ~current->blocked)) { + wait = NULL; + if (retval || !current->timeout || (current->signal & ~current->blocked)) + break; schedule(); - goto repeat; } free_wait(&wait_table); free_page((unsigned long) entry); current->state = TASK_RUNNING; - return count; +out: + return retval; } /* * We do a VERIFY_WRITE here even though we are only reading this time: * we'll write to it eventually.. + * + * Use "int" accesses to let user-mode fd_set's be int-aligned. */ -static int __get_fd_set(int nr, unsigned long * fs_pointer, unsigned long * fdset) +static int __get_fd_set(unsigned long nr, int * fs_pointer, int * fdset) { - int error; - - FD_ZERO(fdset); - if (!fs_pointer) - return 0; - error = verify_area(VERIFY_WRITE,fs_pointer,sizeof(fd_set)); - if (error) + /* round up nr to nearest "int" */ + nr = (nr + 8*sizeof(int)-1) / (8*sizeof(int)); + if (fs_pointer) { + int error = verify_area(VERIFY_WRITE,fs_pointer,nr*sizeof(int)); + if (!error) { + while (nr) { + get_user(*fdset, fs_pointer); + nr--; + fs_pointer++; + fdset++; + } + } return error; - while (nr > 0) { - *fdset = get_user(fs_pointer); + } + while (nr) { + *fdset = 0; + nr--; fdset++; - fs_pointer++; - nr -= 8 * sizeof(unsigned long); } return 0; } -static void __set_fd_set(int nr, unsigned long * fs_pointer, unsigned long * fdset) +static void __set_fd_set(long nr, int * fs_pointer, int * fdset) { if (!fs_pointer) return; - while (nr > 0) { + while (nr >= 0) { put_user(*fdset, fs_pointer); + nr -= 8 * sizeof(int); fdset++; fs_pointer++; - nr -= 8 * sizeof(unsigned long); } } +/* 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.. + */ + #define get_fd_set(nr,fsp,fdp) \ -__get_fd_set(nr, (unsigned long *) (fsp), (unsigned long *) (fdp)) +__get_fd_set(nr, (int *) (fsp), (int *) (fdp)) #define set_fd_set(nr,fsp,fdp) \ -__set_fd_set(nr, (unsigned long *) (fsp), (unsigned long *) (fdp)) +__set_fd_set((nr)-1, (int *) (fsp), (int *) (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 @@ -201,72 +284,59 @@ __set_fd_set(nr, (unsigned long *) (fsp), (unsigned long *) (fdp)) */ asmlinkage int sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp) { - int i; - fd_set res_in, in; - fd_set res_out, out; - fd_set res_ex, ex; + int error; + fd_set_buffer fds; unsigned long timeout; + error = -EINVAL; if (n < 0) - return -EINVAL; + goto out; if (n > NR_OPEN) n = NR_OPEN; - if ((i = get_fd_set(n, inp, &in)) || - (i = get_fd_set(n, outp, &out)) || - (i = get_fd_set(n, exp, &ex))) return i; + 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; timeout = ~0UL; if (tvp) { - i = verify_area(VERIFY_WRITE, tvp, sizeof(*tvp)); - if (i) - return i; - timeout = ROUND_UP(get_fs_long((unsigned long *)&tvp->tv_usec),(1000000/HZ)); - timeout += get_fs_long((unsigned long *)&tvp->tv_sec) * HZ; + error = verify_area(VERIFY_WRITE, tvp, sizeof(*tvp)); + if (error) + goto out; + get_user(timeout, &tvp->tv_usec); + timeout = ROUND_UP(timeout,(1000000/HZ)); + { + unsigned long tmp; + get_user(tmp, &tvp->tv_sec); + timeout += tmp * (unsigned long) HZ; + } if (timeout) timeout += jiffies + 1; } + zero_fd_set(n, &fds.res_in); + zero_fd_set(n, &fds.res_out); + zero_fd_set(n, &fds.res_ex); current->timeout = timeout; - i = do_select(n, &in, &out, &ex, &res_in, &res_out, &res_ex); + error = do_select(n, &fds); timeout = current->timeout - jiffies - 1; current->timeout = 0; if ((long) timeout < 0) timeout = 0; if (tvp && !(current->personality & STICKY_TIMEOUTS)) { - put_fs_long(timeout/HZ, (unsigned long *) &tvp->tv_sec); + put_user(timeout/HZ, &tvp->tv_sec); timeout %= HZ; timeout *= (1000000/HZ); - put_fs_long(timeout, (unsigned long *) &tvp->tv_usec); + put_user(timeout, &tvp->tv_usec); } - if (i < 0) - return i; - if (!i && (current->signal & ~current->blocked)) - return -ERESTARTNOHAND; - set_fd_set(n, inp, &res_in); - set_fd_set(n, outp, &res_out); - set_fd_set(n, exp, &res_ex); - return i; -} - -/* - * Perform the select(nd, in, out, ex, tv) system call. - * Linux/i386 didn't use to be able to handle 5 system call - * parameters, so the old select used a memory block for - * parameter passing.. - */ -asmlinkage int old_select(unsigned long *buffer) -{ - int n; - fd_set *inp; - fd_set *outp; - fd_set *exp; - struct timeval *tvp; - - n = verify_area(VERIFY_READ, buffer, 5*sizeof(unsigned long)); - if (n) - return n; - n = get_user(buffer); - inp = (fd_set *) get_user(buffer+1); - outp = (fd_set *) get_user(buffer+2); - exp = (fd_set *) get_user(buffer+3); - tvp = (struct timeval *) get_user(buffer+4); - return sys_select(n, inp, outp, exp, tvp); + if (error < 0) + goto out; + if (!error) { + error = -ERESTARTNOHAND; + if (current->signal & ~current->blocked) + goto out; + error = 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); +out: + return error; } -- cgit v1.2.3