diff -ruN -X_diff_exclude_from_file linux-2.4.3/arch/i386/config.in linux-2.4.3+fd_events/arch/i386/config.in
--- linux-2.4.3/arch/i386/config.in	Mon Jan  8 16:27:56 2001
+++ linux-2.4.3+fd_events/arch/i386/config.in	Mon Apr  2 09:23:44 2001
@@ -227,6 +227,10 @@
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
 tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC
 
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  bool 'File Descriptor Events Interface (EXPERIMENTAL)' CONFIG_FILE_EVENTS
+fi
+
 bool 'Power Management support' CONFIG_PM
 
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
diff -ruN -X_diff_exclude_from_file linux-2.4.3/fs/select.c linux-2.4.3+fd_events/fs/select.c
--- linux-2.4.3/fs/select.c	Fri Feb  9 14:29:44 2001
+++ linux-2.4.3+fd_events/fs/select.c	Mon Apr  2 16:38:23 2001
@@ -18,6 +18,7 @@
 #include <linux/smp_lock.h>
 #include <linux/poll.h>
 #include <linux/file.h>
+#include <linux/fdevent.h>
 
 #include <asm/uaccess.h>
 
@@ -491,3 +492,159 @@
 	poll_freewait(&table);
 	return err;
 }
+
+#ifdef CONFIG_FILE_EVENTS
+/* Takes an event and registers current->files->fd[fd] to watch for 
+ * ev->mask on ev->fd; when something happens the event structure gets
+ * queued on current->files->file_event_head; this queue is returned
+ * to the user when sys_get_events is called
+ */
+asmlinkage long sys_bind_event(struct fdevent *uev)
+{
+	struct fdevent *ev;
+	int err=0;
+	
+	if( (ev = kmalloc(sizeof(struct fdevent), GFP_KERNEL)) ) {
+		err = -ENOMEM;
+		goto out;
+	}
+	
+	if( copy_from_user(ev, uev, sizeof(struct fdevent)) ) {
+		err = -EFAULT;
+		goto out_ev;
+	}
+	
+	switch( ev->fd ) {
+		int i;
+		struct file *file;
+		
+	case FDEVENT_FD_EXISTING:
+		/* set the ev->mask on each existing current->files->fd[*] */
+		for(i=0;i<current->files->max_fds;i++) {
+			file = fget(i);
+			if( file ) {
+				file->f_event_mask = ev->mask;
+				fput(file);
+			}
+		}
+		break;
+		
+	case FDEVENT_FD_DEFAULT:
+		/* set the default mask for new fd to ev->mask */
+		current->files->file_event_default_mask = ev->mask;
+		break;
+		
+	default:
+		/* set the ev->mask on the specified ev->fd */
+		file = fget(ev->fd);
+		if (!file) {
+			err = -EBADF;
+			goto out_ev;
+		}
+		file->f_event_mask = ev->mask;
+		fput(file);
+	}
+
+out_ev:
+	kfree(ev);
+out:
+	return err;
+}
+
+/* see sys_bind_event for specifics; in general this is just like a sys_poll call
+ * but an array of files does not have to be traversed as the files_struct
+ * of the current process already knows what events have fired */
+asmlinkage long sys_get_events(struct fdevent *uevs, unsigned int nevs, long timeout)
+{
+	int ret=0;
+	register file_event_t *ev, *nev;
+	register struct fdevent *uev = uevs;
+	file_event_list_t *list = &current->files->file_event_list;
+	
+	spin_lock_bh(&list->lock);
+	
+	ev = list->head;
+	while( ev != (file_event_t*)list ) {
+		int error = copy_to_user( uev, &ev->event, sizeof(*uev) );
+		if( signal_pending(current) ) {
+			if ( !ret )
+				ret = -EINTR;
+			break;
+		}
+		if( error ) {
+			if( !ret ) 
+				ret = -EFAULT;
+			break;
+		}
+		
+		ev->event.mask = 0;
+		
+		nev = ev->next;
+		file_event_list_del(ev);
+		ev = nev;
+		
+		uev++;
+		ret++;
+		if( ret==nevs ) 
+			break;
+	}
+	
+	spin_unlock_bh(&list->lock);
+
+	return ret;
+}
+
+/* initialize a file based on file_struct of a task 
+ *  file  - a newly created file
+ *  files - a files_struct of the task called from
+ */
+int file_event_init( struct file *file, struct files_struct *files )
+{
+	if( file ) {
+		file->f_event_mask = files->file_event_default_mask;
+		file->f_event_list = &files->file_event_list;
+		INIT_FILE_EVENT( &file->f_event );
+	}
+	return 0;
+}
+
+/* releases any bindings the file has with the event list for it's task */
+int file_event_release( struct file *file )
+{
+	if( file && file->f_event_list ) {
+		int flags;
+		file_event_list_t *list = file->f_event_list;
+		
+		spin_lock_irqsave(&list->lock,flags);
+		if( file->f_event.event.mask )
+			file_event_list_del( &file->f_event );
+		spin_unlock_irqrestore(&list->lock,flags);
+	}
+	return 0;
+}
+
+/* deliver an event
+ *  file - file pointer, properly locked a la get_file 
+ *  mask - what event fired (see fdevent.h)
+ */
+int deliver_file_event(struct file *file, unsigned long mask)
+{
+	if( file && (file->f_event_mask & mask) ) {
+		
+		file_event_list_t *list = file->f_event_list;
+		file_event_t *ev = &file->f_event;
+		
+		spin_lock_bh(&list->lock);
+		
+		if( ! ev->event.mask ) 
+			file_event_list_add_tail( ev, list );
+
+		ev->event.mask |= mask;
+
+		spin_unlock_bh(&list->lock);
+	}
+	
+	return 0;
+}
+
+#endif
diff -ruN -X_diff_exclude_from_file linux-2.4.3/include/asm-i386/fdevent.h linux-2.4.3+fd_events/include/asm-i386/fdevent.h
--- linux-2.4.3/include/asm-i386/fdevent.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.3+fd_events/include/asm-i386/fdevent.h	Mon Apr  2 16:21:31 2001
@@ -0,0 +1,46 @@
+#ifndef __i386_FDEVENT_H
+#define __i386_FDEVENT_H
+
+#include <asm/poll.h>
+
+/* Event types are borowed from poll.h;
+ * NOTE that while the current implementation shares the flags of poll()
+ *     this cannot be taken as a rule
+ */
+#define FDEVENT_IN	POLLIN     /* 0x0001 */
+#define FDEVENT_PRI	POLLPRI    /* 0x0002 */
+#define FDEVENT_OUT	POLLOUT    /* 0x0004 */
+#define FDEVENT_ERR	POLLERR    /* 0x0008 */
+#define FDEVENT_HUP	POLLHUP    /* 0x0010 */
+#define FDEVENT_NVAL	POLLNVLA   /* 0x0020 */
+
+/* non standard, sais poll.h */
+#define FDEVENT_RDNORM	POLLRDNORM /* 0x0040 */
+#define FDEVENT_RDBAND	POLLRDBAND /* 0x0080 */
+#define FDEVENT_WRNORM	POLLWRNORM /* 0x0100 */
+#define FDEVENT_WRBAND	POLLWRBAND /* 0x0200 */
+#define FDEVENT_MSG	POLLMSG    /* 0x0400 */
+
+struct fdevent;
+
+typedef void (fdevent_fn)(struct fdevent*);
+
+struct fdevent {
+	int fd;
+	unsigned long mask;
+	void *data;
+	fdevent_fn *fn;
+};
+
+#define FDEVENT_INIT { 0, 0, NULL, NULL }
+#define INIT_FDEVENT(ptr) do { \
+	(ptr)->fd = 0; (ptr)->mask = 0; \
+	(ptr)->data = 0; (ptr)->fn = 0; \
+} while (0)
+
+
+/* wildcards for fdevent.fd */
+#define FDEVENT_FD_EXISTING -1
+#define FDEVENT_FD_DEFAULT  -2
+
+#endif
diff -ruN -X_diff_exclude_from_file linux-2.4.3/include/linux/fdevent.h linux-2.4.3+fd_events/include/linux/fdevent.h
--- linux-2.4.3/include/linux/fdevent.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.3+fd_events/include/linux/fdevent.h	Mon Apr  2 16:30:57 2001
@@ -0,0 +1,75 @@
+#ifndef _LINUX_FDEVENT_H
+#define _LINUX_FDEVENT_H
+
+#ifdef CONFIG_FILE_EVENTS
+
+#include <asm/fdevent.h>
+
+#ifdef __KERNEL__
+
+#include <linux/list.h>
+#include <asm/spinlock.h>
+#include <asm/types.h>
+
+typedef struct file_event_s {
+	struct file_event_s *next, *prev; /* queue stored in files_struct */
+	struct fdevent event;
+	
+} file_event_t;
+
+typedef struct file_event_list_s {
+	struct file_event_s *head, *tail; /* just like next/prev above */
+	spinlock_t lock;
+} file_event_list_t;
+
+#define FILE_EVENT_INIT { NULL, NULL, FDEVENT_INIT }
+#define INIT_FILE_EVENT(ptr) do { \
+	(ptr)->next = 0; (ptr)->prev = 0; \
+	INIT_FDEVENT( &(ptr)->event ); \
+} while (0)
+
+#define FILE_EVENT_LIST_INIT { NULL, NULL, SPIN_LOCK_UNLOCKED }
+#define INIT_FILE_EVENT_LIST(ptr) do { \
+	(ptr)->next = 0; (ptr)->prev = 0; \
+	spin_lock_init( &(ptr)->lock ); \
+} while (0)
+
+
+/* 
+ * these are some wrappers for the <list.h> functions we are interested in
+ * ... no point in reinventing the wheel.
+ */
+
+static inline void 
+file_event_list_add_tail( file_event_t *ev, file_event_list_t *list )
+{
+	list_add_tail( (struct list_head*)ev, (struct list_head*)list );
+}
+
+static inline void 
+file_event_list_del( file_event_t *ev )
+{
+	list_del( (struct list_head*)ev );
+}
+
+/* forward declarations */
+struct file;
+struct files_struct;
+
+/* enqueue the event */
+extern int 
+deliver_file_event(struct file *file, unsigned long mask);
+
+/* initialize links between the file and the task's files_struct */
+extern int
+file_event_init( struct file *file, struct files_struct *files );
+
+/* clean up links and remove file from any link list */
+extern int
+file_event_release( struct file *file );
+
+#endif /* __KERNEL__ */
+
+#endif /* CONFIG_FILE_EVENTS */
+
+#endif /* _LINUX_FDEVENT_H */
diff -ruN -X_diff_exclude_from_file linux-2.4.3/include/linux/fs.h linux-2.4.3+fd_events/include/linux/fs.h
--- linux-2.4.3/include/linux/fs.h	Mon Apr  2 08:46:43 2001
+++ linux-2.4.3+fd_events/include/linux/fs.h	Mon Apr  2 16:30:58 2001
@@ -21,6 +21,7 @@
 #include <linux/cache.h>
 #include <linux/stddef.h>
 #include <linux/string.h>
