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 #include #include +#include #include @@ -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;ifiles->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 = ¤t->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 + +/* 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 + +#ifdef __KERNEL__ + +#include +#include +#include + +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 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 #include #include +#include #include #include @@ -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 #include #include /* struct sockaddr_in */ +#include #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) #include /* 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);