]> Zhao Yanbai Git Server - minix.git/commitdiff
Clean up and fix multiple bugs in select:
authorThomas Veerman <thomas@minix3.org>
Wed, 13 Apr 2011 13:25:34 +0000 (13:25 +0000)
committerThomas Veerman <thomas@minix3.org>
Wed, 13 Apr 2011 13:25:34 +0000 (13:25 +0000)
 - Remove redundant code.
 - Always wait for the initial reply from an asynchronous select request,
   even if the select has been satisfied on another file descriptor or
   was canceled due to a serious error.
 - Restart asynchronous selects if upon reply from the driver turns out
   that there are deferred operations (and do not forget we're still
   interested in the results of the deferred operations).
 - Do not hang a non-blocking select when another blocking select on
   the same filp is still blocking.
 - Split blocking operations in read, write, and exceptions (i.e.,
   blocking on read does not imply the write will block as well).
 - Some loops would iterate over OPEN_MAX file descriptors instead of
   the "highest" file descriptor.
 - Use proper internal error return values.
 - A secondary reply from a synchronous driver is essentially the same
   as from an asynchronous driver (the only difference being how the
   answer is received). Merge.
 - Return proper error code after a driver failure.
 - Auto-detect whether a driver is synchronous or asynchronous.
 - Remove some code duplication.
 - Clean up code (coding style, add missing comments, put all select
   related code together).

servers/vfs/device.c
servers/vfs/dmap.c
servers/vfs/dmap.h
servers/vfs/file.h
servers/vfs/main.c
servers/vfs/pipe.c
servers/vfs/proto.h
servers/vfs/select.c

index 3bd48eacd13d3a83e9aa2fcbab9e2f74cb646a1e..8e918482ccba459daabad54c6b5996947f9846de 100644 (file)
@@ -183,8 +183,8 @@ PUBLIC void dev_status(message *m)
                                revive(endpt, st.REP_STATUS);
                                break;
                        case DEV_IO_READY:
-                               select_notified(d, st.DEV_MINOR,
-                                       st.DEV_SEL_OPS);
+                               select_reply2(st.m_source, st.DEV_MINOR,
+                                             st.DEV_SEL_OPS);
                                break;
                        default:
                                printf("FS: unrecognized reply %d to "
index 8048c4db1d0120ebab7af6eb306562361152b379..cae93fa34fa90e9c1a4d6c11063e32e64c4458d9 100644 (file)
@@ -242,3 +242,20 @@ PUBLIC void dmap_endpt_up(int proc_e)
        }
        return;
 }
+
+/*===========================================================================*
+ *                             get_dmap                                     *
+ *===========================================================================*/ 
+PUBLIC struct dmap *get_dmap(endpoint_t proc_e)
+{
+/* See if 'proc_e' endpoint belongs to a valid dmap entry. If so, return a
+ * pointer */
+
+  int major;
+  for (major = 0; major < NR_DEVICES; major++)
+       if (dmap_driver_match(proc_e, major))
+               return(&dmap[major]);
+
+  return(NULL);
+}
+
index ccf53e30a0135a07a04034b9f9ccd675430435de..1430d5287a3511357897b78e345f74ba02257a68 100644 (file)
@@ -1,3 +1,6 @@
+#ifndef __VFS_DMAP_H__
+#define __VFS_DMAP_H__
+
 /*
 dmap.h
 */
@@ -21,3 +24,5 @@ extern struct dmap {
   int dmap_style;
   struct filp *dmap_sel_filp;
 } dmap[];
+
+#endif
index 8ac8d09ba4a9ec9a39d926b597176c557451bdfb..fe3b99971d3e3520a84432b179c473a4a31a89b0 100644 (file)
@@ -1,3 +1,6 @@
+#ifndef __VFS_FILE_H__
+#define __VFS_FILE_H__
+
 /* This is the filp table.  It is an intermediary between file descriptors and
  * inodes.  A slot is free if filp_count == 0.
  */
@@ -7,10 +10,7 @@ EXTERN struct filp {
   int filp_flags;              /* flags from open and fcntl */
   int filp_state;              /* state for crash recovery */
   int filp_count;              /* how many file descriptors share this slot?*/
-/*  struct inode *filp_ino;*/  /* pointer to the inode */
-
-  struct vnode *filp_vno;
-  
+  struct vnode *filp_vno;      /* vnode belonging to this file */
   u64_t filp_pos;              /* file position */
 
   /* the following fields are for select() and are owned by the generic
@@ -29,13 +29,17 @@ EXTERN struct filp {
 #define FS_NORMAL      0       /* file descriptor can be used normally */
 #define FS_NEEDS_REOPEN        1       /* file descriptor needs to be re-opened */
 
-#define FSF_UPDATE     1       /* The driver should be informed about new
+#define FSF_UPDATE     001     /* The driver should be informed about new
                                 * state.
                                 */
-#define FSF_BUSY       2       /* Select operation sent to driver but no 
+#define FSF_BUSY       002     /* Select operation sent to driver but no 
                                 * reply yet.
                                 */
-#define FSF_BLOCK      4       /* Request is blocking, the driver should 
+#define FSF_RD_BLOCK   010     /* Read request is blocking, the driver should 
                                 * keep state.
                                 */
+#define FSF_WR_BLOCK   020     /* Write request is blocking */
+#define FSF_ERR_BLOCK  040     /* Exception request is blocking */
+#define FSF_BLOCKED    070
+#endif
 
index 6317886c0cfc3a8707dcdea35be170c520b3e246..5ee8ec302c86b76b0c036b4aa11b058b09b00b11 100644 (file)
@@ -95,12 +95,12 @@ PUBLIC int main(void)
        }
        if (call_nr == DEV_SEL_REPL1)
        {
-               select_reply1();
+               select_reply1(m_in.m_source, m_in.DEV_MINOR, m_in.DEV_SEL_OPS);
                continue;
        }
        if (call_nr == DEV_SEL_REPL2)
        {
-               select_reply2();
+               select_reply2(m_in.m_source, m_in.DEV_MINOR, m_in.DEV_SEL_OPS);
                continue;
        }
 