+#include <linux/fdevent.h>
 
 #include <asm/atomic.h>
 #include <asm/bitops.h>
@@ -495,6 +496,12 @@
 
 	/* needed for tty driver, and maybe others */
 	void			*private_data;
+	
+#ifdef CONFIG_FILE_EVENTS
+	unsigned long           f_event_mask;
+	file_event_t		f_event;
+	file_event_list_t       *f_event_list;
+#endif
 };
 extern spinlock_t files_lock;
 #define file_list_lock() spin_lock(&files_lock);
diff -ruN -X_diff_exclude_from_file linux-2.4.3/include/linux/sched.h linux-2.4.3+fd_events/include/linux/sched.h
--- linux-2.4.3/include/linux/sched.h	Mon Apr  2 08:46:44 2001
+++ linux-2.4.3+fd_events/include/linux/sched.h	Mon Apr  2 16:30:58 2001
@@ -177,8 +177,18 @@
 	fd_set close_on_exec_init;
 	fd_set open_fds_init;
 	struct file * fd_array[NR_OPEN_DEFAULT];
+#ifdef CONFIG_FILE_EVENTS
+	unsigned long file_event_default_mask;
+	file_event_list_t file_event_list;
+#endif
 };
 
+#ifdef CONFIG_FILE_EVENTS
+#define INIT_FILES_STRUCT_EVENTS , 0, FILE_EVENT_LIST_INIT
+#else
+#define INIT_FILES_STRUCT_EVENTS
+#endif
+
 #define INIT_FILES \
 { 							\
 	count:		ATOMIC_INIT(1), 		\
@@ -192,6 +202,7 @@
 	close_on_exec_init: { { 0, } }, 		\
 	open_fds_init:	{ { 0, } }, 			\
 	fd_array:	{ NULL, } 			\
+	INIT_FILES_STRUCT_EVENTS			\
 }
 
 /* Maximum number of active map areas.. This is a random (large) number */
