diff -aur --exclude=CVS linux/Documentation/Configure.help 2.4.18-kpoll/Documentation/Configure.help
--- linux/Documentation/Configure.help	Mon Feb 25 20:37:51 2002
+++ 2.4.18-kpoll/Documentation/Configure.help	Tue Mar 26 16:54:11 2002
@@ -24382,6 +24382,13 @@
   information:  http://www.candelatech.com/~greear/vlan.html  If unsure,
   you can safely say 'N'.
 
+/dev/kpoll support
+CONFIG_KPOLL
+ '/dev/kpoll' is drived from '/dev/epoll' but is also handling pending
+  events like a regular poll or select. It does so in an efficent way
+  making /dev/kpoll a real alternative to poll or select. Create the
+  /dev/kpoll with "mknod /dev/kpoll c 10 152".
+
 #
 # A couple of things I keep forgetting:
 #   capitalize: AppleTalk, Ethernet, DOS, DMA, FAT, FTP, Internet,
diff -aur --exclude=CVS linux/Makefile 2.4.18-kpoll/Makefile
--- linux/Makefile	Mon Feb 25 20:37:52 2002
+++ 2.4.18-kpoll/Makefile	Tue Mar 26 16:54:10 2002
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 4
 SUBLEVEL = 18
-EXTRAVERSION =
+EXTRAVERSION = kpoll
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
diff -aur --exclude=CVS linux/drivers/char/Config.in 2.4.18-kpoll/drivers/char/Config.in
--- linux/drivers/char/Config.in	Mon Feb 25 20:37:57 2002
+++ 2.4.18-kpoll/drivers/char/Config.in	Tue Mar 26 16:54:11 2002
@@ -185,6 +185,7 @@
 
 dep_tristate 'Intel i8x0 Random Number Generator support' CONFIG_INTEL_RNG $CONFIG_PCI
 tristate '/dev/nvram support' CONFIG_NVRAM
+tristate '/dev/kpoll - Efficient replacement to poll' CONFIG_KPOLL
 tristate 'Enhanced Real Time Clock Support' CONFIG_RTC
 if [ "$CONFIG_IA64" = "y" ]; then
    bool 'EFI Real Time Clock Services' CONFIG_EFI_RTC
diff -aur --exclude=CVS linux/drivers/char/Makefile 2.4.18-kpoll/drivers/char/Makefile
--- linux/drivers/char/Makefile	Mon Feb 25 20:37:57 2002
+++ 2.4.18-kpoll/drivers/char/Makefile	Tue Mar 26 16:54:11 2002
@@ -192,6 +192,7 @@
 ifeq ($(CONFIG_PPC),)
   obj-$(CONFIG_NVRAM) += nvram.o
 endif
+obj-$(CONFIG_KPOLL) += kpoll.o
 obj-$(CONFIG_TOSHIBA) += toshiba.o
 obj-$(CONFIG_I8K) += i8k.o
 obj-$(CONFIG_DS1620) += ds1620.o
