patch-2.1.23 linux/fs/select.c
Next file: linux/fs/smbfs/dir.c
Previous file: linux/fs/romfs/inode.c
Back to the patch index
Back to the overall index
- Lines: 282
- Date:
Sun Jan 26 12:07:45 1997
- Orig file:
v2.1.22/linux/fs/select.c
- Orig date:
Tue Dec 31 21:41:08 1996
diff -u --recursive --new-file v2.1.22/linux/fs/select.c linux/fs/select.c
@@ -1,5 +1,5 @@
/*
- * This file contains the procedures for the handling of select
+ * This file contains the procedures for the handling of select and poll
*
* Created for Linux based loosely upon Mathius Lattner's minix
* patches by Peter MacDonald. Heavily edited by Linus.
@@ -21,11 +21,16 @@
#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 <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)
/*
* Ok, Peter made a complicated, but straightforward multiple_wait() function.
@@ -34,20 +39,20 @@
* understand what I'm doing here, then you understand how the linux
* sleep/wakeup mechanism works.
*
- * Two very simple procedures, select_wait() and free_wait() make all the work.
- * select_wait() is an inline-function defined in <linux/sched.h>, as all select
- * functions have to call it to add an entry to the select 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.
*/
/*
- * I rewrote this again to make the select_table size variable, take some
+ * I rewrote this again to make the poll_table size variable, take some
* more shortcuts, improve responsiveness, and remove another race that
* Linus noticed. -- jrs
*/
-static void free_wait(select_table * p)
+static void free_wait(poll_table * p)
{
- struct select_table_entry * entry = p->entry + p->nr;
+ struct poll_table_entry * entry = p->entry + p->nr;
while (p->nr > 0) {
p->nr--;
@@ -57,34 +62,6 @@
}
/*
- * The check function checks the ready status of a file using the vfs layer.
- *
- * If the file was not ready we were added to its wait queue. But in
- * case it became ready just after the check and just before it called
- * select_wait, we call it again, knowing we are already on its
- * wait queue this time. The second call is not necessary if the
- * select_table is NULL indicating an earlier file check was ready
- * and we aren't going to sleep on the select_table. -- jrs
- */
-
-static inline int __check(
- int (*select) (struct inode *, struct file *, int, select_table *),
- struct inode *inode,
- struct file *file,
- int flag,
- select_table * wait)
-{
- return select(inode, file, flag, wait) ||
- (wait && select(inode, file, flag, NULL));
-}
-
-#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
@@ -154,11 +131,15 @@
#define ISSET(i,m) (((i)&*(m)) != 0)
#define SET(i,m) (*(m) |= (i))
+#define POLLIN_SET (POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP | POLLERR)
+#define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR)
+#define POLLEX_SET (POLLPRI)
+
static int do_select(int n, fd_set_buffer *fds)
{
int retval;
- select_table wait_table, *wait;
- struct select_table_entry *entry;
+ poll_table wait_table, *wait;
+ struct poll_table_entry *entry;
int i;
retval = max_select_fd(n, fds);
@@ -166,7 +147,8 @@
goto out;
n = retval;
retval = -ENOMEM;
- if(!(entry = (struct select_table_entry*) __get_free_page(GFP_KERNEL)))
+ entry = (struct poll_table_entry *) __get_free_page(GFP_KERNEL);
+ if (!entry)
goto out;
retval = 0;
wait_table.nr = 0;
@@ -178,24 +160,30 @@
for (i = 0 ; i < n ; i++,fd++) {
unsigned long bit = BIT(i);
unsigned long *in = MEM(i,fds->in);
- struct file * file = *fd;
- if (!file)
- continue;
- if (ISSET(bit,__IN(in)) && check(SEL_IN,wait,file)) {
- SET(bit, __RES_IN(in));
- retval++;
- wait = NULL;
- }
- if (ISSET(bit,__OUT(in)) && check(SEL_OUT,wait,file)) {
- SET(bit, __RES_OUT(in));
- retval++;
- wait = NULL;
- }
- if (ISSET(bit,__EX(in)) && check(SEL_EX,wait,file)) {
- SET(bit, __RES_EX(in));
- retval++;
- wait = NULL;
+ 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;
+ }
}
}
wait = NULL;
@@ -288,11 +276,11 @@
*/
asmlinkage int sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp)
{
- int error;
+ int error = -EINVAL;
fd_set_buffer fds;
unsigned long timeout;
- error = -EINVAL;
+ lock_kernel();
if (n < 0)
goto out;
if (n > NR_OPEN)
@@ -342,5 +330,105 @@
set_fd_set(n, outp, &fds.res_out);
set_fd_set(n, exp, &fds.res_ex);
out:
+ unlock_kernel();
return error;
+}
+
+static int do_poll(unsigned int nfds, struct pollfd *fds, poll_table *wait)
+{
+ int count;
+ struct file ** fd = current->files->fd;
+
+ count = 0;
+ for (;;) {
+ unsigned int j;
+ struct pollfd * fdpnt;
+
+ current->state = TASK_INTERRUPTIBLE;
+ for (fdpnt = fds, j = 0; j < nfds; j++, fdpnt++) {
+ unsigned int i;
+ unsigned int mask;
+ struct file * file;
+
+ mask = POLLNVAL;
+ i = fdpnt->fd;
+ if (i < NR_OPEN && (file = fd[i]) != NULL) {
+ mask = DEFAULT_POLLMASK;
+ if (file->f_op && file->f_op->poll)
+ mask = file->f_op->poll(file, wait);
+ mask &= fdpnt->events | POLLERR | POLLHUP;
+ }
+ if (mask) {
+ wait = NULL;
+ count++;
+ }
+ fdpnt->revents = mask;
+ }
+
+ wait = NULL;
+ if (count || !current->timeout || (current->signal & ~current->blocked))
+ break;
+ schedule();
+ }
+ current->state = TASK_RUNNING;
+ return count;
+}
+
+asmlinkage int sys_poll(struct pollfd * ufds, unsigned int nfds, int timeout)
+{
+ int i, count, fdcount, err = -EINVAL;
+ struct pollfd * fds, *fds1;
+ poll_table wait_table;
+ struct poll_table_entry *entry;
+
+ lock_kernel();
+ if (nfds > NR_OPEN)
+ goto out;
+
+ err = -ENOMEM;
+ entry = (struct poll_table_entry *) __get_free_page(GFP_KERNEL);
+ if (!entry)
+ goto out;
+ fds = (struct pollfd *) kmalloc(nfds*sizeof(struct pollfd), GFP_KERNEL);
+ if (!fds) {
+ free_page((unsigned long) entry);
+ goto out;
+ }
+
+ err = -EFAULT;
+ if (copy_from_user(fds, ufds, nfds*sizeof(struct pollfd))) {
+ free_page((unsigned long)entry);
+ kfree(fds);
+ goto out;
+ }
+
+ if (timeout < 0)
+ timeout = 0x7fffffff;
+ else if (timeout)
+ timeout = ((unsigned long)timeout*HZ+999)/1000+jiffies+1;
+ current->timeout = timeout;
+
+ count = 0;
+ wait_table.nr = 0;
+ wait_table.entry = entry;
+
+ fdcount = do_poll(nfds, fds, &wait_table);
+ current->timeout = 0;
+
+ free_wait(&wait_table);
+ free_page((unsigned long) entry);
+
+ /* OK, now copy the revents fields back to user space. */
+ fds1 = fds;
+ for(i=0; i < (int)nfds; i++, ufds++, fds++) {
+ __put_user(fds->revents, &ufds->revents);
+ }
+ kfree(fds1);
+ if (!fdcount && (current->signal & ~current->blocked))
+ err = -EINTR;
+ else
+ err = fdcount;
+out:
+ unlock_kernel();
+ return err;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov