diff -urN linux.10/fs/select.c linux.10.diff/fs/select.c --- linux.10/fs/select.c Mon Sep 24 02:16:05 2001 +++ linux.10.diff/fs/select.c Sat Jan 19 03:43:27 2002 @@ -12,6 +12,8 @@ * 24 January 2000 * Changed sys_poll()/do_poll() to use PAGE_SIZE chunk-based allocation * of fds to overcome nfds < 16390 descriptors limit (Tigran Aivazian). + * June 2001 + * Added async_poll implementation. -bcrl */ #include @@ -19,6 +21,8 @@ #include #include /* for STICKY_TIMEOUTS */ #include +#include +#include #include @@ -26,9 +30,10 @@ #define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM) struct poll_table_entry { - struct file * filp; wait_queue_t wait; wait_queue_head_t * wait_address; + struct file * filp; + poll_table * p; }; struct poll_table_page { @@ -40,6 +45,8 @@ #define POLL_TABLE_FULL(table) \ ((unsigned long)((table)->entry+1) > PAGE_SIZE + (unsigned long)(table)) +static kmem_cache_t *poll_table_cache; + /* * Ok, Peter made a complicated, but straightforward multiple_wait() function. * I have rewritten this, taking some shortcuts: This code may not be easy to @@ -72,6 +79,72 @@ } } +void async_poll_complete(void *data) +{ + poll_table *p = data, *pwait; + struct kiocb *iocb = p->iocb; + unsigned int mask; + + pwait = p; + p->wake = 0; + wmb(); + do { + mask = iocb->filp->f_op->poll(iocb->filp, p); + mask &= p->events | POLLERR | POLLHUP; + if (mask) { + poll_freewait(p); + aio_complete(iocb, mask, 0); + return; + } + p->sync = 0; + wmb(); + } while (p->wake); + +} + +static void async_poll_waiter(wait_queue_t *wait) +{ + struct poll_table_entry *entry = (struct poll_table_entry *)wait; + poll_table *p = entry->p; + + /* avoid writes to the cacheline if possible for SMP */ + if (!p->wake) { + p->wake = 1; + /* ensure only one wake up queues the wtd */ + if (!p->sync && !test_and_set_bit(0, &p->sync)) + wtd_queue(&p->wtd); + } +} + +int async_poll(struct kiocb *iocb, int events) +{ + unsigned int mask; + poll_table *p, *pwait; + + p = kmem_cache_alloc(poll_table_cache, SLAB_KERNEL); + if (!p) + return -ENOMEM; + + poll_initwait(p); + wtd_set_action(&p->wtd, async_poll_complete, p); + p->iocb = iocb; + p->wake = 0; + p->sync = 0; + p->events = events; + pwait = p; + + mask = DEFAULT_POLLMASK; + if (iocb->filp->f_op && iocb->filp->f_op->poll) + mask = iocb->filp->f_op->poll(iocb->filp, p); + mask &= events | POLLERR | POLLHUP; + if (mask) { + poll_freewait(p); + aio_complete(iocb, mask, 0); + } + + return 0; +} + void __pollwait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p) { struct poll_table_page *table = p->table; @@ -98,7 +171,11 @@ get_file(filp); entry->filp = filp; entry->wait_address = wait_address; - init_waitqueue_entry(&entry->wait, current); + entry->p = p; + if (p->iocb) + init_waitqueue_func_entry(&entry->wait, async_poll_waiter); + else + init_waitqueue_entry(&entry->wait, current); add_wait_queue(wait_address,&entry->wait); } } @@ -494,3 +571,14 @@ poll_freewait(&table); return err; } + +static int __init poll_init(void) +{ + poll_table_cache = kmem_cache_create("poll table", + sizeof(poll_table), 0, 0, NULL, NULL); + if (!poll_table_cache) + panic("unable to alloc poll_table_cache"); + return 0; +} + +module_init(poll_init); diff -urN linux.10/include/linux/poll.h linux.10.diff/include/linux/poll.h --- linux.10/include/linux/poll.h Mon Jan 14 21:10:19 2002 +++ linux.10.diff/include/linux/poll.h Sat Jan 19 03:42:18 2002 @@ -7,14 +7,25 @@ #include #include +#ifndef __LINUX__MM_H #include +#endif #include +#ifndef __LINUX__WORKTODO_H +#include +#endif struct poll_table_page; +struct kiocb; typedef struct poll_table_struct { - int error; - struct poll_table_page * table; + struct worktodo wtd; + int error; + struct poll_table_page *table; + struct kiocb *iocb; /* iocb for async poll */ + int events; /* event mask for async poll */ + int wake; + long sync; } poll_table; extern void __pollwait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p); @@ -29,8 +40,11 @@ { pt->error = 0; pt->table = NULL; + pt->iocb = NULL; } + extern void poll_freewait(poll_table* pt); +int async_poll(struct kiocb *iocb, int events); /*