diff -aur --exclude=CVS linux/drivers/char/kpoll.c 2.4.18-kpoll/drivers/char/kpoll.c
--- linux/drivers/char/kpoll.c	Sat Jul 13 13:00:11 2002
+++ 2.4.18-kpoll/drivers/char/kpoll.c	Sat Mar 30 07:54:23 2002
@@ -0,0 +1,1064 @@
+/*
+ *  kpoll.c - driver for /dev/kpoll
+ *
+ *  --- Notices from eventpoll.c, upon which this driver is based ---
+ *
+ *  Copyright (C) 2001, Davide Libenzi <davidel@xmailserver.org>
+ *
+ *  Efficent event polling implementation
+ *
+ *  --- End of notice from eventpoll.c ---
+ *
+ *  eventpoll was changed to handle pending events, making kpoll
+ *  simpler to use when replacing poll()
+ *
+ *  also added the possiblity to update new file descriptors from
+ *  other threads and waking the poll when the updated file descriptor
+ *  had pending events.
+ *
+ *  Tony Rogvall <tony@rogvall.com>
+ *  Per Bergqvist <per@synap.se>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/random.h>
+#include <linux/smp_lock.h>
+#include <linux/wrapper.h>
+#include <linux/string.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/fcblist.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+
+#include <linux/kpoll.h>
+
+
+#define DEBUG	0
+#ifdef DEBUG
+#define DPRINTK(x)	printk x
+#define DNPRINTK(n,x)	if (n <= DEBUG) printk x
+#else
+#define DPRINTK(x)
+#define DNPRINTK(n,x)
+#endif
+
+#define DEBUG_DPI	0
+
+#if DEBUG_DPI
+#define DPI_SLAB_DEBUG	(SLAB_DEBUG_FREE | SLAB_RED_ZONE /* | SLAB_POISON */)
+#else
+#define DPI_SLAB_DEBUG	0
+#endif
+
+#define INITIAL_HASH_BITS	7
+#define MAX_HASH_BITS	18
+#define RESIZE_LENGTH	2
+
+#define dpi_mem_alloc()	(struct kpitem *) kmem_cache_alloc(dpi_cache, SLAB_KERNEL)
+#define dpi_mem_free(p) kmem_cache_free(dpi_cache, p)
+
+#define EVENT_PAGE_INDEX(n)	((n) / KPOLLFD_X_PAGE)
+#define EVENT_PAGE_REM(n)	((n) % KPOLLFD_X_PAGE)
+#define EVENT_PAGE_OFFSET(n)	(((n) % KPOLLFD_X_PAGE) * sizeof(struct pollfd))
+
+#define PAGE_FD(pages, ix) \
+(struct pollfd *) ((pages)[EVENT_PAGE_INDEX((ix))]+EVENT_PAGE_OFFSET((ix)))
+
+#define MAX_EVT(kp) ((kp)->numpages * KPOLLFD_X_PAGE)
+
+typedef unsigned long long event_version_t;
+
+struct kpoll {
+    rwlock_t lock;
+    wait_queue_head_t wq;
+    wait_queue_head_t poll_wait;
+    struct list_head *hash;
+    unsigned int hbits;
+    unsigned int hmask;
+    atomic_t hents;
+    atomic_t resize;
+    int numpages;
+    char **pages;
+    char *pages0[MAX_KPOLL_PAGES];
+    char *pages1[MAX_KPOLL_PAGES];
+    atomic_t mmapped;
+    int eventcnt0;
+    int eventcnt;
+    int eventpoll;
+    event_version_t ver;
+    atomic_t shutdown;
+};
+
+struct kpitem {
+    struct list_head llink;
+    struct kpoll *kp;
+    struct pollfd pfd;
+    int index;
+    event_version_t ver;
+};
+
+
+static int kp_alloc_pages(char **pages, int numpages);
+static int kp_free_pages(char **pages, int numpages);
+static int kp_init(struct kpoll *kp);
+static void kp_free(struct kpoll *kp);
+static inline struct kpitem *kp_find_nl(struct kpoll *kp, int fd);
+static struct kpitem *kp_find(struct kpoll *kp, int fd);
+static int kp_hashresize(struct kpoll *kp, unsigned long *kflags);
+static int kp_insert(struct kpoll *kp, struct pollfd *pfd, struct file* file);
+static int kp_remove(struct kpoll *kp, struct kpitem *dpi, struct file* file);
+static int kp_update(struct kpoll *kp, struct kpitem *dpi, 
+		     struct pollfd *pfd, struct file* file);
+static void notify_proc(struct file *file, void *data, unsigned long *local, long *event);
+static int open_kpoll(struct inode *inode, struct file *file);
+static int close_kpoll(struct inode *inode, struct file *file);
+static unsigned int poll_kpoll(struct file *file, poll_table *wait);
+static int write_kpoll(struct file *file, const char *buffer, size_t count,
+		       loff_t *ppos);
+static int kp_poll(struct kpoll *kp, void *arg);
+static int ioctl_kpoll(struct inode *inode, struct file *file,
+		       unsigned int cmd, unsigned long arg);
+static void kpoll_mm_open(struct vm_area_struct * vma);
+static void kpoll_mm_close(struct vm_area_struct * vma);
+static int mmap_kpoll(struct file *file, struct vm_area_struct *vma);
+
+
+
+
+static kmem_cache_t *dpi_cache;
+
+static struct file_operations kpoll_fops = {
+ write: write_kpoll,
+ ioctl: ioctl_kpoll,
+ mmap: mmap_kpoll,
+ open: open_kpoll,
+ release: close_kpoll,
+ poll: poll_kpoll
+};
+
+static struct vm_operations_struct kpoll_mmap_ops = {
+ open: kpoll_mm_open,
+ close: kpoll_mm_close,
+};
+
+static struct miscdevice kpoll = {
+    KPOLL_MINOR, "kpoll", &kpoll_fops
+};
+
+
+
+
+static int kp_alloc_pages(char **pages, int numpages)
+{
+    int ii;
+
+    for (ii = 0; ii < numpages; ii++) {
+	pages[ii] = (char *) __get_free_pages(GFP_KERNEL, 0);
+	if (!pages[ii]) {
+	    for (--ii; ii >= 0; ii--) {
+		clear_bit(PG_reserved, &virt_to_page(pages[ii])->flags);
+		free_pages((unsigned long) pages[ii], 0);
+	    }
+	    return -ENOMEM;
+	}
+	set_bit(PG_reserved, &virt_to_page(pages[ii])->flags);
+    }
+    return 0;
+}
+
+
+static int kp_free_pages(char **pages, int numpages)
+{
+    int ii;
+
+    for (ii = 0; ii < numpages; ii++) {
+	clear_bit(PG_reserved, &virt_to_page(pages[ii])->flags);
+	free_pages((unsigned long) pages[ii], 0);
+    }
+    return 0;
+}
+
+
+static int kp_init(struct kpoll *kp)
+{
+    int ii, hentries;
+
+    rwlock_init(&kp->lock);
+    init_waitqueue_head(&kp->wq);
+    init_waitqueue_head(&kp->poll_wait);
+    kp->hbits = INITIAL_HASH_BITS;
+    kp->hmask = (1 << kp->hbits) - 1;
+    atomic_set(&kp->hents, 0);
+    atomic_set(&kp->resize, 0);
+    atomic_set(&kp->mmapped, 0);
+    kp->numpages = 0;
+    kp->pages = kp->pages0;
+    kp->eventcnt = 0;
+    kp->eventcnt0 = 0;
+    kp->ver = 1;
+    kp->eventpoll = 0;
+    atomic_set(&kp->shutdown, 0);
+
+    hentries = kp->hmask + 1;
+    if (!(kp->hash = (struct list_head *) vmalloc(hentries * sizeof(struct list_head))))
+	return -ENOMEM;
+
+    for (ii = 0; ii < hentries; ii++)
+	INIT_LIST_HEAD(&kp->hash[ii]);
+
+    return 0;
+}
+
+
+static void kp_free(struct kpoll *kp)
+{
+    int ii;
+    struct list_head *lnk;
+    struct file *file;
+
+    lock_kernel();
+    for (ii = 0; ii <= kp->hmask; ii++) {
+	while ((lnk = list_first(&kp->hash[ii]))) {
+	    struct kpitem *dpi = list_entry(lnk, struct kpitem, llink);
+
+	    if (current->files && (file = fcheck(dpi->pfd.fd)))
+		file_notify_delcb(file, notify_proc);
+	    list_del(lnk);
+	    dpi_mem_free(dpi);
+	}
+    }
+    vfree(kp->hash);
+    if (kp->numpages > 0) {
+	kp_free_pages(kp->pages0, kp->numpages);
+	kp_free_pages(kp->pages1, kp->numpages);
+    }
+    unlock_kernel();
+}
+
+
+static inline struct kpitem *kp_find_nl(struct kpoll *kp, int fd)
+{
+    struct kpitem *dpi = NULL;
+    struct list_head *lsthead, *lnk;
+
+    lsthead = &kp->hash[fd & kp->hmask];
+    list_for_each(lnk, lsthead) {
+	dpi = list_entry(lnk, struct kpitem, llink);
+
+	if (dpi->pfd.fd == fd) break;
+	dpi = NULL;
+    }
+
+    DNPRINTK(3, (KERN_INFO "[%p] /dev/kpoll: kp_find(%d) -> %p\n", current, fd, dpi));
+
+    return dpi;
+}
+
+
+static struct kpitem *kp_find(struct kpoll *kp, int fd)
+{
+    struct kpitem *dpi;
+    unsigned long flags;
+
+    read_lock_irqsave(&kp->lock, flags);
+
+    dpi = kp_find_nl(kp, fd);
+
+    read_unlock_irqrestore(&kp->lock, flags);
+
+    return dpi;
+}
+
+/* remove and item from current set (FIXME: inline) */
+static void kp_remove_nl(struct kpoll *kp, struct kpitem *dpi)
+{
+    if ((dpi->ver == kp->ver) && 
+	(dpi->index >= 0) && (dpi->index < kp->eventcnt)) {
+	struct pollfd *pfd = PAGE_FD(kp->pages, dpi->index);
+	if (dpi->index < --kp->eventcnt) {
+	    struct kpitem *ldpi;
+	    *pfd = *PAGE_FD(kp->pages, kp->eventcnt);
+	    if ((ldpi = kp_find_nl(kp, pfd->fd)))
+		ldpi->index = dpi->index;
+	}
+	dpi->index = -1;
+	dpi->ver = kp->ver - 1;
+    }
+}
+
+static int kp_hashresize(struct kpoll *kp, unsigned long *kflags)
+{
+    struct list_head *hash, *oldhash;
+    unsigned int hbits = kp->hbits + 1;
+    unsigned int hmask = (1 << hbits) - 1;
+    int ii, res, hentries = hmask + 1;
+    unsigned long flags = *kflags;
+
+    DNPRINTK(3, (KERN_INFO "[%p] /dev/kpoll: kp_hashresize(%p) bits=%u\n", current, kp, hbits));
+
+    write_unlock_irqrestore(&kp->lock, flags);
+
+    res = -ENOMEM;
+    if (!(hash = (struct list_head *) vmalloc(hentries * sizeof(struct list_head)))) {
+	write_lock_irqsave(&kp->lock, flags);
+	goto out;
+    }
+
+    for (ii = 0; ii < hentries; ii++)
+	INIT_LIST_HEAD(&hash[ii]);
+
+    write_lock_irqsave(&kp->lock, flags);
+
+    oldhash = kp->hash;
+    for (ii = 0; ii <= kp->hmask; ii++) {
+	struct list_head *oldhead = &oldhash[ii], *lnk;
+
+	while ((lnk = list_first(oldhead))) {
+	    struct kpitem *dpi = list_entry(lnk, struct kpitem, llink);
+
+	    list_del(lnk);
+	    list_add(lnk, &hash[dpi->pfd.fd & hmask]);
+	}
+    }
+
+    kp->hash = hash;
+    kp->hbits = hbits;
+    kp->hmask = hmask;
+
+    write_unlock_irqrestore(&kp->lock, flags);
+    vfree(oldhash);
+    write_lock_irqsave(&kp->lock, flags);
+
+    res = 0;
+ out:
+    *kflags = flags;
+    atomic_dec(&kp->resize);
+    return res;
+}
+
+/*
+ * When updating an existing poll item, we need to check current status
+ * 1. status may have changed
+ *    a) trigged event was OUT new event is IN and IN was ready
+ *       then just update the revents to reflect that
+ *    b) trigged event was OUT and new event is IN and there is no IN
+ *       condtion, then the slot is removed and eventcount is updated
+ *
+ * 2. status is new
+ *    a) no revents but poll return IN/OUT then insert a slot
+ *       and update revents
+ *
+ * returns:
+ *      0  ok but nothing where added 
+ *      1  ok and we added the fd, had and event by polling
+ *      <0 error
+ *
+ */
+
+static int kp_update(struct kpoll *kp, struct kpitem *dpi, 
+		     struct pollfd *pfd, struct file* file)
+{
+    unsigned long flags;
+    int res = 0;
+
+    write_lock_irqsave(&kp->lock, flags);
+    
+    dpi->pfd.events = pfd->events;
+    dpi->pfd.revents = 0;
+
+    if (!kp->eventpoll && file->f_op && file->f_op->poll) {
+	unsigned int mask;
+	struct pollfd *dpfd;
+
+	mask = file->f_op->poll(file,NULL);
+	mask &= (dpi->pfd.events | POLLERR | POLLHUP);
+	dpi->pfd.revents = mask;
+
+	if (mask) {		/* update or insert event */
+	    if (dpi->ver == kp->ver) {
+		if ((dpi->index >= 0) && (dpi->index < kp->eventcnt)) {
+		    dpfd = PAGE_FD(kp->pages, dpi->index);
+		    *dpfd = dpi->pfd;
+		}
+	    } else if (kp->eventcnt < MAX_EVT(kp)) {
+		dpi->index = kp->eventcnt++;
+		dpi->ver = kp->ver;
+		dpfd = PAGE_FD(kp->pages, dpi->index);
+		*dpfd = dpi->pfd;
+		res = 1;
+	    }
+	} else {
+	    kp_remove_nl(kp, dpi);
+	}
+    }
+    
+    write_unlock_irqrestore(&kp->lock, flags);
+
+    return res;
+}
+
+/*
+ * Insert a new file descriptor.
+ *
+ * returns:
+ *      0  ok inserted but no pending events
+ *      1  ok inserted and had pending events
+ *      <0 error
+ */
+
+static int kp_insert(struct kpoll *kp, struct pollfd *pfd, struct file* file)
+{
+    struct kpitem *dpi;
+    unsigned long flags;
+    int res = 0;
+
+    if (atomic_read(&kp->hents) >= MAX_EVT(kp))
+	return -E2BIG;
+
+    if (!(dpi = dpi_mem_alloc()))
+	return -ENOMEM;
+
+    INIT_LIST_HEAD(&dpi->llink);
+    dpi->kp = kp;
+    dpi->pfd = *pfd;
+    dpi->index = -1;
+    dpi->ver = kp->ver - 1;
+
+    write_lock_irqsave(&kp->lock, flags);
+
+    list_add(&dpi->llink, &kp->hash[pfd->fd & kp->hmask]);
+    atomic_inc(&kp->hents);
+
+    if (!atomic_read(&kp->resize) &&
+	(atomic_read(&kp->hents) >> kp->hbits) > RESIZE_LENGTH &&
+	kp->hbits < MAX_HASH_BITS) {
+	atomic_inc(&kp->resize);
+	kp_hashresize(kp, &flags);
+    }
+
+    write_unlock_irqrestore(&kp->lock, flags);
+
+    file_notify_addcb(file, notify_proc, dpi);
+
+    if (!kp->eventpoll) {
+	write_lock_irqsave(&kp->lock, flags);
+
+	/* check for pending events */
+	if ( file->f_op && file->f_op->poll ) {
+	    unsigned int mask;
+	    mask = file->f_op->poll(file,NULL);
+	    mask &= (dpi->pfd.events | POLLERR | POLLHUP );
+	    if ( mask ) {
+		dpi->pfd.revents = mask;
+		if (kp->eventcnt < MAX_EVT(kp)) {
+		    dpi->index = kp->eventcnt++;
+		    dpi->ver = kp->ver;
+		    pfd = PAGE_FD(kp->pages, dpi->index);
+		    *pfd = dpi->pfd;
+		    res = 1;
+		}
+	    }
+	}
+	write_unlock_irqrestore(&kp->lock, flags);
+    }
+
+    DNPRINTK(3, (KERN_INFO "[%p] /dev/kpoll: kp_insert(%p, %d)\n", current, kp, pfd->fd));
+    return res;
+}
+
+/*
+ * Remove a file descriptor.
+ *
+ * returns:
+ *      0  ok
+ *      <0 error
+ */
+
+static int kp_remove(struct kpoll *kp, struct kpitem *dpi, struct file* file)
+{
+    int fd = dpi->pfd.fd;
+    unsigned long flags;
+
+    if (file != NULL)
+	file_notify_delcb(file, notify_proc);
+
+    write_lock_irqsave(&kp->lock, flags);
+
+    list_del(&dpi->llink);
+    atomic_dec(&kp->hents);
+
+    kp_remove_nl(kp, dpi);
+
+    write_unlock_irqrestore(&kp->lock, flags);
+
+    dpi_mem_free(dpi);
+
+    DNPRINTK(3, (KERN_INFO "[%p] /dev/kpoll: kp_remove(%p, %d)\n", 
+		 current, kp, fd));
+    return 0;
+}
+
+static void notify_proc(struct file *file, void *data, unsigned long *local, long *event)
+{
+    struct kpitem *dpi = (struct kpitem *) data;
+    struct kpoll *kp = dpi->kp;
+    struct pollfd *pfd;
+
+    DNPRINTK(3, (KERN_INFO "[%p] /dev/kpoll: notify(%p, %p, %08lx, %08lx) kp=%p\n",
+		 current, file, data, event[0], event[1], kp));
+
+    if (atomic_read(&kp->shutdown)) return;
+    
+    write_lock(&kp->lock);
+
+    if (event[1] & POLLREMOVE) {
+	/* file is about to get closed */
+        if ( current->files )
+	   file_notify_delcb(file, notify_proc);
+	list_del(&dpi->llink);
+	atomic_dec(&kp->hents);
+	
+	kp_remove_nl(kp, dpi);
+
+	write_unlock(&kp->lock);
+	dpi_mem_free(dpi);
+	return;
+    }
+    else if ((dpi->pfd.events | POLLERR | POLLHUP) & event[1]) {
+	if ((dpi->index < 0) || (dpi->ver != kp->ver)) {
+	    if (kp->eventcnt >= MAX_EVT(kp))
+		goto out;
+	    dpi->index = kp->eventcnt++;
+	    dpi->ver = kp->ver;
+	    pfd = PAGE_FD(kp->pages, dpi->index);
+	    *pfd = dpi->pfd;
+	} else {
+	    /* FIXME: when do we need this? */
+	    pfd = PAGE_FD(kp->pages, dpi->index);
+	    if (pfd->fd != dpi->pfd.fd) {
+		if (kp->eventcnt >= MAX_EVT(kp))
+		    goto out;
+		dpi->index = kp->eventcnt++;
+		pfd = PAGE_FD(kp->pages, dpi->index);
+		*pfd = dpi->pfd;
+	    }
+	    dpi->ver = kp->ver;
+	}
+	pfd->revents |= ((pfd->events | POLLERR | POLLHUP) & event[1]);
+
+	if (waitqueue_active(&kp->wq))
+	    wake_up(&kp->wq);
+	if (waitqueue_active(&kp->poll_wait))
+	    wake_up(&kp->poll_wait);
+    }
+
+ out:
+    write_unlock(&kp->lock);
+}
+
+
+static int open_kpoll(struct inode *inode, struct file *file)
+{
+    int res;
+    struct kpoll *kp;
+
+    if (!(kp = kmalloc(sizeof(struct kpoll), GFP_KERNEL)))
+	return -ENOMEM;
+
+    memset(kp, 0, sizeof(*kp));
+    if ((res = kp_init(kp))) {
+	kfree(kp);
+	return res;
+    }
+
+    file->private_data = kp;
+
+    MOD_INC_USE_COUNT;
+
+    DNPRINTK(3, (KERN_INFO "[%p] /dev/kpoll: open() kp=%p\n", current, kp));
+    return 0;
+}
+
+
+static int close_kpoll(struct inode *inode, struct file *file)
+{
+    struct kpoll *kp = file->private_data;
+
+    atomic_set(&kp->shutdown, 1);
+
+    kp_free(kp);
+
+    kfree(kp);
+
+    MOD_DEC_USE_COUNT;
+
+    DNPRINTK(3, (KERN_INFO "[%p] /dev/kpoll: close() kp=%p\n", current, kp));
+    return 0;
+}
+
+
+static int write_kpoll(struct file *file, const char *buffer, size_t count,
+		       loff_t *ppos)
+{
+    int res, rcount, ready;
+    struct kpoll *kp = file->private_data;
+
+    DNPRINTK(3, (KERN_INFO "[%p] /dev/kpoll: write(%p, %d)\n", current, kp, count));
+
+    if (count % sizeof(struct pollfd))
+	return -EINVAL;
+
+    if ((res = verify_area(VERIFY_READ, buffer, count)))
+	return res;
+
+    rcount = 0;
+    ready  = 0;
+
+    lock_kernel(); /* Is this needed when we use fget (I do not think so) */
+
+    while (count > 0) {
+	struct pollfd pfd;
+
+	__copy_from_user(&pfd, buffer, sizeof(pfd));
+
+	if (pfd.fd >= 0) {
+	    struct kpitem *dpi = kp_find(kp, pfd.fd);
+	    struct file *file  = fget(pfd.fd);
+
+	    if (file == NULL)
+		pfd.events = POLLREMOVE;
+	    if (pfd.events & POLLREMOVE) {
+		if (dpi) {
+		    kp_remove(kp, dpi, file);
+		    rcount += sizeof(pfd);
+		}
+	    } else if (dpi) {
+		if ((res = kp_update(kp, dpi, &pfd, file)) >= 0) {
+		    rcount += sizeof(pfd);
+		    if (res > 0)
+			ready = 1;
+		}
+	    } else {
+		pfd.revents = 0;
+		if ((res = kp_insert(kp, &pfd, file)) >= 0) {
+		    rcount += sizeof(pfd);
+		    if (res > 0)
+			ready = 1;
+		}
+	    }
+	    if (file != NULL)
+		fput(file);
+	}
+	buffer += sizeof(pfd);
+	count -= sizeof(pfd);
+    }
+
+    unlock_kernel();
+	
+    if (ready) {
+	unsigned long flags;
+
+	write_lock_irqsave(&kp->lock, flags);
+	if (kp->eventcnt) {
+	    if (waitqueue_active(&kp->wq))
+		wake_up(&kp->wq);
+	    if (waitqueue_active(&kp->poll_wait))
+		wake_up(&kp->poll_wait);	    
+	}
+	write_unlock_irqrestore(&kp->lock, flags);
+    }
+    return rcount;
+}
+
+/*
+ * check the slots that had events in previous KP_POLL, need to
+ * re-poll them if they have not trigged already. 
+ * Since there may be an input ready, but nothing to trigg that!!
+ * scan_only = 0 moves trigged fd to current version
+ * scan_only = 1 returns realy ready fd count
+ * scan_only = 2 return 0|1 if there are ready fds
+ */
+static int kp_scan_previous(struct kpoll *kp, int scan_only)
+{
+    int i;
+    int readycnt = 0;
+    char** pages = (kp->pages == kp->pages0) ? kp->pages1 : kp->pages0;
+
+    for (i = 0; i < kp->eventcnt0; i++) {
+	struct pollfd* pfd = PAGE_FD(pages, i);
+	struct kpitem *dpi = kp_find_nl(kp, pfd->fd);
+	if ((dpi != NULL) && 
+	    (dpi->ver != kp->ver) &&
+	    (dpi->pfd.events & ~(POLLHUP|POLLERR))) {
+	    struct file *file = fget(pfd->fd);
+
+	    if (file != NULL) {
+		struct pollfd *dpfd;
+		unsigned int mask = 0;
+
+		if (file->f_op && file->f_op->poll)
+		    mask = file->f_op->poll(file,NULL);
+		fput(file);
+		mask &= (dpi->pfd.events | POLLERR | POLLHUP);
+
+		if (!scan_only)
+		    dpi->pfd.revents = mask;
+		if (mask) {
+		    readycnt++;
+		    if (scan_only == 2) break;
+		    if ((!scan_only) && (kp->eventcnt < MAX_EVT(kp))) {
+			dpi->index = kp->eventcnt++;
+			dpi->ver = kp->ver;
+			dpfd = PAGE_FD(kp->pages, dpi->index);
+			*dpfd = dpi->pfd;
+		    }
+		}
+	    }
+	}
+    }
+    if ( (!scan_only) || (!readycnt) )
+	kp->eventcnt0 = 0;
+    return readycnt;
+}
+
+static void kp_scan_current(struct kpoll *kp, int max_index)
+{
+    int i;
+
+    /* poll the fds we haven't scan in this run */
+    for(i = 0; i < max_index; i++) {
+	struct pollfd* pfd = PAGE_FD(kp->pages, i);
+	struct kpitem *dpi = kp_find_nl(kp, pfd->fd);
+	if ((dpi != NULL) &&
+	    (dpi->pfd.events & ~(POLLHUP|POLLERR))) {
+	    struct file *file = fget(pfd->fd);
+      
+	    if (file != NULL) {
+		unsigned int mask = 0;
+
+		if (file->f_op && file->f_op->poll)
+		    mask = file->f_op->poll(file,NULL);
+		fput(file);
+		mask &= (dpi->pfd.events|POLLERR|POLLHUP);
+		
+		dpi->pfd.revents = mask;
+		if (mask) {
+		    *pfd = dpi->pfd;
+		} else {
+		    kp_remove_nl(kp, dpi);
+		}
+	    }
+	}
+    }
+}
+
+
+static unsigned int poll_kpoll(struct file *file, poll_table *wait)
+{
+    unsigned long flags;
+    struct kpoll *kp = file->private_data;
+
+    write_lock_irqsave(&kp->lock, flags);
+
+    if (kp_scan_previous(kp,2)) {
+	write_unlock_irqrestore(&kp->lock, flags);
+	return POLLIN | POLLRDNORM;
+    }
+    write_unlock_irqrestore(&kp->lock, flags);
+
+    poll_wait(file, &kp->poll_wait, wait);
+    if (kp->eventcnt)
+	return POLLIN | POLLRDNORM;
+
+    return 0;
+}
+
+
+static int kp_poll(struct kpoll *kp, void *arg)
+{
+    int res = 0;
+    long timeout;
+    unsigned long flags;
+    struct k_poll dvp;
+    wait_queue_t wait;
+
+    if (copy_from_user(&dvp, arg, sizeof(struct k_poll)))
+	return -EFAULT;
+
+    if (!atomic_read(&kp->mmapped))
+	return -EINVAL;
+
+    DNPRINTK(3, (KERN_INFO "[%p] /dev/kpoll: ioctl(%p, KP_POLL, %d)\n", current, kp, dvp.kp_timeout));
+
+    write_lock_irqsave(&kp->lock, flags);
+
+    if (!kp->eventpoll) {
+        kp_scan_current(kp,kp->eventcnt);
+	kp_scan_previous(kp,0);
+    }
+
+    res = 0;
+    if (!kp->eventcnt) {
+	init_waitqueue_entry(&wait, current);
+	add_wait_queue(&kp->wq, &wait);
+	if ((timeout = dvp.kp_timeout) != 0) {
+	    /* from sys_poll */
+	    if ((unsigned long) timeout < MAX_SCHEDULE_TIMEOUT / HZ)
+		timeout = (unsigned long)(timeout*HZ+999)/1000+1;
+	    else /* Negative or overflow */
+		timeout = MAX_SCHEDULE_TIMEOUT;
+	}
+
+	for (;;) {
+	    if (kp->eventcnt || !timeout)
+		break;
+	    if (signal_pending(current)) {
+		res = -EINTR;
+		break;
+	    }
+
+	    set_current_state(TASK_INTERRUPTIBLE);
+
+	    write_unlock_irqrestore(&kp->lock, flags);
+	    timeout = schedule_timeout(timeout);
+	    write_lock_irqsave(&kp->lock, flags);
+	}
+	remove_wait_queue(&kp->wq, &wait);
+
+	set_current_state(TASK_RUNNING);
+    }
+
+    if (!res && kp->eventcnt) {
+	kp->eventcnt0 = kp->eventcnt;
+	res = kp->eventcnt;
+	kp->eventcnt = 0;
+	++kp->ver;
+	if (kp->pages == kp->pages0) {
+	    kp->pages = kp->pages1;
+	    dvp.kp_resoff = 0;
+	} else {
+	    kp->pages = kp->pages0;
+	    dvp.kp_resoff = kp->numpages * PAGE_SIZE;
+	}
+    }
+
+    write_unlock_irqrestore(&kp->lock, flags);
+
+    if (res > 0)
+	copy_to_user(arg, &dvp, sizeof(struct k_poll));
+
+    DNPRINTK(3, (KERN_INFO "[%p] /dev/kpoll: ioctl(%p, KP_POLL, %d) == %d\n", current, kp, dvp.kp_timeout, res));
+    return res;
+}
+
+
+static int ioctl_kpoll(struct inode *inode, struct file *file,
+		       unsigned int cmd, unsigned long arg)
+{
+    int res, numpages;
+    struct kpoll *kp = file->private_data;
+    struct kpitem *dpi;
+    unsigned long flags;
+    struct pollfd pfd;
+
+    switch (cmd) {
+    case KP_ALLOC:
+	if (atomic_read(&kp->mmapped))
+	    return -EBUSY;
+
+	numpages = KP_FDS_PAGES(arg);
+	if (numpages > MAX_KPOLL_PAGES)
+	    return -EINVAL;
+
+	res = 0;
+	write_lock_irqsave(&kp->lock, flags);
+	if (numpages > kp->numpages) {
+	    if (!(res = kp_alloc_pages(&kp->pages0[kp->numpages], numpages - kp->numpages))) {
+		if (!(res = kp_alloc_pages(&kp->pages1[kp->numpages], numpages - kp->numpages))) {
+		    kp->numpages = numpages;
+		} else {
+		    kp_free_pages(&kp->pages0[kp->numpages], numpages - kp->numpages);
+		}
+	    }
+	}
+	write_unlock_irqrestore(&kp->lock, flags);
+
+	DNPRINTK(3, (KERN_INFO "[%p] /dev/kpoll: ioctl(%p, KP_ALLOC, %lu) == %d\n",
+		     current, kp, arg, res));
+	return res;
+
+    case KP_FREE:
+	if (atomic_read(&kp->mmapped))
+	    return -EBUSY;
+
+	res = -EINVAL;
+	write_lock_irqsave(&kp->lock, flags);
+	if (kp->numpages > 0) {
+	    kp_free_pages(kp->pages0, kp->numpages);
+	    kp_free_pages(kp->pages1, kp->numpages);
+	    kp->numpages = 0;
+	    kp->pages = kp->pages0;
+	    res = 0;
+	}
+	write_unlock_irqrestore(&kp->lock, flags);
+
+	DNPRINTK(3, (KERN_INFO "[%p] /dev/kpoll: ioctl(%p, KP_FREE) == %d\n",
+		     current, kp, res));
+	return res;
+
+    case KP_POLL:
+	return kp_poll(kp, (void *) arg);
+
+    case KP_EVENTPOLL:
+	kp->eventpoll = (int) arg;
+	return 0;
+	
+
+    case KP_ISPOLLED:
+	if (copy_from_user(&pfd, (void *) arg, sizeof(struct pollfd)))
+	    return 0;
+
+	read_lock_irqsave(&kp->lock, flags);
+
+	res = 0;
+	if (!(dpi = kp_find_nl(kp, pfd.fd)))
+	    goto out_ispolled;
+
+	pfd = dpi->pfd;
+	res = 1;
+
+    out_ispolled:
+	read_unlock_irqrestore(&kp->lock, flags);
+
+	if (res)
+	    copy_to_user((void *) arg, &pfd, sizeof(struct pollfd));
+
+	DNPRINTK(3, (KERN_INFO "[%p] /dev/kpoll: ioctl(%p, KP_ISPOLLED, %d) == %d\n",
+		     current, kp, pfd.fd, res));
+	return res;
+    }
+
+    return -EINVAL;
+}
+
+
+static void kpoll_mm_open(struct vm_area_struct * vma)
+{
+    struct file *file = vma->vm_file;
+    struct kpoll *kp = file->private_data;
+
+    if (kp) atomic_inc(&kp->mmapped);
+
+    DNPRINTK(3, (KERN_INFO "[%p] /dev/kpoll: mm_open(%p)\n", current, kp));
+}
+
+
+static void kpoll_mm_close(struct vm_area_struct * vma)
+{
+    struct file *file = vma->vm_file;
+    struct kpoll *kp = file->private_data;
+
+    if (kp) atomic_dec(&kp->mmapped);
+
+    DNPRINTK(3, (KERN_INFO "[%p] /dev/kpoll: mm_close(%p)\n", current, kp));
+}
+
+
+static int mmap_kpoll(struct file *file, struct vm_area_struct *vma)
+{
+    struct kpoll *kp = file->private_data;
+    unsigned long start, flags;
+    int ii, res;
+    int numpages;
+    size_t mapsize;
+
+    DNPRINTK(3, (KERN_INFO "[%p] /dev/kpoll: mmap(%p, %lx, %lx)\n",
+		 current, kp, vma->vm_start, vma->vm_pgoff << PAGE_SHIFT));
+
+    if ((vma->vm_pgoff << PAGE_SHIFT) != 0)
+	return -EINVAL;
+
+    mapsize = PAGE_ALIGN(vma->vm_end - vma->vm_start);
+    numpages = mapsize >> PAGE_SHIFT;
+
+    write_lock_irqsave(&kp->lock, flags);
+
+    res = -EINVAL;
+    if (numpages != (2 * kp->numpages))
+	goto out;
+
+    start = vma->vm_start;
+    for (ii = 0; ii < kp->numpages; ii++) {
+	if (remap_page_range(start, __pa(kp->pages0[ii]),
+			     PAGE_SIZE, vma->vm_page_prot))
+	    goto out;
+	start += PAGE_SIZE;
+    }
+    for (ii = 0; ii < kp->numpages; ii++) {
+	if (remap_page_range(start, __pa(kp->pages1[ii]),
+			     PAGE_SIZE, vma->vm_page_prot))
+	    goto out;
+	start += PAGE_SIZE;
+    }
+    vma->vm_ops = &kpoll_mmap_ops;
+    atomic_set(&kp->mmapped, 1);
+    res = 0;
+ out:
+    write_unlock_irqrestore(&kp->lock, flags);
+
+    DNPRINTK(3, (KERN_INFO "[%p] /dev/kpoll: mmap(%p, %lx, %lx) == %d\n",
+		 current, kp, vma->vm_start, vma->vm_pgoff << PAGE_SHIFT, res));
+    return res;
+}
+
+
+int __init kpoll_init(void)
+{
+    dpi_cache = kmem_cache_create("kpoll",
+				  sizeof(struct kpitem),
+				  __alignof__(struct kpitem),
+				  DPI_SLAB_DEBUG, NULL, NULL);
+    if (!dpi_cache) {
+	printk(KERN_INFO "[%p] /dev/kpoll: driver install failed.\n", current);
+	return -ENOMEM;
+    }
+
+    printk(KERN_INFO "[%p] /dev/kpoll: driver installed.\n", current);
+
+    misc_register(&kpoll);
+
+    return 0;
+}
+
+
+module_init(kpoll_init);
+
+#ifdef MODULE
+
+void cleanup_module(void)
+{
+    misc_deregister(&kpoll);
+    kmem_cache_destroy(dpi_cache);
+    printk(KERN_INFO "[%p] /dev/kpoll: driver cleaned up.\n", current);
+
+}
+
+#endif
diff -aur --exclude=CVS linux/fs/Makefile 2.4.18-kpoll/fs/Makefile
--- linux/fs/Makefile	Mon Feb 25 20:38:07 2002
+++ 2.4.18-kpoll/fs/Makefile	Tue Mar 26 16:54:11 2002
@@ -7,12 +7,12 @@
 
 O_TARGET := fs.o
 