index 1491342a42e0db110899996f2533ab028c32fa74..c5183b33790b3513f2a1a8b25863f377250ccf5d 100644 (file)
@@ -328,20 +328,18 @@ size_t size;
  *===========================================================================*/
 PUBLIC void unsuspend_by_endpt(endpoint_t proc_e)
 {
+/* Revive processes waiting for drivers (SUSPENDed) that have disappeared with
+ * return code EAGAIN.
+ */
   struct fproc *rp;
 
-  /* Revive processes waiting for drivers (SUSPENDed) that have
-   * disappeared with return code EAGAIN.
-   */
-  for (rp = &fproc[0]; rp < &fproc[NR_PROCS]; rp++)
-       if(rp->fp_pid != PID_FREE &&
-          rp->fp_blocked_on == FP_BLOCKED_ON_OTHER && rp->fp_task == proc_e) {
+  for (rp = &fproc[0]; rp < &fproc[NR_PROCS]; rp++) {
+       if (rp->fp_pid == PID_FREE) continue;
+       if (rp->fp_blocked_on == FP_BLOCKED_ON_OTHER && rp->fp_task == proc_e) 
                revive(rp->fp_endpoint, EAGAIN);
-       }
+  }
 
-  /* Revive processes waiting in drivers on select()s
-   * with EAGAIN too.
-   */
+  /* Revive processes waiting in drivers on select()s with EAGAIN too */
   select_unsuspend_by_endpt(proc_e);
 
   return;
index 1cb17b9ca385e87507b9fe0f0593f3428b86c572..443e135d3bca6d64bd8fa0ab3b406cc9fa1779ee 100644 (file)
@@ -36,14 +36,15 @@ _PROTOTYPE( endpoint_t suspended_ep, (endpoint_t driver,
 _PROTOTYPE( void reopen_reply, (void)                                  );
 
 /* dmap.c */
-_PROTOTYPE( int do_mapdriver, (void)                                   );
-_PROTOTYPE( int map_service, (struct rprocpub *rpub)                   );
 _PROTOTYPE( void build_dmap, (void)                                    );
-_PROTOTYPE( int map_driver, (const char *label, int major, endpoint_t proc_nr,
-       int dev_style, int flags)                                       );
+_PROTOTYPE( int do_mapdriver, (void)                                   );
 _PROTOTYPE( int dmap_driver_match, (endpoint_t proc, int major)                );
-_PROTOTYPE( void dmap_unmap_by_endpt, (int proc_nr)                    );
 _PROTOTYPE( void dmap_endpt_up, (int proc_nr)                          );
+_PROTOTYPE( void dmap_unmap_by_endpt, (int proc_nr)                    );
+_PROTOTYPE( struct dmap *get_dmap, (endpoint_t proc_e)                 );
+_PROTOTYPE( int map_driver, (const char *label, int major, endpoint_t proc_nr,
+       int dev_style, int flags)                                       );
+_PROTOTYPE( int map_service, (struct rprocpub *rpub)                   );
 
 /* exec.c */
 _PROTOTYPE( int pm_exec, (int proc_e, char *path, vir_bytes path_len,
@@ -150,11 +151,7 @@ _PROTOTYPE( void revive, (int proc_nr, int bytes)                  );
 _PROTOTYPE( void suspend, (int task)                                   );
 _PROTOTYPE( void pipe_suspend, (int rw_flag, int fd_nr, char *buf,
                                                        size_t size)    );
-_PROTOTYPE( int select_request_pipe, (struct filp *f, int *ops, int bl)        );
-_PROTOTYPE( int select_match_pipe, (struct filp *f)                    );
 _PROTOTYPE( void unsuspend_by_endpt, (endpoint_t)                      );
-_PROTOTYPE( void select_reply1, (void)                                 );
-_PROTOTYPE( void select_reply2, (void)                                 );
 _PROTOTYPE( void wait_for, (endpoint_t)                                        );
 #if DO_SANITYCHECKS
 _PROTOTYPE( int check_pipe, (void)                                     );
@@ -288,9 +285,10 @@ _PROTOTYPE( int do_gcov_flush, (void)                                      );
 
 /* select.c */
 _PROTOTYPE( int do_select, (void)                                      );
-_PROTOTYPE( int select_callback, (struct filp *, int ops)              );
-_PROTOTYPE( void select_forget, (int fproc)                            );
-_PROTOTYPE( void select_timeout_check, (timer_t *)                     );
 _PROTOTYPE( void init_select, (void)                                   );
+_PROTOTYPE( void select_callback, (struct filp *, int ops)             );
+_PROTOTYPE( void select_forget, (endpoint_t proc_e)                            );
+_PROTOTYPE( void select_reply1, (endpoint_t driver_e, int minor, int status));
+_PROTOTYPE( void select_reply2, (endpoint_t driver_e, int minor, int status));
+_PROTOTYPE( void select_timeout_check, (timer_t *)                     );
 _PROTOTYPE( void select_unsuspend_by_endpt, (endpoint_t proc)          );
-_PROTOTYPE( int select_notified, (int major, int minor, int ops)       );
index 96c2c54d04608c71bb0b23e4a328167a306a1faf..653929fa130529071ce845384d3f3ea7513f54a2 100644 (file)
@@ -3,22 +3,22 @@
  * The entry points into this file are
  *   do_select:               perform the SELECT system call
  *   select_callback:  notify select system of possible fd operation 
- *   select_notified:  low-level entry for device notifying select
  *   select_unsuspend_by_endpt: cancel a blocking select on exiting driver
  */
 
-#define DEBUG_SELECT 0
-
 #include "fs.h"
-#include "select.h"
-#include "file.h"
-#include "vnode.h"
-
 #include <sys/time.h>
 #include <sys/select.h>
 #include <minix/com.h>
 #include <minix/u64.h>
 #include <string.h>
+#include <assert.h>
+
+#include "select.h"
+#include "file.h"
+#include "fproc.h"
+#include "dmap.h"
+#include "vnode.h"
 
 /* max. number of simultaneously pending select() calls */
 #define MAXSELECTS 25
 
 PRIVATE struct selectentry {
   struct fproc *requestor;     /* slot is free iff this is NULL */
-  int req_endpt;
+  endpoint_t req_endpt;
   fd_set readfds, writefds, errorfds;
   fd_set ready_readfds, ready_writefds, ready_errorfds;
   fd_set *vir_readfds, *vir_writefds, *vir_errorfds;
   struct filp *filps[OPEN_MAX];
   int type[OPEN_MAX];
-  int deferred;                /* awaiting initial reply from driver */
-  int deferred_fd;     /* fd awaiting initial reply from driver */
   int nfds, nreadyfds;
+  int error;
   char block;
   clock_t expiry;
   timer_t timer;       /* if expiry > 0 */
@@ -43,39 +42,48 @@ PRIVATE struct selectentry {
 
 FORWARD _PROTOTYPE(int copy_fdsets, (struct selectentry *se, int nfds,
                                      int direction)                    );
+FORWARD _PROTOTYPE(int do_select_request, (struct selectentry *se, int fd,
+                                          int *ops)                    );
 FORWARD _PROTOTYPE(void filp_status, (struct filp *fp, int status)     );
-FORWARD _PROTOTYPE(void restart_proc, (int slot)                       );
+FORWARD _PROTOTYPE(int is_deferred, (struct selectentry *se)           );
+FORWARD _PROTOTYPE(void restart_proc, (struct selectentry *se)         );
 FORWARD _PROTOTYPE(void ops2tab, (int ops, int fd, struct selectentry *e));
-FORWARD _PROTOTYPE(int select_reevaluate, (struct filp *fp)            );
+FORWARD _PROTOTYPE(int is_regular_file, (struct filp *f)               );
+FORWARD _PROTOTYPE(int is_pipe, (struct filp *f)                       );
+FORWARD _PROTOTYPE(int is_supported_major, (struct filp *f)                    );
+FORWARD _PROTOTYPE(int select_request_async, (struct filp *f, int *ops,
+                                              int block)               );
 FORWARD _PROTOTYPE(int select_request_file, (struct filp *f, int *ops,
                                             int block)                 );
-FORWARD _PROTOTYPE(int select_match_file, (struct filp *f)             );
-FORWARD _PROTOTYPE(int select_request_general, (struct filp *f, int *ops,
+FORWARD _PROTOTYPE(int select_request_major, (struct filp *f, int *ops,
+                                            int block)                 );
+FORWARD _PROTOTYPE(int select_request_pipe, (struct filp *f, int *ops,
+                                            int block)                 );
+FORWARD _PROTOTYPE(int select_request_sync, (struct filp *f, int *ops,
                                                int block)              );
-FORWARD _PROTOTYPE(int select_request_asynch, (struct filp *f, int *ops,
-                                              int block)               );
-FORWARD _PROTOTYPE(int select_major_match, (int match_major,
-                                           struct filp *file)          );
 FORWARD _PROTOTYPE(void select_cancel_all, (struct selectentry *e)     );
-FORWARD _PROTOTYPE(void select_wakeup, (struct selectentry *e, int r)  );
-FORWARD _PROTOTYPE(void select_return, (struct selectentry *, int)     );
-FORWARD _PROTOTYPE(void sel_restart_dev, (void)                                );
+FORWARD _PROTOTYPE(void select_cancel_filp, (struct filp *f)           );
+FORWARD _PROTOTYPE(void select_return, (struct selectentry *)          );
+FORWARD _PROTOTYPE(void select_restart_filps, (void)                           );
 FORWARD _PROTOTYPE(int tab2ops, (int fd, struct selectentry *e)                );
 FORWARD _PROTOTYPE(void wipe_select, (struct selectentry *s)           );
 
 PRIVATE struct fdtype {
        int (*select_request)(struct filp *, int *ops, int block);      
-       int (*select_match)(struct filp *);
-       int select_major;
-} fdtypes[] = {
-       { select_request_file, select_match_file, 0 },
-       { select_request_general, NULL, TTY_MAJOR },
-       { select_request_general, NULL, INET_MAJOR },
-       { select_request_general, NULL, UDS_MAJOR },
-       { select_request_pipe, select_match_pipe, 0 },
-       { select_request_asynch, NULL, LOG_MAJOR },
+       int (*type_match)(struct filp *f);
+} fdtypes[] = { 
+       { select_request_major, is_supported_major },
+       { select_request_file, is_regular_file },
+       { select_request_pipe, is_pipe },
 };
 #define SEL_FDS                (sizeof(fdtypes) / sizeof(fdtypes[0]))
+PRIVATE int select_majors[] = { /* List of majors that support selecting on */
+       TTY_MAJOR,
+       INET_MAJOR,
+       UDS_MAJOR,
+       LOG_MAJOR,
+};
+#define SEL_MAJORS     (sizeof(select_majors) / sizeof(select_majors[0]))
 
 /*===========================================================================*
  *                             do_select                                     *
@@ -106,7 +114,7 @@ PUBLIC int do_select(void)
   if (s >= MAXSELECTS) return(ENOSPC);
 
   se = &selecttab[s];
-  wipe_select(se);     /* Clear results of previous usage.*/
+  wipe_select(se);     /* Clear results of previous usage */
   se->req_endpt = who_e;
   se->vir_readfds = (fd_set *) m_in.SEL_READFDS;
   se->vir_writefds = (fd_set *) m_in.SEL_WRITEFDS;
@@ -123,13 +131,14 @@ PUBLIC int do_select(void)
        if (r != OK) return(r);
   }
 
-  /* No nonsense in the timeval. */
-  if(do_timeout && (timeout.tv_sec < 0 || timeout.tv_usec < 0)) return(EINVAL);
+  /* No nonsense in the timeval */
+  if (do_timeout && (timeout.tv_sec < 0 || timeout.tv_usec < 0))
+       return(EINVAL);
 
   /* If there is no timeout, we block forever. Otherwise, we block up to the
    * specified time interval.  
    */
-  if(!do_timeout)      /* No timeout value set */
+  if (!do_timeout)     /* No timeout value set */
        se->block = 1;
   else if (do_timeout && (timeout.tv_sec > 0 || timeout.tv_usec > 0))
        se->block = 1;
@@ -137,62 +146,78 @@ PUBLIC int do_select(void)
        se->block = 0;  
   se->expiry = 0;      /* no timer set (yet) */
 
-  /* Check all file descriptors in the set whether one is 'ready' now. */
+  /* Verify that file descriptors are okay to select on */
   for (fd = 0; fd < nfds; fd++) {
-       int ops, t, type = -1, r;
-       struct filp *filp;
-       
-       if (!(ops = tab2ops(fd, se)))
+       struct filp *f;
+       int type, ops;
+
+       /* Because the select() interface implicitly includes file descriptors
+        * you might not want to select on, we have to figure out whether we're
+        * interested in them. Typically, these file descriptors include fd's
+        * inherited from the parent proc and file descriptors that have been
+        * close()d, but had a lower fd than one in the current set.
+        */
+       if (!(ops = tab2ops(fd, se))) 
                continue; /* No operations set; nothing to do for this fd */
 
        /* Get filp belonging to this fd */
-       filp = se->filps[fd] = get_filp(fd);
-       if (filp == NULL) {
-               if (err_code == EBADF) {
-                       select_cancel_all(se);
-                       return(EBADF);
-               }
-
-               /* File descriptor is 'ready' to return EIO */
-               printf("VFS do_select: EIO after driver failure\n");
-               ops2tab(SEL_RD|SEL_WR|SEL_ERR, fd, se);
-               continue;
-       }
+       f = se->filps[fd] = get_filp(fd);
+       if (f == NULL) {
+               if (err_code == EBADF) 
+                       r = err_code;
+               else /* File descriptor is 'ready' to return EIO */
+                       r = EINTR;
+
+               return(r);
+       } else if (f->filp_vno == NULL) 
+               return(EBADF);
 
-       /* Figure out what type of file we're dealing with */
-       for(t = 0; t < SEL_FDS; t++) {
-               if (fdtypes[t].select_match) {
-                       if (fdtypes[t].select_match(filp)) {
-                               type = t;
-                       }
-               } else if (select_major_match(fdtypes[t].select_major, filp)) {
-                       type = t;
+       /* Check file types. According to POSIX 2008:
+        * "The pselect() and select() functions shall support regular files,
+        * terminal and pseudo-terminal devices, FIFOs, pipes, and sockets. The
+        * behavior of pselect() and select() on file descriptors that refer to
+        * other types of file is unspecified."
+        *
+        * In our case, terminal and pseudo-terminal devices are handled by the
+        * TTY major and sockets by either INET major (socket type AF_INET) or
+        * PFS major (socket type AF_UNIX). PFS acts as an FS when it handles
+        * pipes and as a driver when it handles sockets. Additionally, we
+        * support select on the LOG major to handle kernel logging, which is
+        * beyond the POSIX spec. */
+
+       se->type[fd] = -1;
+       for (type = 0; type < SEL_FDS; type++) {
+               if (fdtypes[type].type_match(f)) {
+                       se->type[fd] = type;
+                       se->nfds = fd+1;
+                       se->filps[fd]->filp_selectors++;
+                       break;
                }
        }
-
-       if (type == -1) {
-               select_cancel_all(se);
+       if (se->type[fd] == -1) /* Type not found */
                return(EBADF);
-       }
-       se->type[fd] = type;
+  }
 
-       /* Test filp for select operations if not already done so. e.g., files
-        * sharing a filp and both doing a select on that filp. */
-       if ((se->filps[fd]->filp_select_ops & ops) != ops) {
-               int wantops;
-               
-               wantops = (se->filps[fd]->filp_select_ops |= ops);
-               r = fdtypes[type].select_request(filp, &wantops, se->block);
+  /* Check all file descriptors in the set whether one is 'ready' now */
+  for (fd = 0; fd < nfds; fd++) {
+       int type, ops, r;
+       struct filp *f;
+       
+       /* Again, check for involuntarily selected fd's */
+       if (!(ops = tab2ops(fd, se))) 
+               continue; /* No operations set; nothing to do for this fd */
+
+       /* Test filp for select operations if not already done so. e.g., 
+        * processes sharing a filp and both doing a select on that filp. */
+       f = se->filps[fd];
+       if ((f->filp_select_ops & ops) != ops) {
+               int wantops, type, block;
+
+               wantops = (f->filp_select_ops |= ops);
+               r = do_select_request(se, fd, &wantops);
                if (r != SEL_OK) {
-                       if (r == SEL_DEFERRED) {
-                               se->deferred = TRUE;
-                               se->deferred_fd = 0;
-                               continue;
-                       }
-                       
-                       /* Error or bogus return code; cancel select. */
-                       select_cancel_all(se);
-                       return(EINVAL);
+                       if (r == SEL_DEFERRED) continue;
+                       else break; /* Error or bogus return code; abort */
                }
                
                /* The select request above might have turned on/off some
@@ -201,12 +226,9 @@ PUBLIC int do_select(void)
                 * in the select table entry. */
                if (wantops & ops) ops2tab(wantops, fd, se);
        } 
-
-       se->nfds = fd+1;
-       se->filps[fd]->filp_selectors++;
   }
 
-  if (se->nreadyfds > 0 || (!se->block && !se->deferred)) {
+  if ((se->nreadyfds > 0 || !se->block) && !is_deferred(se)) {
        /* fd's were found that were ready to go right away, and/or
         * we were instructed not to block at all. Must return
         * immediately.
@@ -214,8 +236,12 @@ PUBLIC int do_select(void)
        r = copy_fdsets(se, se->nfds, TO_PROC);
        select_cancel_all(se);
 
-       if (r != OK) return(r);
-       else return(se->nreadyfds);
+       if (r != OK)
+               return(r);
+       else if (se->error != OK)
+               return(se->error);
+
+       return(se->nreadyfds);
   }
  
   /* Convert timeval to ticks and set the timer. If it fails, undo
@@ -241,7 +267,7 @@ PUBLIC int do_select(void)
        set_timer(&se->timer, ticks, select_timeout_check, s);
   }
 
-  /* if we're blocking, the table entry is now valid. */
+  /* If we're blocking, the table entry is now valid  */
   se->requestor = fp;
 
   /* process now blocked */
@@ -249,61 +275,103 @@ PUBLIC int do_select(void)
   return(SUSPEND);
 }
 
-
 /*===========================================================================*
- *                             select_request_file                          *
+ *                             is_deferred                                  *
  *===========================================================================*/
-PRIVATE int select_request_file(struct filp *f, int *ops, int block)
+PRIVATE int is_deferred(struct selectentry *se)
 {
-  /* output *ops is input *ops */
-  return(SEL_OK);
+/* Find out whether this select has pending initial replies */
+
+  int fd;
+  struct filp *f;
+
+  for (fd = 0; fd < se->nfds; fd++) {
+       if ((f = se->filps[fd]) == NULL) continue;
+       if (f->filp_select_flags & (FSF_UPDATE|FSF_BUSY)) return(TRUE);
+  }
+
+  return(FALSE);
 }
 
 
 /*===========================================================================*
- *                             select_match_file                            *
+ *                             is_regular_file                              *
  *===========================================================================*/
-PRIVATE int select_match_file(struct filp *file)
+PRIVATE int is_regular_file(struct filp *f)
 {
-  return(file && file->filp_vno && (file->filp_vno->v_mode & I_REGULAR));
+  return(f && f->filp_vno && (f->filp_vno->v_mode & I_TYPE) == I_REGULAR);
 }
 
+/*===========================================================================*
+ *                             is_pipe                                      *
+ *===========================================================================*/
+PRIVATE int is_pipe(struct filp *f)
+{
+/* Recognize either anonymous pipe or named pipe (FIFO) */
+  return(f && f->filp_vno && (f->filp_vno->v_mode & I_TYPE) == I_NAMED_PIPE);
+}
 
 /*===========================================================================*
- *                             select_request_general                       *
+ *                             is_supported_major                                           *
  *===========================================================================*/
-PRIVATE int select_request_general(struct filp *f, int *ops, int block)
+PRIVATE int is_supported_major(struct filp *f)
 {
-  int rops = *ops;
-  if (block) rops |= SEL_NOTIFY;
-  *ops = dev_io(VFS_DEV_SELECT, f->filp_vno->v_sdev, rops, NULL,
-               cvu64(0), 0, 0, FALSE);
-  if (*ops < 0)
-       return(SEL_ERR);
+/* See if this filp is a handle on a device on which we support select() */
+  int m;
 
-  return(SEL_OK);
-}
+  if (!(f && f->filp_vno)) return(FALSE);
+  if ((f->filp_vno->v_mode & I_TYPE) != I_CHAR_SPECIAL) return(FALSE);
 
+  for (m = 0; m < SEL_MAJORS; m++) 
+       if (major(f->filp_vno->v_sdev) == select_majors[m]) 
+               return(TRUE);
+
+  return(FALSE);
+}
 
 /*===========================================================================*
- *                             select_request_asynch                        *
+ *                             select_request_async                         *
  *===========================================================================*/
-PRIVATE int select_request_asynch(struct filp *f, int *ops, int block)
+PRIVATE int select_request_async(struct filp *f, int *ops, int block)
 {
-  int r, rops;
+  int r, rops, major;
   struct dmap *dp;
 
   rops = *ops;
+
+  if (!block && (f->filp_select_flags & FSF_BLOCKED)) {
+       /* This filp is blocked waiting for a reply, but we don't want to
+        * block ourselves. Unless we're awaiting the initial reply, these
+        * operations won't be ready */
+       if (!(f->filp_select_flags & FSF_BUSY)) {
+               if ((rops & SEL_RD) && (f->filp_select_flags & FSF_RD_BLOCK))
+                       rops &= ~SEL_RD;
+               if ((rops & SEL_WR) && (f->filp_select_flags & FSF_WR_BLOCK))
+                       rops &= ~SEL_WR;
+               if ((rops & SEL_ERR) && (f->filp_select_flags & FSF_ERR_BLOCK))
+                       rops &= ~SEL_ERR;
+               if (!(rops & (SEL_RD|SEL_WR|SEL_ERR))) {
+                       /* Nothing left to do */
+                       *ops = 0;
+                       return(SEL_OK);
+               }
+       }
+  }
+
   f->filp_select_flags |= FSF_UPDATE;
   if (block) {
        rops |= SEL_NOTIFY;
-       f->filp_select_flags |= FSF_BLOCK;
+       if (rops & SEL_RD)      f->filp_select_flags |= FSF_RD_BLOCK;
+       if (rops & SEL_WR)      f->filp_select_flags |= FSF_WR_BLOCK;
+       if (rops & SEL_ERR)     f->filp_select_flags |= FSF_ERR_BLOCK;
   }
 
   if (f->filp_select_flags & FSF_BUSY)
        return(SEL_DEFERRED);
 
-  dp = &dmap[((f->filp_vno->v_sdev) >> MAJOR) & BYTE];
+  major = major(f->filp_vno->v_sdev);
+  if (major < 0 || major >= NR_DEVICES) return(SEL_ERROR);
+  dp = &dmap[major];
   if (dp->dmap_sel_filp)
        return(SEL_DEFERRED);
 
@@ -311,12 +379,11 @@ PRIVATE int select_request_asynch(struct filp *f, int *ops, int block)
   r = dev_io(VFS_DEV_SELECT, f->filp_vno->v_sdev, rops, NULL,
             cvu64(0), 0, 0, FALSE);
   if (r < 0 && r != SUSPEND)
-       return(SEL_ERR);
+       return(SEL_ERROR);
 
   if (r != SUSPEND) 
        panic("select_request_asynch: expected SUSPEND got: %d", r);
 
-  f->filp_count++;
   dp->dmap_sel_filp = f;
   f->filp_select_flags |= FSF_BUSY;
 
@@ -324,19 +391,96 @@ PRIVATE int select_request_asynch(struct filp *f, int *ops, int block)
 }
 
 /*===========================================================================*
- *                             select_major_match                           *
+ *                             select_request_file                          *
+ *===========================================================================*/
+PRIVATE int select_request_file(struct filp *f, int *ops, int block)
+{
+  /* Files are always ready, so output *ops is input *ops */
+  return(SEL_OK);
+}
+
+/*===========================================================================*
+ *                             select_request_major                         *
  *===========================================================================*/
-PRIVATE int select_major_match(int match_major, struct filp *file)
+PRIVATE int select_request_major(struct filp *f, int *ops, int block)
 {
-  int major;
-  if (!(file && file->filp_vno &&
-      (file->filp_vno->v_mode & I_TYPE) == I_CHAR_SPECIAL))
-       return(0);
-  major = (file->filp_vno->v_sdev >> MAJOR) & BYTE;
-  if (major == match_major) return 1;
-  return 0;
+  int major, r;
+
+  major = major(f->filp_vno->v_sdev);
+  if (major < 0 || major >= NR_DEVICES) return(SEL_ERROR);
+
+  if (dmap[major].dmap_style == STYLE_DEVA)
+       r = select_request_async(f, ops, block);
+  else
+       r = select_request_sync(f, ops, block);
+
+  return(r);
+}
+
+/*===========================================================================*
+ *                             select_request_sync                          *
+ *===========================================================================*/
+PRIVATE int select_request_sync(struct filp *f, int *ops, int block)
+{
+  int rops;
+
+  rops = *ops;
+  if (block) rops |= SEL_NOTIFY;
+  *ops = dev_io(VFS_DEV_SELECT, f->filp_vno->v_sdev, rops, NULL,
+               cvu64(0), 0, 0, FALSE);
+  if (*ops < 0)
+       return(SEL_ERROR);
+
+  return(SEL_OK);
 }
 
+/*===========================================================================*
+ *                             select_request_pipe                          *
+ *===========================================================================*/
+PRIVATE int select_request_pipe(struct filp *f, int *ops, int block)
+{
+  int orig_ops, r = 0, err;
+
+  orig_ops = *ops;
+
+  if ((*ops & (SEL_RD|SEL_ERR))) {
+       err = pipe_check(f->filp_vno, READING, 0, 1, f->filp_pos, 1);
+
+       if (err != SUSPEND)
+               r |= SEL_RD;
+       if (err < 0 && err != SUSPEND)
+               r |= SEL_ERR;
+       if (err == SUSPEND && !(f->filp_mode & R_BIT)) {
+               /* A "meaningless" read select, therefore ready
+                * for reading and no error set. */
+               r |= SEL_RD; 
+               r &= ~SEL_ERR;
+       }
+  }
+
+  if ((*ops & (SEL_WR|SEL_ERR))) {
+       err = pipe_check(f->filp_vno, WRITING, 0, 1, f->filp_pos, 1);
+
+       if (err != SUSPEND)
+               r |= SEL_WR;
+       if (err < 0 && err != SUSPEND)
+               r |= SEL_ERR;
+       if (err == SUSPEND && !(f->filp_mode & W_BIT)) {
+               /* A "meaningless" write select, therefore ready
+                   for writing and no error set. */
+               r |= SEL_WR; 
+               r &= ~SEL_ERR;
+       }
+  }
+
+  /* Some options we collected might not be requested. */
+  *ops = r & orig_ops;
+
+  if (!*ops && block)
+       f->filp_pipe_select_ops |= orig_ops;
+
+  return(SEL_OK);
+}
 
 /*===========================================================================*
  *                             tab2ops                                      *
@@ -382,15 +526,16 @@ PRIVATE void ops2tab(int ops, int fd, struct selectentry *e)
  *===========================================================================*/
 PRIVATE int copy_fdsets(struct selectentry *se, int nfds, int direction)
 {
-  int fd_setsize, r;
+  int r;
+  size_t fd_setsize;
   endpoint_t src_e, dst_e;
   fd_set *src_fds, *dst_fds;
 
-  if(nfds < 0 || nfds > OPEN_MAX)
+  if (nfds < 0 || nfds > OPEN_MAX)
        panic("select copy_fdsets: nfds wrong: %d", nfds);
 
   /* Only copy back as many bits as the user expects. */
-  fd_setsize = _FDSETWORDS(nfds) * _FDSETBITSPERWORD/8;
+  fd_setsize = (size_t) (_FDSETWORDS(nfds) * _FDSETBITSPERWORD/8);
 
   /* Set source and destination endpoints */
   src_e = (direction == FROM_PROC) ? se->req_endpt : SELF;
@@ -401,7 +546,7 @@ PRIVATE int copy_fdsets(struct selectentry *se, int nfds, int direction)
   dst_fds = (direction == FROM_PROC) ? &se->readfds : se->vir_readfds;
   if (se->vir_readfds) {
        r = sys_vircopy(src_e, D, (vir_bytes) src_fds, dst_e, D,
-                   (vir_bytes) dst_fds, fd_setsize);
+                       (vir_bytes) dst_fds, fd_setsize);
        if (r != OK) return(r);
   }
 
@@ -410,7 +555,7 @@ PRIVATE int copy_fdsets(struct selectentry *se, int nfds, int direction)
   dst_fds = (direction == FROM_PROC) ? &se->writefds : se->vir_writefds;
   if (se->vir_writefds) {
        r = sys_vircopy(src_e, D, (vir_bytes) src_fds, dst_e, D,
-                   (vir_bytes) dst_fds, fd_setsize);
+                       (vir_bytes) dst_fds, fd_setsize);
        if (r != OK) return(r);
   }
 
@@ -419,7 +564,7 @@ PRIVATE int copy_fdsets(struct selectentry *se, int nfds, int direction)
   dst_fds = (direction == FROM_PROC) ? &se->errorfds : se->vir_errorfds;
   if (se->vir_errorfds) {
        r = sys_vircopy(src_e, D, (vir_bytes) src_fds, dst_e, D,
-                   (vir_bytes) dst_fds, fd_setsize);
+                       (vir_bytes) dst_fds, fd_setsize);
        if (r != OK) return(r);
   }
 
@@ -430,221 +575,114 @@ PRIVATE int copy_fdsets(struct selectentry *se, int nfds, int direction)
 /*===========================================================================*
  *                             select_cancel_all                            *
  *===========================================================================*/
-PRIVATE void select_cancel_all(struct selectentry *e)
-{
-       int fd;
-
-       e->requestor = NULL;
-       for(fd = 0; fd < e->nfds; fd++) {
-               struct filp *fp;
-               fp = e->filps[fd];
-               if (!fp) {
-#if DEBUG_SELECT
-                       printf("[ fd %d/%d NULL ] ", fd, e->nfds);
-#endif
-                       continue;
-               }
-               if (fp->filp_selectors < 1) {
-#if DEBUG_SELECT
-                       printf("select: %d selectors?!\n", fp->filp_selectors);
-#endif
-                       continue;
-               }
-               fp->filp_selectors--;
-               e->filps[fd] = NULL;
-               select_reevaluate(fp);
-       }
-
-       if (e->expiry > 0) {
-#if DEBUG_SELECT
-               printf("cancelling timer %d\n", e - selecttab);
-#endif
-               cancel_timer(&e->timer); 
-               e->expiry = 0;
-       }
-
-       return;
-}
-
-
-/*===========================================================================*
- *                             select_wakeup                                *
- *===========================================================================*/
-PRIVATE void select_wakeup(struct selectentry *e, int r)
+PRIVATE void select_cancel_all(struct selectentry *se)
 {
-  revive(e->req_endpt, r);
-}
+/* Cancel select. Decrease select usage and cancel timer */
 
+  int fd;
+  struct filp *f;
 
-/*===========================================================================*
- *                             select_reevaluate                            *
- *===========================================================================*/
-PRIVATE int select_reevaluate(struct filp *fp)
-{
-       int s, remain_ops = 0, fd;
-
-       if (!fp) {
-               printf("fs: select: reevalute NULL fp\n");
-               return 0;
-       }
+  /* Always await results of asynchronous requests */
+  assert(!is_deferred(se));
 
-       for(s = 0; s < MAXSELECTS; s++) {
-               if (selecttab[s].requestor != NULL) continue;
-               
-               for(fd = 0; fd < selecttab[s].nfds; fd++)
-                       if (fp == selecttab[s].filps[fd]) {
-                               remain_ops |= tab2ops(fd, &selecttab[s]);
-                       }
-       }
+  for (fd = 0; fd < se->nfds; fd++) {
+       if ((f = se->filps[fd]) == NULL) continue;
+       se->filps[fd] = NULL;
+       select_cancel_filp(f);
+  }
 
-       /* If there are any select()s open that want any operations on
-        * this fd that haven't been satisfied by this callback, then we're
-        * still in the market for it.
-        */
-       fp->filp_select_ops = remain_ops;
-#if DEBUG_SELECT
-       printf("remaining operations on fp are %d\n", fp->filp_select_ops);
-#endif
+  if (se->expiry > 0) {
+       cancel_timer(&se->timer); 
+       se->expiry = 0;
+  }
 
-       return remain_ops;
+  se->requestor = NULL;
 }
 
-
 /*===========================================================================*
- *                             select_return                                *
+ *                             select_cancel_filp                           *
  *===========================================================================*/
-PRIVATE void select_return(struct selectentry *se, int r)
+PRIVATE void select_cancel_filp(struct filp *f)
 {
-  select_cancel_all(se);
-  copy_fdsets(se, se->nfds, TO_PROC); /* FIXME, return error status */
-  select_wakeup(se, r ? r : se->nreadyfds);
+/* Reduce number of select users of this filp */
+
+  assert(f);
+  assert(f->filp_selectors >= 0);
+  if (f->filp_selectors == 0) return;
+
+  f->filp_selectors--;
+  if (f->filp_selectors == 0) { 
+       /* No one selecting on this filp anymore, forget about select state */
+       f->filp_select_ops = 0;
+       f->filp_select_flags = 0;
+       f->filp_pipe_select_ops = 0;
+  }
 }
 
-
 /*===========================================================================*
- *                             select_callback                              *
+ *                             select_return                                *
  *===========================================================================*/
-PUBLIC int select_callback(struct filp *fp, int ops)
+PRIVATE void select_return(struct selectentry *se)
 {
-       int s, fd;
+  int r, r1;
 
-       /* We are being notified that file pointer fp is available for
-        * operations 'ops'. We must re-register the select for
-        * operations that we are still interested in, if any.
-        */
+  assert(!is_deferred(se));    /* Not done yet, first wait for async reply */
 
-       for(s = 0; s < MAXSELECTS; s++) {
-               int wakehim = 0;
-               if (selecttab[s].requestor == NULL) continue;
-               
-               for(fd = 0; fd < selecttab[s].nfds; fd++) {
-                       if (!selecttab[s].filps[fd])
-                               continue;
-                       if (selecttab[s].filps[fd] == fp) {
-                               int this_want_ops;
-                               this_want_ops = tab2ops(fd, &selecttab[s]);
-                               if (this_want_ops & ops) {
-                                       /* this select() has been satisfied. */
-                                       ops2tab(ops, fd, &selecttab[s]);
-                                       wakehim = 1;
-                               }
-                       }
-               }
-               if (wakehim)
-                       select_return(&selecttab[s], 0);
-       }
-
-       return 0;
+  select_cancel_all(se);
+  r1 = copy_fdsets(se, se->nfds, TO_PROC); 
+  if (r1 != OK)
+       r = r1;
+  else if (se->error != OK)
+       r = se->error;
+  else
+       r = se->nreadyfds;
+
+  revive(se->req_endpt, r);
 }
 
 
 /*===========================================================================*
- *                             select_notified                              *
+ *                             select_callback                              *
  *===========================================================================*/
-PUBLIC int select_notified(int major, int minor, int selected_ops)
+PUBLIC void select_callback(struct filp *f, int status)
 {
-       int s, f, t;
-
-#if DEBUG_SELECT
-       printf("select callback: %d, %d: %d\n", major, minor, selected_ops);
-#endif
-
-       for(t = 0; t < SEL_FDS; t++)
-               if (!fdtypes[t].select_match && fdtypes[t].select_major == major)
-                       break;
-
-       if (t >= SEL_FDS) {
-#if DEBUG_SELECT
-               printf("select callback: no fdtype found for device %d\n", major);
-#endif
-               return OK;
-       }
-
-       /* We have a select callback from major device no.
-        * d, which corresponds to our select type t.
-        */
-
-       for(s = 0; s < MAXSELECTS; s++) {
-               int s_minor, ops;
-               if (selecttab[s].requestor == NULL) continue;
-               for(f = 0; f < selecttab[s].nfds; f++) {
-                       if (!selecttab[s].filps[f] ||
-                          !select_major_match(major, selecttab[s].filps[f]))
-                               continue;
-                       ops = tab2ops(f, &selecttab[s]);
-                       s_minor =
-                       (selecttab[s].filps[f]->filp_vno->v_sdev >> MINOR)
-                               & BYTE;
-                       if ((s_minor == minor) &&
-                               (selected_ops & ops)) {
-                               select_callback(selecttab[s].filps[f], (selected_ops & ops));
-                       }
-               }
-       }
-
-       return OK;
+  filp_status(f, status);
 }
 
-
 /*===========================================================================*
  *                             init_select                                  *
  *===========================================================================*/
 PUBLIC void init_select(void)
 {
-       int s;
+  int s;
 
-       for(s = 0; s < MAXSELECTS; s++)
-               init_timer(&selecttab[s].timer);
+  for (s = 0; s < MAXSELECTS; s++)
+       init_timer(&selecttab[s].timer);
 }
 
 
 /*===========================================================================*
  *                             select_forget                                *
  *===========================================================================*/
-PUBLIC void select_forget(int proc_e)
+PUBLIC void select_forget(endpoint_t proc_e)
 {
-       /* something has happened (e.g. signal delivered that interrupts
-        * select()). totally forget about the select().
-        */
-       int s;
+/* Something has happened (e.g. signal delivered that interrupts select()). 
+ * Totally forget about the select(). */
 
-       for(s = 0; s < MAXSELECTS; s++) {
-               if (selecttab[s].requestor != NULL &&
-                   selecttab[s].req_endpt == proc_e) {
-                       break;
-               }
-       }
+  int slot;
+  struct selectentry *se;
 
-       if (s >= MAXSELECTS) {
-#if DEBUG_SELECT
-               printf("select: cancelled select() not found");
-#endif
-               return;
-       }
+  for (slot = 0; slot < MAXSELECTS; slot++) {
+       se = &selecttab[slot];
+       if (se->requestor != NULL && se->req_endpt == proc_e) 
+               break;
+  }
 
-       select_cancel_all(&selecttab[s]);
+  if (slot >= MAXSELECTS) return;      /* Entry not found */
+  se->error = EINTR;
+  if (is_deferred(se)) return;         /* Still awaiting initial reply */
 
-       return;
+  select_cancel_all(se);
 }
 
 
@@ -657,31 +695,14 @@ PUBLIC void select_timeout_check(timer_t *timer)
   struct selectentry *se;
 
   s = tmr_arg(timer)->ta_int;
-  if (s < 0 || s >= MAXSELECTS) {
-#if DEBUG_SELECT
-       printf("select: bogus slot arg to watchdog %d\n", s);
-#endif
-       return;
-  }
-  se = &selecttab[s]; /* Point to select table entry */
-
-  if (se->requestor == NULL) {
-#if DEBUG_SELECT
-       printf("select: no requestor in watchdog\n");
-#endif
-       return;
-  }
-
-  if (se->expiry <= 0) {
-#if DEBUG_SELECT
-       printf("select: strange expiry value in watchdog\n", s);
-#endif
-       return;
-  }
-
+  if (s < 0 || s >= MAXSELECTS) return;        /* Entry does not exist */
+  
+  se = &selecttab[s]; 
+  if (se->requestor == NULL) return;
+  if (se->expiry <= 0) return; /* Strange, did we even ask for a timeout? */
   se->expiry = 0;
-  select_return(se, 0);
-
+  if (is_deferred(se)) return; /* Wait for initial replies to DEV_SELECT */
+  select_return(se);
 }
 
 
@@ -690,22 +711,32 @@ PUBLIC void select_timeout_check(timer_t *timer)
  *===========================================================================*/
 PUBLIC void select_unsuspend_by_endpt(endpoint_t proc_e)
 {
-  int fd, s, maj;
+/* Revive blocked processes when a driver has disappeared */
 
-  for(s = 0; s < MAXSELECTS; s++) {
-       if (selecttab[s].requestor == NULL) continue;
+  int fd, s, major;
+  struct selectentry *se;
+  struct filp *f;
+
+  for (s = 0; s < MAXSELECTS; s++) {
+       int wakehim = 0;
+       se = &selecttab[s];
+       if (se->requestor == NULL) continue;
          
-       for(fd = 0; fd < selecttab[s].nfds; fd++) {
-               if (selecttab[s].filps[fd] == NULL ||
-                   selecttab[s].filps[fd]->filp_vno == NULL) {
+       for (fd = 0; fd < se->nfds; fd++) {
+               if ((f = se->filps[fd]) == NULL || f->filp_vno == NULL) 
                        continue;
+
+               major = major(f->filp_vno->v_sdev);
+               if (dmap_driver_match(proc_e, major)) {
+                       se->filps[fd] = NULL;
+                       se->error = EINTR;
+                       select_cancel_filp(f);
+                       wakehim = 1;
                }
-               
-               maj = (selecttab[s].filps[fd]->filp_vno->v_sdev >> MAJOR)&BYTE;
-               if (dmap_driver_match(proc_e, maj))
-                       select_return(&selecttab[s], EAGAIN);
-           
        }
+
+       if (wakehim && !is_deferred(se))
+               select_return(se);
   }
 }
 
@@ -713,258 +744,257 @@ PUBLIC void select_unsuspend_by_endpt(endpoint_t proc_e)
 /*===========================================================================*
  *                             select_reply1                                *
  *===========================================================================*/
-PUBLIC void select_reply1()
+PUBLIC void select_reply1(driver_e, minor, status)
+endpoint_t driver_e;
+int minor;
+int status;
 {
-       int i, minor, status;
-       endpoint_t driver_e;
-       dev_t dev;
-       struct filp *fp;
-       struct dmap *dp;
-       struct vnode *vp;
-
-       driver_e= m_in.m_source;
-       minor= m_in.DEV_MINOR;
-       status= m_in.DEV_SEL_OPS;
-
-       /* Locate dmap entry */
-       for (i= 0, dp= dmap; i<NR_DEVICES; i++, dp++)
-       {
-               if (dp->dmap_driver == driver_e)
-                       break;
-       }
-       if (i >= NR_DEVICES)
-       {
-               printf("select_reply1: proc %d is not a recoqnized driver\n",
-                       driver_e);
-               return;
-       }
-       dev= (i << MAJOR) | (minor & BYTE);
+/* Handle reply to DEV_SELECT request */
 
-       fp= dp->dmap_sel_filp;
-       if (!fp)
-       {
-               printf("select_reply1: strange, no dmap_sel_filp\n");
-               return;
-       }
+  int major;
+  dev_t dev;
+  struct filp *f;
+  struct dmap *dp;
+  struct vnode *vp;
 
-       if (!(fp->filp_select_flags & FSF_BUSY))
-               panic("select_reply1: strange; not FSF_BUSY");
+  /* Figure out which device is replying */
+  if ((dp = get_dmap(driver_e)) == NULL) {
+       printf("VFS (%s:%d): endpoint %d is not a known driver endpoint\n",
+               __FILE__, __LINE__, driver_e);
+       return;
+  }
+  major = dp-dmap;
+  dev = makedev(major, minor);
 
-       vp= fp->filp_vno;
-       if (!vp)
-               panic("select_reply1: FSF_BUSY but no vp");
+  /* Get filp belonging to character special file */
+  if ((f = dp->dmap_sel_filp) == NULL) {
+       printf("VFS (%s:%d): major %d was not expecting a DEV_SELECT reply\n",
+               __FILE__, __LINE__, major);
+       return;
+  }
 
-       if ((vp->v_mode & I_TYPE) != I_CHAR_SPECIAL) {
-               panic("select_reply1: FSF_BUSY but not char special");
-       }
+  /* The filp must be busy waiting for a reply. */
+  assert(f->filp_select_flags & FSF_BUSY);
+  assert(f->filp_count >= 1); /* There should be at least one owner */
+
+  /* Find vnode and check we got a reply from the device we expected */
+  vp = f->filp_vno;
+  assert(vp != NULL);
+  assert((vp->v_mode & I_TYPE) == I_CHAR_SPECIAL); /* Must be char. special */
+  if (vp->v_sdev != dev) {
+       printf("VFS (%s:%d): expected reply from dev %d not %d\n",
+               vp->v_sdev, dev);
+       return;
+  }
 
-       if (vp->v_sdev != dev)
-       {
-               printf("select_reply1: strange, reply from wrong dev\n");
-               return;
-       }
+  /* No longer waiting for a reply from this device */
+  f->filp_select_flags &= ~FSF_BUSY;
+  dp->dmap_sel_filp = NULL;
+
+  /* The select call is done now, except when 
+   * - another process started a select on the same filp with possibly a
+   *   different set of operations.
+   * - a process does a select on the same filp but using different file
+   *   descriptors.
+   * - the select has a timeout. Upon receiving this reply the operations might
+   *   not be ready yet, so we want to wait for that to ultimately happen.
+   *   Therefore we need to keep remembering what the operations are. */
+  if (!(f->filp_select_flags & (FSF_UPDATE|FSF_BLOCKED)))
+       f->filp_select_ops = 0;         /* done selecting */
+  else if (!(f->filp_select_flags & FSF_UPDATE)) 
+       f->filp_select_ops &= ~status;  /* there may be operations pending */
+
+  /* Tell filp owners about result unless we need to wait longer */
+  if (!(status == 0 && (f->filp_select_flags & FSF_BLOCKED))) {
+       if (status > 0) {       /* operations ready */
+               if (status & SEL_RD) f->filp_select_flags &= ~FSF_RD_BLOCK;
+               if (status & SEL_WR) f->filp_select_flags &= ~FSF_WR_BLOCK;
+               if (status & SEL_ERR) f->filp_select_flags &= ~FSF_ERR_BLOCK;
+       } else if (status < 0)  /* error */
+               f->filp_select_flags &= ~FSF_BLOCKED; /* No longer blocking */
+       filp_status(f, status); /* Tell filp owners about the results */
+  }
 
-       dp->dmap_sel_filp= NULL;
-       fp->filp_select_flags &= ~FSF_BUSY;
-       if (!(fp->filp_select_flags & (FSF_UPDATE|FSF_BLOCK)))
-       fp->filp_select_ops= 0;
-
-       if (status > 0)
-       {
-               /* Clear the replied bits from the request mask unless
-                * FSF_UPDATE is set.
-                */
-               if (!(fp->filp_select_flags & FSF_UPDATE))
-                       fp->filp_select_ops &= ~status;
-       }
-       filp_status(fp, status);
-
-       if (fp->filp_count > 1)
-               fp->filp_count--;
-       else
-       {
-               if (fp->filp_count != 1) {
-                       panic("select_reply1: bad filp_count: %d", fp->filp_count);
-               }
-               close_filp(fp);
-       }
-       sel_restart_dev();
+  select_restart_filps();
 }
 
 
 /*===========================================================================*
  *                             select_reply2                                *
  *===========================================================================*/
-PUBLIC void select_reply2()
+PUBLIC void select_reply2(driver_e, minor, status)
+endpoint_t driver_e;
+int minor;
+int status;
 {
-       int i, s, minor, status;
-       endpoint_t driver_e;
-       dev_t dev;
-       struct filp *fp;
-       struct dmap *dp;
-       struct vnode *vp;
-
-       driver_e= m_in.m_source;
-       minor= m_in.DEV_MINOR;
-       status= m_in.DEV_SEL_OPS;
-
-       /* Locate dmap entry */
-       for (i= 0, dp= dmap; i<NR_DEVICES; i++, dp++)
-       {
-               if (dp->dmap_driver == driver_e)
-                       break;
-       }
-       if (i >= NR_DEVICES)
-       {
-               printf("select_reply2: proc %d is not a recognized driver\n",
-                       driver_e);
-               return;
-       }
-       dev= (i << MAJOR) | (minor & BYTE);
+/* Handle secondary reply to DEV_SELECT request. A secondary reply occurs when
+ * the select request is 'blocking' until an operation becomes ready. */
+  int major, slot, fd;
+  dev_t dev;
+  struct filp *f;
+  struct dmap *dp;
+  struct vnode *vp;
+  struct selectentry *se;
 
-       /* Find filedescriptors for this device */
-       for (s= 0; s<MAXSELECTS; s++)
-       {
-               if (selecttab[s].requestor == NULL)
-                       continue;       /* empty slot */
+  if (status == 0) {
+       printf("VFS (%s:%d): weird status (%d) to report\n", 
+               __FILE__, __LINE__, status);
+       return;
+  }
+
+  /* Figure out which device is replying */
+  if ((dp = get_dmap(driver_e)) == NULL) {
+       printf("VFS (%s:%d): endpoint %d is not a known driver endpoint\n",
+               __FILE__, __LINE__, driver_e);
+       return;
+  }
+  major = dp-dmap;
+  dev = makedev(major, minor);
+
+  /* Find all file descriptors selecting for this device */
+  for (slot = 0; slot < MAXSELECTS; slot++) {
+       se = &selecttab[slot];
+       if (se->requestor == NULL) continue;    /* empty slot */
                                         
-               for (i= 0; i<OPEN_MAX; i++)
-               {
-                       fp= selecttab[s].filps[i];
-                       if (!fp)
-                               continue;
-                       vp= fp->filp_vno;
-                       if (!vp)
-                               continue;
-                       if ((vp->v_mode & I_TYPE) != I_CHAR_SPECIAL)
-                               continue;
-
-                       if (vp->v_sdev != dev)
-                               continue;
-
-                       if (status < 0)
-                       {
-                               printf("select_reply2: should handle error\n");
-                       }
-                       else 
-                       {
-                               /* Clear the replied bits from the request
-                                * mask unless FSF_UPDATE is set.
-                                */
-                               if (!(fp->filp_select_flags & FSF_UPDATE))
-                                       fp->filp_select_ops &= ~status;
-                               ops2tab(status, i, &selecttab[s]);
-                       }
+       for (fd = 0; fd < se->nfds; fd++) {
+               if ((f = se->filps[fd]) == NULL) continue;
+               if ((vp = f->filp_vno) == NULL) continue;
+               if ((vp->v_mode & I_TYPE) != I_CHAR_SPECIAL) continue;
+               if (vp->v_sdev != dev) continue;
+               if (status > 0) {       /* Operations ready */
+                       /* Clear the replied bits from the request
+                        * mask unless FSF_UPDATE is set.
+                        */
+                       if (!(f->filp_select_flags & FSF_UPDATE)) 
+                               f->filp_select_ops &= ~status;
+                       if (status & SEL_RD)
+                               f->filp_select_flags &= ~FSF_RD_BLOCK;
+                       if (status & SEL_WR)
+                               f->filp_select_flags &= ~FSF_WR_BLOCK;
+                       if (status & SEL_ERR)
+                               f->filp_select_flags &= ~FSF_ERR_BLOCK;
+
+                       ops2tab(status, fd, se);
+               } else {
+                       f->filp_select_flags &= ~FSF_BLOCKED; 
+                       ops2tab(SEL_RD|SEL_WR|SEL_ERR, fd, se);
                }
-               if (selecttab[s].nreadyfds > 0)
-                       restart_proc(s);
+
+               if (se->nreadyfds > 0) restart_proc(se);
        }
-}
+  }
 
+  select_restart_filps();
+}
 
-PRIVATE void sel_restart_dev()
+/*===========================================================================*
+ *                             select_restart_filps                         *
+ *===========================================================================*/
+PRIVATE void select_restart_filps()
 {
-       int i, s;
-       struct filp *fp;
-       struct vnode *vp;
-       struct dmap *dp;
-
-       /* Locate filps that can be restarted */
-       for (s= 0; s<MAXSELECTS; s++)
-       {
-               if (selecttab[s].requestor == NULL)
-                       continue; /* empty slot */
-               if (!selecttab[s].deferred)
-                       continue;       /* process is not waiting for an
-                                        * initial reply.
-                                        */
-               for (i= 0; i<OPEN_MAX; i++)
-               {
-                       fp= selecttab[s].filps[i];
-                       if (!fp)
-                               continue;
-                       if (fp->filp_select_flags & FSF_BUSY)
-                               continue;
-                       if (!(fp->filp_select_flags & FSF_UPDATE))
-                               continue;
-
-                       vp= fp->filp_vno;
-                       if (!vp) {
-                               panic("sel_restart_dev: FSF_UPDATE but no vp");
-                       }
-                       if ((vp->v_mode & I_TYPE) != I_CHAR_SPECIAL) {
-                               panic("sel_restart_dev: FSF_UPDATE but not char special");
-                       }
-
-                       dp = &dmap[((vp->v_sdev) >> MAJOR) & BYTE];
-                       if (dp->dmap_sel_filp)
-                               continue;
-
-                       printf(
-                       "sel_restart_dev: should consider fd %d in slot %d\n",
-                               i, s);
+  int fd, slot;
+  struct filp *f;
+  struct vnode *vp;
+  struct selectentry *se;
+
+  /* Locate filps that can be restarted */
+  for (slot = 0; slot < MAXSELECTS; slot++) {
+       se = &selecttab[slot];
+       if (se->requestor == NULL) continue; /* empty slot */
+
+       /* Only 'deferred' processes are eligible to restart */
+       if (!is_deferred(se)) continue;
+
+       /* Find filps that are not waiting for a reply, but have an updated
+        * status (i.e., another select on the same filp with possibly a 
+        * different set of operations is to be done), and thus requires the
+        * select request to be sent again).
+        */
+       for (fd = 0; fd < se->nfds; fd++) {
+               int r, type, wantops, ops, block;
+               if ((f = se->filps[fd]) == NULL) continue;
+               if (f->filp_select_flags & FSF_BUSY) /* Still waiting for */
+                       continue;                    /* initial reply */
+               if (!(f->filp_select_flags & FSF_UPDATE)) /* Must be in  */
+                       continue;                         /* 'update' state */
+
+               vp = f->filp_vno;
+               assert(vp != NULL);
+               assert((vp->v_mode & I_TYPE) == I_CHAR_SPECIAL); 
+               wantops = ops = f->filp_select_ops;
+               r = do_select_request(se, fd, &wantops);
+               if (r != SEL_OK) {
+                       if (r == SEL_DEFERRED) continue;
+                       else break; /* Error or bogus return code; abort */
                }
+               if (wantops & ops) ops2tab(wantops, fd, se);
        }
+  }
 }
 
+/*===========================================================================*
+ *                             do_select_request                            *
+ *===========================================================================*/
+PRIVATE int do_select_request(se, fd, ops)
+struct selectentry *se;
+int fd;
+int *ops;
+{
+/* Perform actual select request for file descriptor fd */
+
+  int r, type, block;
+  struct filp *f;
+
+  type = se->type[fd];
+  block = se->block;
+  f = se->filps[fd];
+  r = fdtypes[type].select_request(f, ops, block);
+  if (r != SEL_OK && r != SEL_DEFERRED) {
+       se->error = EINTR;
+       se->block = 0;  /* Stop blocking to return asap */
+       select_cancel_all(se);
+  }
+
+  return(r);
+}
 
-PRIVATE void filp_status(fp, status)
-struct filp *fp;
+/*===========================================================================*
+ *                             filp_status                                  *
+ *===========================================================================*/
+PRIVATE void filp_status(f, status)
+struct filp *f;
 int status;
 {
-       int i, s;
-
-       /* Locate processes that need to know about this result */
-       for (s= 0; s<MAXSELECTS; s++)
-       {
-               if (selecttab[s].requestor == NULL) continue; /* empty slot */
-
-               for (i= 0; i<OPEN_MAX; i++)
-               {
-                       if (selecttab[s].filps[i] != fp)
-                               continue;
-
-                       if (status < 0)
-                       {
-                               printf("filp_status: should handle error\n");
-                       }
-                       else 
-                               ops2tab(status, i, &selecttab[s]);
-
-                       restart_proc(s);
-               }
+/* Tell processes that need to know about the status of this filp */
+  int fd, slot;
+  struct selectentry *se;
+
+  for (slot = 0; slot < MAXSELECTS; slot++) {
+       se = &selecttab[slot];
+       if (se->requestor == NULL) continue; /* empty slot */
+
+       for (fd = 0; fd < se->nfds; fd++) {
+               if (se->filps[fd] != f) continue;
+               if (status < 0)
+                       ops2tab(SEL_RD|SEL_WR|SEL_ERR, fd, se);
+               else 
+                       ops2tab(status, fd, se);
+               restart_proc(se);
        }
+  }
 }
 
-
-PRIVATE void restart_proc(slot)
-int slot;
+/*===========================================================================*
+ *                             restart_proc                                 *
+ *===========================================================================*/
+PRIVATE void restart_proc(se)
+struct selectentry *se;
 {
-       int fd;
-       struct selectentry *se;
-       struct filp *fp;
-
-       se= &selecttab[slot];
-       if (se->deferred)
-       {
-               for (fd= se->deferred_fd; fd < OPEN_MAX; fd++)
-               {
-                       fp= se->filps[fd];
-                       if (!fp)
-                               continue;
-                       if (fp->filp_select_flags & (FSF_UPDATE|FSF_BUSY))
-                               break;
-               }
-               if (fd < OPEN_MAX)
-               {
-                       se->deferred_fd= fd;
-                       return;
-               }
-               se->deferred= FALSE;
-       }
-       if (se->nreadyfds > 0 || !se->block) {
-               select_return(se, 0);
-       }
+/* Tell process about select results (if any) unless there are still results
+ * pending. */
+
+  if ((se->nreadyfds > 0 || !se->block) && !is_deferred(se)) 
+       select_return(se);
 }
 
 /*===========================================================================*
@@ -972,10 +1002,9 @@ int slot;
  *===========================================================================*/
 PRIVATE void wipe_select(struct selectentry *se)
 {
-  se->deferred = FALSE;
   se->nfds = 0;
   se->nreadyfds = 0;
-/*  memset(se->filps, 0, OPEN_MAX * sizeof(struct filp *)); */
+  se->error = OK;
   memset(se->filps, 0, sizeof(se->filps)); 
 
   FD_ZERO(&se->readfds);