From: Thomas Veerman Date: Wed, 13 Apr 2011 13:25:34 +0000 (+0000) Subject: Clean up and fix multiple bugs in select: X-Git-Tag: v3.2.0~579 X-Git-Url: http://zhaoyanbai.com/repos/doc/mandoc_char.7.html?a=commitdiff_plain;h=aba392e630a2e68cad903b3750ffe9e55a93a206;p=minix.git Clean up and fix multiple bugs in select: - 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). --- diff --git a/servers/vfs/device.c b/servers/vfs/device.c index 3bd48eacd..8e918482c 100644 --- a/servers/vfs/device.c +++ b/servers/vfs/device.c @@ -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 " diff --git a/servers/vfs/dmap.c b/servers/vfs/dmap.c index 8048c4db1..cae93fa34 100644 --- a/servers/vfs/dmap.c +++ b/servers/vfs/dmap.c @@ -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); +} + diff --git a/servers/vfs/dmap.h b/servers/vfs/dmap.h index ccf53e30a..1430d5287 100644 --- a/servers/vfs/dmap.h +++ b/servers/vfs/dmap.h @@ -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 diff --git a/servers/vfs/file.h b/servers/vfs/file.h index 8ac8d09ba..fe3b99971 100644 --- a/servers/vfs/file.h +++ b/servers/vfs/file.h @@ -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 diff --git a/servers/vfs/main.c b/servers/vfs/main.c index 6317886c0..5ee8ec302 100644 --- a/servers/vfs/main.c +++ b/servers/vfs/main.c @@ -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; } diff --git a/servers/vfs/pipe.c b/servers/vfs/pipe.c index 1491342a4..c5183b337 100644 --- a/servers/vfs/pipe.c +++ b/servers/vfs/pipe.c @@ -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; diff --git a/servers/vfs/proto.h b/servers/vfs/proto.h index 1cb17b9ca..443e135d3 100644 --- a/servers/vfs/proto.h +++ b/servers/vfs/proto.h @@ -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) ); diff --git a/servers/vfs/select.c b/servers/vfs/select.c index 96c2c54d0..653929fa1 100644 --- a/servers/vfs/select.c +++ b/servers/vfs/select.c @@ -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 #include #include #include #include +#include + +#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 @@ -27,15 +27,14 @@ 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; idmap_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; idmap_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; srequestor == NULL) continue; /* empty slot */ - for (i= 0; ifilp_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; sfilp_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; srequestor == 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);