-export-objs :=	filesystems.o open.o dcache.o buffer.o
+export-objs :=	filesystems.o open.o dcache.o buffer.o fcblist.o
 mod-subdirs :=	nls
 
 obj-y :=	open.o read_write.o devices.o file_table.o buffer.o \
 		super.o block_dev.o char_dev.o stat.o exec.o pipe.o namei.o \
-		fcntl.o ioctl.o readdir.o select.o fifo.o locks.o \
+		fcntl.o ioctl.o readdir.o select.o fifo.o locks.o  fcblist.o \
 		dcache.o inode.o attr.o bad_inode.o file.o iobuf.o dnotify.o \
 		filesystems.o namespace.o seq_file.o
 
diff -aur --exclude=CVS linux/fs/fcblist.c 2.4.18-kpoll/fs/fcblist.c
--- linux/fs/fcblist.c	Sat Jul 13 13:00:22 2002
+++ 2.4.18-kpoll/fs/fcblist.c	Sat Jul 13 07:34:41 2002
@@ -0,0 +1,126 @@
+/*
+ *  linux/fs/fcblist.c
+ *
+ *  Copyright (C) 2001, Davide Libenzi <davidel@xmailserver.org>
+ *
+ *  Handle file callbacks
+ */
+
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <asm/bitops.h>
+#include <linux/fcblist.h>
+
+
+long ion_band_table[NSIGPOLL] = {
+	ION_IN,		/* POLL_IN */
+	ION_OUT,	/* POLL_OUT */
+	ION_IN,		/* POLL_MSG */
+	ION_ERR,	/* POLL_ERR */
+	0,			/* POLL_PRI */
+	ION_HUP		/* POLL_HUP */
+};
+
+long poll_band_table[NSIGPOLL] = {
+	POLLIN | POLLRDNORM,			/* POLL_IN */
+	POLLOUT | POLLWRNORM | POLLWRBAND,	/* POLL_OUT */
+	POLLIN | POLLRDNORM | POLLMSG,		/* POLL_MSG */
+	POLLERR,				/* POLL_ERR */
+	POLLPRI | POLLRDBAND,			/* POLL_PRI */
+	POLLHUP | POLLERR			/* POLL_HUP */
+};
+
+#define list_first(head) (((head)->next != (head)) ? (head)->next: (struct list_head *) 0)
+
+
+void file_notify_event(struct file *filep, long *event)
+{
+	unsigned long flags;
+	struct list_head *lnk, *lsthead;
+
+	fcblist_read_lock(filep, flags);
+
+	lsthead = &filep->f_cblist;
+	list_for_each(lnk, lsthead) {
+		struct fcb_struct *fcbp = list_entry(lnk, struct fcb_struct, llink);
+
+		fcbp->cbproc(filep, fcbp->data, fcbp->local, event);
+	}
+
+	fcblist_read_unlock(filep, flags);
+}
+
+int file_notify_addcb(struct file *filep,
+		void (*cbproc)(struct file *, void *, unsigned long *, long *), void *data)
+{
+	unsigned long flags;
+	struct fcb_struct *fcbp;
+
+	if (!(fcbp = (struct fcb_struct *) kmalloc(sizeof(struct fcb_struct), GFP_KERNEL)))
+		return -ENOMEM;
+
+	memset(fcbp, 0, sizeof(struct fcb_struct));
+	fcbp->cbproc = cbproc;
+	fcbp->data = data;
+
+	fcblist_write_lock(filep, flags);
+	list_add_tail(&fcbp->llink, &filep->f_cblist);
+	fcblist_write_unlock(filep, flags);
+
+	return 0;
+}
+
+int file_notify_delcb(struct file *filep,
+		void (*cbproc)(struct file *, void *, unsigned long *, long *))
+{
+	unsigned long flags;
+	struct list_head *lnk, *lsthead;
+
+	fcblist_write_lock(filep, flags);
+
+	lsthead = &filep->f_cblist;
+	list_for_each(lnk, lsthead) {
+		struct fcb_struct *fcbp = list_entry(lnk, struct fcb_struct, llink);
+
+		if (fcbp->cbproc == cbproc) {
+			list_del(lnk);
+			fcblist_write_unlock(filep, flags);
+			kfree(fcbp);
+			return 0;
+		}
+	}
+
+	fcblist_write_unlock(filep, flags);
+
+	return -ENOENT;
+}
+
+void file_notify_cleanup(struct file *filep)
+{
+	unsigned long flags;
+	struct list_head *lnk, *lsthead;
+
+	file_send_notify(filep, 0, POLLREMOVE);
+
+	fcblist_write_lock(filep, flags);
+
+	lsthead = &filep->f_cblist;
+	while ((lnk = list_first(lsthead))) {
+		struct fcb_struct *fcbp = list_entry(lnk, struct fcb_struct, llink);
+
+		list_del(lnk);
+		fcblist_write_unlock(filep, flags);
+		kfree(fcbp);
+		fcblist_write_lock(filep, flags);
+	}
+
+	fcblist_write_unlock(filep, flags);
+}
+
+EXPORT_SYMBOL(file_notify_addcb);
+EXPORT_SYMBOL(file_notify_delcb);
diff -aur --exclude=CVS linux/fs/file_table.c 2.4.18-kpoll/fs/file_table.c
--- linux/fs/file_table.c	Mon Sep 17 22:16:30 2001
+++ 2.4.18-kpoll/fs/file_table.c	Tue Mar 26 17:07:26 2002
@@ -8,6 +8,7 @@
 #include <linux/string.h>
 #include <linux/slab.h>
 #include <linux/file.h>