diff -ruN -X_diff_exclude_from_file linux-2.4.3/include/net/sock.h linux-2.4.3+fd_events/include/net/sock.h
--- linux-2.4.3/include/net/sock.h	Mon Apr  2 08:47:16 2001
+++ linux-2.4.3+fd_events/include/net/sock.h	Mon Apr  2 16:31:16 2001
@@ -37,6 +37,7 @@
 #include <linux/timer.h>
 #include <linux/cache.h>
 #include <linux/in.h>		/* struct sockaddr_in */
+#include <linux/fdevent.h>
 
 #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
 #include <linux/in6.h>		/* struct sockaddr_in6 */
@@ -1220,6 +1221,9 @@
 {
 	if (sk->socket && sk->socket->fasync_list)
 		sock_wake_async(sk->socket, how, band);
+#ifdef CONFIG_FILE_EVENTS
+	deliver_file_event(sk->socket->file, band);
+#endif
 }
 
 #define SOCK_MIN_SNDBUF 2048
diff -ruN -X_diff_exclude_from_file linux-2.4.3/net/socket.c linux-2.4.3+fd_events/net/socket.c
--- linux-2.4.3/net/socket.c	Fri Nov 17 14:36:27 2000
+++ linux-2.4.3+fd_events/net/socket.c	Mon Apr  2 16:04:58 2001
@@ -484,6 +484,8 @@
  
 void sock_release(struct socket *sock)
 {
+	(void)file_event_release( sock->file );
+
 	if (sock->ops) 
 		sock->ops->release(sock);
 
@@ -898,7 +900,9 @@
 	retval = sock_map_fd(sock);
 	if (retval < 0)
 		goto out_release;
-
+	
+	(void)file_event_init( sock->file, current->files );
+	
 out:
 	/* It may be already another descriptor 8) Not kernel problem. */
 	return retval;
@@ -953,8 +957,11 @@
 	err = put_user(fd1, &usockvec[0]); 
 	if (!err)
 		err = put_user(fd2, &usockvec[1]);
-	if (!err)
+	if (!err) {
+		(void)file_event_init( sock1->file, current->files );
+		(void)file_event_init( sock2->file, current->files );
 		return 0;
+	}
 
 	sys_close(fd2);
 	sys_close(fd1);
@@ -1066,6 +1073,8 @@
 
 	if ((err = sock_map_fd(newsock)) < 0)
 		goto out_release;
+
+	(void)file_event_init( newsock->file, current->files );
 
 out_put:
 	sockfd_put(sock);