+#include <linux/fcblist.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/smp_lock.h>
@@ -47,6 +48,7 @@
 		f->f_uid = current->fsuid;
 		f->f_gid = current->fsgid;
 		list_add(&f->f_list, &anon_list);
+		file_notify_init(f);
 		file_list_unlock();
 		return f;
 	}
@@ -91,6 +93,7 @@
 	filp->f_uid    = current->fsuid;
 	filp->f_gid    = current->fsgid;
 	filp->f_op     = dentry->d_inode->i_fop;
+	file_notify_init(filp);
 	if (filp->f_op->open)
 		return filp->f_op->open(dentry->d_inode, filp);
 	else
@@ -104,6 +107,7 @@
 	struct inode * inode = dentry->d_inode;
 
 	if (atomic_dec_and_test(&file->f_count)) {
+		file_notify_cleanup(file);
 		locks_remove_flock(file);
 
 		if (file->f_iobuf)
diff -aur --exclude=CVS linux/fs/pipe.c 2.4.18-kpoll/fs/pipe.c
--- linux/fs/pipe.c	Sat Sep 29 03:03:48 2001
+++ 2.4.18-kpoll/fs/pipe.c	Tue Mar 26 17:07:26 2002
@@ -10,6 +10,7 @@
 #include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/fcblist.h>
 
 #include <asm/uaccess.h>
 #include <asm/ioctls.h>
@@ -40,6 +41,7 @@
 pipe_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
 {
 	struct inode *inode = filp->f_dentry->d_inode;
+	int pfull;
 	ssize_t size, read, ret;
 
 	/* Seeks are not allowed on pipes.  */
@@ -72,6 +74,7 @@
 			PIPE_WAITING_READERS(*inode)++;
 			pipe_wait(inode);
 			PIPE_WAITING_READERS(*inode)--;
+			pfull = PIPE_FULL(*inode);
 			ret = -ERESTARTSYS;
 			if (signal_pending(current))
 				goto out;
@@ -82,6 +85,8 @@
 				goto out;
 		}
 	}
+	else
+		pfull = PIPE_FULL(*inode);
 
 	/* Read what data is available.  */
 	ret = -EFAULT;
@@ -104,6 +109,9 @@
 		count -= chars;
 		buf += chars;
 	}
+	/* Send notification message */
+	if (pfull && !PIPE_FULL(*inode) && PIPE_WRITEFILE(*inode))
+		file_send_notify(PIPE_WRITEFILE(*inode), ION_OUT, POLLOUT | POLLWRNORM | POLLWRBAND);
 
 	/* Cache behaviour optimization */
 	if (!PIPE_LEN(*inode))
@@ -136,6 +144,7 @@
 pipe_write(struct file *filp, const char *buf, size_t count, loff_t *ppos)
 {
 	struct inode *inode = filp->f_dentry->d_inode;
+	int pempty;
 	ssize_t free, written, ret;
 
 	/* Seeks are not allowed on pipes.  */
@@ -180,6 +189,7 @@
 	}
 
 	/* Copy into available space.  */
+	pempty = PIPE_EMPTY(*inode);
 	ret = -EFAULT;
 	while (count > 0) {
 		int space;
@@ -208,6 +218,9 @@
 			break;
 
 		do {
+			/* Send notification message */
+			if (pempty && !PIPE_EMPTY(*inode) && PIPE_READFILE(*inode))
+				file_send_notify(PIPE_READFILE(*inode), ION_IN, POLLIN | POLLRDNORM);
 			/*
 			 * Synchronous wake-up: it knows that this process
 			 * is going to give up this CPU, so it doesnt have
@@ -217,6 +230,7 @@
 			PIPE_WAITING_WRITERS(*inode)++;
 			pipe_wait(inode);
 			PIPE_WAITING_WRITERS(*inode)--;
+			pempty = PIPE_EMPTY(*inode);
 			if (signal_pending(current))
 				goto out;
 			if (!PIPE_READERS(*inode))
@@ -225,6 +239,9 @@
 		ret = -EFAULT;
 	}
 
+	/* Send notification message */
+	if (pempty && !PIPE_EMPTY(*inode) && PIPE_READFILE(*inode))
+		file_send_notify(PIPE_READFILE(*inode), ION_IN, POLLIN | POLLRDNORM);
 	/* Signal readers asynchronously that there is more data.  */
 	wake_up_interruptible(PIPE_WAIT(*inode));
 
@@ -303,9 +320,22 @@
 static int
 pipe_release(struct inode *inode, int decr, int decw)
 {
+	struct file *rdfile, *wrfile;
 	down(PIPE_SEM(*inode));
 	PIPE_READERS(*inode) -= decr;
 	PIPE_WRITERS(*inode) -= decw;
+	rdfile = PIPE_READFILE(*inode);
+	wrfile = PIPE_WRITEFILE(*inode);
+ 	if (decr && !PIPE_READERS(*inode)) {
+		PIPE_READFILE(*inode) = NULL;
+		if (wrfile)
+			file_send_notify(wrfile, ION_HUP, POLLHUP);
+	}
+	if (decw && !PIPE_WRITERS(*inode)) {
+		PIPE_WRITEFILE(*inode) = NULL;
+		if (rdfile)
+			file_send_notify(rdfile, ION_HUP, POLLHUP);
+	}
 	if (!PIPE_READERS(*inode) && !PIPE_WRITERS(*inode)) {
 		struct pipe_inode_info *info = inode->i_pipe;
 		inode->i_pipe = NULL;
@@ -458,6 +488,7 @@
 	PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0;
 	PIPE_WAITING_READERS(*inode) = PIPE_WAITING_WRITERS(*inode) = 0;
 	PIPE_RCOUNTER(*inode) = PIPE_WCOUNTER(*inode) = 1;
+	PIPE_READFILE(*inode) = PIPE_WRITEFILE(*inode) = NULL;
 
 	return inode;
 fail_page:
@@ -564,6 +595,9 @@
 	f2->f_op = &write_pipe_fops;
 	f2->f_mode = 2;
 	f2->f_version = 0;
+
+	PIPE_READFILE(*inode) = f1;
+	PIPE_WRITEFILE(*inode) = f2;
 
 	fd_install(i, f1);
 	fd_install(j, f2);
diff -aur --exclude=CVS linux/include/asm-i386/poll.h 2.4.18-kpoll/include/asm-i386/poll.h
--- linux/include/asm-i386/poll.h	Thu Jan 23 20:01:28 1997
+++ 2.4.18-kpoll/include/asm-i386/poll.h	Tue Mar 26 16:54:11 2002
@@ -15,6 +15,7 @@
 #define POLLWRNORM	0x0100
 #define POLLWRBAND	0x0200
 #define POLLMSG		0x0400
+#define POLLREMOVE	0x1000
 
 struct pollfd {
 	int fd;
diff -aur --exclude=CVS linux/include/linux/fcblist.h 2.4.18-kpoll/include/linux/fcblist.h
--- linux/include/linux/fcblist.h	Sat Jul 13 13:00:32 2002
+++ 2.4.18-kpoll/include/linux/fcblist.h	Sat Jul 13 07:35:11 2002
@@ -0,0 +1,67 @@
+/*
+ *  include/linux/fcblist.h
+ *
+ *  Copyright (C) 2001, Davide Libenzi <davidel@xmailserver.org>
+ *
+ *  Handle file callbacks
+ */
+
+#ifndef __LINUX_FCBLIST_H
+#define __LINUX_FCBLIST_H
+
+#include <linux/config.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/file.h>
+
+
+
+/* file callback notification events */
+#define ION_IN		1
+#define ION_OUT		2
+#define ION_HUP		3
+#define ION_ERR		4
+
+#define FCB_LOCAL_SIZE	4
+
+#define fcblist_read_lock(fp, fl)		read_lock_irqsave(&(fp)->f_cblock, fl)
+#define fcblist_read_unlock(fp, fl)		read_unlock_irqrestore(&(fp)->f_cblock, fl)
+#define fcblist_write_lock(fp, fl)		write_lock_irqsave(&(fp)->f_cblock, fl)
+#define fcblist_write_unlock(fp, fl)	write_unlock_irqrestore(&(fp)->f_cblock, fl)
+
+struct fcb_struct {
+	struct list_head llink;
+	void (*cbproc)(struct file *, void *, unsigned long *, long *);
+	void *data;
+	unsigned long local[FCB_LOCAL_SIZE];
+};
+
+
+extern long ion_band_table[];
+extern long poll_band_table[];
+
+
+extern void file_notify_event(struct file *filep, long *event);
+
+extern int file_notify_addcb(struct file *filep,
+		void (*cbproc)(struct file *, void *, unsigned long *, long *), void *data);
+
+extern int file_notify_delcb(struct file *filep,
+		void (*cbproc)(struct file *, void *, unsigned long *, long *));
+
+extern void file_notify_cleanup(struct file *filep);
+
+
+static inline void file_notify_init(struct file *filep)
+{
+	rwlock_init(&filep->f_cblock);
+	INIT_LIST_HEAD(&filep->f_cblist);
+}
+
+static inline void file_send_notify(struct file *filep, long ioevt, long plevt) {
+	long event[] = { ioevt, plevt, -1 };
+
+	file_notify_event(filep, event);
+}
+
+#endif
diff -aur --exclude=CVS linux/include/linux/fs.h 2.4.18-kpoll/include/linux/fs.h
--- linux/include/linux/fs.h	Mon Feb 25 20:38:13 2002
+++ 2.4.18-kpoll/include/linux/fs.h	Sat Jul 13 07:36:26 2002
@@ -535,6 +535,10 @@
 	/* needed for tty driver, and maybe others */
 	void			*private_data;
 
+	/* file callback list */
+        rwlock_t                f_cblock;
+	struct list_head        f_cblist;
+
 	/* preallocated helper kiobuf to speedup O_DIRECT */
 	struct kiobuf		*f_iobuf;
 	long			f_iobuf_lock;
diff -aur --exclude=CVS linux/include/linux/kpoll.h 2.4.18-kpoll/include/linux/kpoll.h
--- linux/include/linux/kpoll.h	Sat Jul 13 13:00:40 2002
+++ 2.4.18-kpoll/include/linux/kpoll.h	Tue Mar 26 16:54:11 2002
@@ -0,0 +1,40 @@
+/*
+ *  --- Notices from eventpoll.h, upon which this driver is based ---
+ *
+ *  Copyright (C) 2001, Davide Libenzi <davidel@xmailserver.org>
+ *
+ *  Efficent event polling implementation
+ *
+ *  --- End of notice from eventpoll.h ---
+ *
+ *
+ */
+
+#ifndef _LINUX_KPOLL_H
+#define _LINUX_KPOLL_H
+
+
+#define KPOLL_MINOR	        152
+#define KPOLLFD_X_PAGE	        (PAGE_SIZE / sizeof(struct pollfd))
+#define MAX_FDS_IN_KPOLL	(1024 * 128)
+#define MAX_KPOLL_PAGES	        (MAX_FDS_IN_KPOLL / KPOLLFD_X_PAGE)
+
+
+#define KP_FDS_PAGES(n)	(((n) + KPOLLFD_X_PAGE - 1) / KPOLLFD_X_PAGE)
+#define KP_MAP_SIZE(n)	(KP_FDS_PAGES(n) * PAGE_SIZE * 2)
+
+
+struct k_poll {
+	int kp_timeout;
+	unsigned long kp_resoff;
+};
+
+#define KP_ALLOC	_IOR('P',  1, int)
+#define KP_POLL		_IOWR('P', 2, struct k_poll)
+#define KP_FREE		_IO('P',   3)
+#define KP_ISPOLLED	_IOWR('P', 4, struct pollfd)
+#define KP_EVENTPOLL    _IOWR('P', 5, int)
+
+
+#endif
+
diff -aur --exclude=CVS linux/include/linux/list.h 2.4.18-kpoll/include/linux/list.h
--- linux/include/linux/list.h	Fri Dec 21 18:42:03 2001
+++ 2.4.18-kpoll/include/linux/list.h	Sat Jul 13 07:36:30 2002
@@ -172,6 +172,11 @@
         	pos = pos->prev, prefetch(pos->prev))
         	
 
+#define list_first(head)	(((head)->next != (head)) ? (head)->next: (struct list_head *) 0)
+#define list_last(head)	(((head)->prev != (head)) ? (head)->prev: (struct list_head *) 0)
+#define list_next(pos, head)	(((pos)->next != (head)) ? (pos)->next: (struct list_head *) 0)
+#define list_prev(pos, head)	(((pos)->prev != (head)) ? (pos)->prev: (struct list_head *) 0)
+
 #endif /* __KERNEL__ || _LVM_H_INCLUDE */
 
 #endif
diff -aur --exclude=CVS linux/include/linux/pipe_fs_i.h 2.4.18-kpoll/include/linux/pipe_fs_i.h
--- linux/include/linux/pipe_fs_i.h	Wed Apr 25 23:18:23 2001
+++ 2.4.18-kpoll/include/linux/pipe_fs_i.h	Tue Mar 26 16:54:11 2002
@@ -13,6 +13,8 @@
 	unsigned int waiting_writers;
 	unsigned int r_counter;
 	unsigned int w_counter;
+	struct file *rdfile;
+	struct file *wrfile;
 };
 
 /* Differs from PIPE_BUF in that PIPE_SIZE is the length of the actual
@@ -30,6 +32,8 @@
 #define PIPE_WAITING_WRITERS(inode)	((inode).i_pipe->waiting_writers)
 #define PIPE_RCOUNTER(inode)	((inode).i_pipe->r_counter)
 #define PIPE_WCOUNTER(inode)	((inode).i_pipe->w_counter)
+#define PIPE_READFILE(inode)	((inode).i_pipe->rdfile)
+#define PIPE_WRITEFILE(inode)	((inode).i_pipe->wrfile)
 
 #define PIPE_EMPTY(inode)	(PIPE_LEN(inode) == 0)
 #define PIPE_FULL(inode)	(PIPE_LEN(inode) == PIPE_SIZE)
diff -aur --exclude=CVS linux/include/net/sock.h 2.4.18-kpoll/include/net/sock.h
--- linux/include/net/sock.h	Fri Dec 21 18:42:04 2001
+++ 2.4.18-kpoll/include/net/sock.h	Tue Mar 26 16:54:11 2002
@@ -105,7 +105,9 @@
 
 #include <asm/atomic.h>
 #include <net/dst.h>
-
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/fcblist.h>
 
 /* The AF_UNIX specific socket options */
 struct unix_opt {
@@ -1215,8 +1217,13 @@
 
 static inline void sk_wake_async(struct sock *sk, int how, int band)
 {
-	if (sk->socket && sk->socket->fasync_list)
+	if (sk->socket) {
+		if (sk->socket->file)
+			file_send_notify(sk->socket->file, ion_band_table[band - POLL_IN],
+					poll_band_table[band - POLL_IN]);
+		if (sk->socket->fasync_list)
 		sock_wake_async(sk->socket, how, band);
+	}
 }
 
 #define SOCK_MIN_SNDBUF 2048
diff -aur --exclude=CVS linux/net/ipv4/tcp.c 2.4.18-kpoll/net/ipv4/tcp.c
--- linux/net/ipv4/tcp.c	Fri Dec 21 18:42:05 2001
+++ 2.4.18-kpoll/net/ipv4/tcp.c	Fri Jul 12 14:02:49 2002
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp.c,v 1.215 2001/10/31 08:17:58 davem Exp $
+ * Version:	$Id: tcp.c,v 1.3 2002/04/24 09:22:17 tonyrog Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -470,8 +470,8 @@
 		if (sk->sleep && waitqueue_active(sk->sleep))
 			wake_up_interruptible(sk->sleep);
 
-		if (sock->fasync_list && !(sk->shutdown&SEND_SHUTDOWN))
-			sock_wake_async(sock, 2, POLL_OUT);
+		if (!(sk->shutdown&SEND_SHUTDOWN))
+			sk_wake_async(sk, 2, POLL_OUT);
 	}
 }
 
