xref: /minix3/minix/servers/vfs/select.c (revision 722cbc6186261c8af242e20abae1d1fd67d6e387)
1433d6423SLionel Sambuc /* Implement entry point to select system call.
2433d6423SLionel Sambuc  *
3433d6423SLionel Sambuc  * The entry points into this file are
4433d6423SLionel Sambuc  *   do_select:	       perform the SELECT system call
5433d6423SLionel Sambuc  *   select_callback:  notify select system of possible fd operation
6433d6423SLionel Sambuc  *   select_unsuspend_by_endpt: cancel a blocking select on exiting driver
7433d6423SLionel Sambuc  *
8433d6423SLionel Sambuc  * The select code uses minimal locking, so that the replies from character
9433d6423SLionel Sambuc  * drivers can be processed without blocking. Filps are locked only for pipes.
10433d6423SLionel Sambuc  * We make the assumption that any other structures and fields are safe to
11433d6423SLionel Sambuc  * check (and possibly change) as long as we know that a process is blocked on
12433d6423SLionel Sambuc  * a select(2) call, meaning that all involved filps are guaranteed to stay
13433d6423SLionel Sambuc  * open until either we finish the select call, it the process gets interrupted
14433d6423SLionel Sambuc  * by a signal.
15433d6423SLionel Sambuc  */
16433d6423SLionel Sambuc 
17433d6423SLionel Sambuc #include "fs.h"
18433d6423SLionel Sambuc #include <sys/fcntl.h>
19433d6423SLionel Sambuc #include <sys/time.h>
20433d6423SLionel Sambuc #include <sys/select.h>
21433d6423SLionel Sambuc #include <sys/stat.h>
22433d6423SLionel Sambuc #include <minix/callnr.h>
23433d6423SLionel Sambuc #include <minix/u64.h>
24433d6423SLionel Sambuc #include <string.h>
25433d6423SLionel Sambuc #include <assert.h>
26433d6423SLionel Sambuc 
27433d6423SLionel Sambuc #include "file.h"
28433d6423SLionel Sambuc #include "vnode.h"
29433d6423SLionel Sambuc 
30433d6423SLionel Sambuc /* max. number of simultaneously pending select() calls */
31433d6423SLionel Sambuc #define MAXSELECTS 25
32433d6423SLionel Sambuc #define FROM_PROC 0
33433d6423SLionel Sambuc #define TO_PROC   1
34433d6423SLionel Sambuc 
35cfd712b4SDavid van Moolenbroek #define USECPERSEC 1000000	/* number of microseconds in a second */
36cfd712b4SDavid van Moolenbroek 
3750b7f13fSCristiano Giuffrida typedef fd_set *ixfer_fd_set_ptr;
3850b7f13fSCristiano Giuffrida 
39433d6423SLionel Sambuc static struct selectentry {
40433d6423SLionel Sambuc   struct fproc *requestor;	/* slot is free iff this is NULL */
41433d6423SLionel Sambuc   endpoint_t req_endpt;
42433d6423SLionel Sambuc   fd_set readfds, writefds, errorfds;
43433d6423SLionel Sambuc   fd_set ready_readfds, ready_writefds, ready_errorfds;
4450b7f13fSCristiano Giuffrida   ixfer_fd_set_ptr vir_readfds, vir_writefds, vir_errorfds;
45433d6423SLionel Sambuc   struct filp *filps[OPEN_MAX];
46433d6423SLionel Sambuc   int type[OPEN_MAX];
47433d6423SLionel Sambuc   int nfds, nreadyfds;
48433d6423SLionel Sambuc   int error;
49433d6423SLionel Sambuc   char block;
50433d6423SLionel Sambuc   char starting;
51433d6423SLionel Sambuc   clock_t expiry;
52433d6423SLionel Sambuc   minix_timer_t timer;	/* if expiry > 0 */
53433d6423SLionel Sambuc } selecttab[MAXSELECTS];
54433d6423SLionel Sambuc 
55433d6423SLionel Sambuc static int copy_fdsets(struct selectentry *se, int nfds, int direction);
56433d6423SLionel Sambuc static void filp_status(struct filp *fp, int status);
57433d6423SLionel Sambuc static int is_deferred(struct selectentry *se);
58433d6423SLionel Sambuc static void restart_proc(struct selectentry *se);
59433d6423SLionel Sambuc static void ops2tab(int ops, int fd, struct selectentry *e);
60433d6423SLionel Sambuc static int is_regular_file(struct filp *f);
61433d6423SLionel Sambuc static int is_pipe(struct filp *f);
62433d6423SLionel Sambuc static int is_char_device(struct filp *f);
63e3b8d4bbSDavid van Moolenbroek static int is_sock_device(struct filp *f);
64433d6423SLionel Sambuc static void select_lock_filp(struct filp *f, int ops);
6527d0ecdbSDavid van Moolenbroek static int select_request_file(struct filp *f, int *ops, int block,
6627d0ecdbSDavid van Moolenbroek 	struct fproc *rfp);
6727d0ecdbSDavid van Moolenbroek static int select_request_char(struct filp *f, int *ops, int block,
6827d0ecdbSDavid van Moolenbroek 	struct fproc *rfp);
69e3b8d4bbSDavid van Moolenbroek static int select_request_sock(struct filp *f, int *ops, int block,
70e3b8d4bbSDavid van Moolenbroek 	struct fproc *rfp);
7127d0ecdbSDavid van Moolenbroek static int select_request_pipe(struct filp *f, int *ops, int block,
7227d0ecdbSDavid van Moolenbroek 	struct fproc *rfp);
73433d6423SLionel Sambuc static void select_cancel_all(struct selectentry *e);
74433d6423SLionel Sambuc static void select_cancel_filp(struct filp *f);
75433d6423SLionel Sambuc static void select_return(struct selectentry *);
76433d6423SLionel Sambuc static void select_restart_filps(void);
77433d6423SLionel Sambuc static int tab2ops(int fd, struct selectentry *e);
78433d6423SLionel Sambuc static void wipe_select(struct selectentry *s);
79cfd712b4SDavid van Moolenbroek void select_timeout_check(int s);
80433d6423SLionel Sambuc 
81433d6423SLionel Sambuc static struct fdtype {
8227d0ecdbSDavid van Moolenbroek 	int (*select_request)(struct filp *, int *ops, int block,
8327d0ecdbSDavid van Moolenbroek 		struct fproc *rfp);
84433d6423SLionel Sambuc 	int (*type_match)(struct filp *f);
85433d6423SLionel Sambuc } fdtypes[] = {
86433d6423SLionel Sambuc 	{ select_request_char, is_char_device },
87e3b8d4bbSDavid van Moolenbroek 	{ select_request_sock, is_sock_device },
88433d6423SLionel Sambuc 	{ select_request_file, is_regular_file },
89433d6423SLionel Sambuc 	{ select_request_pipe, is_pipe },
90433d6423SLionel Sambuc };
91433d6423SLionel Sambuc #define SEL_FDS		(sizeof(fdtypes) / sizeof(fdtypes[0]))
92433d6423SLionel Sambuc 
93433d6423SLionel Sambuc /*===========================================================================*
94433d6423SLionel Sambuc  *				do_select				     *
95433d6423SLionel Sambuc  *===========================================================================*/
do_select(void)96433d6423SLionel Sambuc int do_select(void)
97433d6423SLionel Sambuc {
98433d6423SLionel Sambuc /* Implement the select(nfds, readfds, writefds, errorfds, timeout) system
99433d6423SLionel Sambuc  * call. First we copy the arguments and verify their sanity. Then we check
100433d6423SLionel Sambuc  * whether there are file descriptors that satisfy the select call right off
101433d6423SLionel Sambuc  * the bat. If so, or if there are no ready file descriptors but the process
102433d6423SLionel Sambuc  * requested to return immediately, we return the result. Otherwise we set a
103433d6423SLionel Sambuc  * timeout and wait for either the file descriptors to become ready or the
104433d6423SLionel Sambuc  * timer to go off. If no timeout value was provided, we wait indefinitely.
105433d6423SLionel Sambuc  */
106*722cbc61SDavid van Moolenbroek   int r, nfds, do_timeout, fd, type, s;
107433d6423SLionel Sambuc   struct filp *f;
108*722cbc61SDavid van Moolenbroek   unsigned int ops;
109433d6423SLionel Sambuc   struct timeval timeout;
110433d6423SLionel Sambuc   struct selectentry *se;
111433d6423SLionel Sambuc   vir_bytes vtimeout;
112cfd712b4SDavid van Moolenbroek   clock_t ticks;
113433d6423SLionel Sambuc 
114433d6423SLionel Sambuc   nfds = job_m_in.m_lc_vfs_select.nfds;
115433d6423SLionel Sambuc   vtimeout = job_m_in.m_lc_vfs_select.timeout;
116433d6423SLionel Sambuc 
117433d6423SLionel Sambuc   /* Sane amount of file descriptors? */
118433d6423SLionel Sambuc   if (nfds < 0 || nfds > OPEN_MAX) return(EINVAL);
119433d6423SLionel Sambuc 
120433d6423SLionel Sambuc   /* Find a slot to store this select request */
121433d6423SLionel Sambuc   for (s = 0; s < MAXSELECTS; s++)
122433d6423SLionel Sambuc 	if (selecttab[s].requestor == NULL) /* Unused slot */
123433d6423SLionel Sambuc 		break;
124433d6423SLionel Sambuc   if (s >= MAXSELECTS) return(ENOSPC);
125433d6423SLionel Sambuc 
126433d6423SLionel Sambuc   se = &selecttab[s];
127433d6423SLionel Sambuc   wipe_select(se);	/* Clear results of previous usage */
128433d6423SLionel Sambuc   se->requestor = fp;
129433d6423SLionel Sambuc   se->req_endpt = who_e;
130433d6423SLionel Sambuc   se->vir_readfds = job_m_in.m_lc_vfs_select.readfds;
131433d6423SLionel Sambuc   se->vir_writefds = job_m_in.m_lc_vfs_select.writefds;
132433d6423SLionel Sambuc   se->vir_errorfds = job_m_in.m_lc_vfs_select.errorfds;
133433d6423SLionel Sambuc 
134433d6423SLionel Sambuc   /* Copy fdsets from the process */
135433d6423SLionel Sambuc   if ((r = copy_fdsets(se, nfds, FROM_PROC)) != OK) {
136433d6423SLionel Sambuc 	se->requestor = NULL;
137433d6423SLionel Sambuc 	return(r);
138433d6423SLionel Sambuc   }
139433d6423SLionel Sambuc 
140433d6423SLionel Sambuc   /* Did the process set a timeout value? If so, retrieve it. */
141433d6423SLionel Sambuc   if (vtimeout != 0) {
142433d6423SLionel Sambuc 	r = sys_datacopy_wrapper(who_e, vtimeout, SELF, (vir_bytes) &timeout,
143433d6423SLionel Sambuc 		sizeof(timeout));
144cfd712b4SDavid van Moolenbroek 
145cfd712b4SDavid van Moolenbroek 	/* No nonsense in the timeval */
146cfd712b4SDavid van Moolenbroek 	if (r == OK && (timeout.tv_sec < 0 || timeout.tv_usec < 0 ||
147cfd712b4SDavid van Moolenbroek 	    timeout.tv_usec >= USECPERSEC))
148cfd712b4SDavid van Moolenbroek 		r = EINVAL;
149cfd712b4SDavid van Moolenbroek 
150433d6423SLionel Sambuc 	if (r != OK) {
151433d6423SLionel Sambuc 		se->requestor = NULL;
152433d6423SLionel Sambuc 		return(r);
153433d6423SLionel Sambuc 	}
154cfd712b4SDavid van Moolenbroek 	do_timeout = 1;
155cfd712b4SDavid van Moolenbroek   } else
156cfd712b4SDavid van Moolenbroek 	do_timeout = 0;
157433d6423SLionel Sambuc 
158433d6423SLionel Sambuc   /* If there is no timeout, we block forever. Otherwise, we block up to the
159433d6423SLionel Sambuc    * specified time interval.
160433d6423SLionel Sambuc    */
161433d6423SLionel Sambuc   if (!do_timeout)	/* No timeout value set */
162433d6423SLionel Sambuc 	se->block = 1;
163433d6423SLionel Sambuc   else if (do_timeout && (timeout.tv_sec > 0 || timeout.tv_usec > 0))
164433d6423SLionel Sambuc 	se->block = 1;
165433d6423SLionel Sambuc   else			/* timeout set as (0,0) - this effects a poll */
166433d6423SLionel Sambuc 	se->block = 0;
167433d6423SLionel Sambuc   se->expiry = 0;	/* no timer set (yet) */
168433d6423SLionel Sambuc 
169433d6423SLionel Sambuc   /* We are going to lock filps, and that means that while locking a second
170433d6423SLionel Sambuc    * filp, we might already get the results for the first one. In that case,
171433d6423SLionel Sambuc    * the incoming results must not cause the select call to finish prematurely.
172433d6423SLionel Sambuc    */
173433d6423SLionel Sambuc   se->starting = TRUE;
174433d6423SLionel Sambuc 
175433d6423SLionel Sambuc   /* Verify that file descriptors are okay to select on */
176433d6423SLionel Sambuc   for (fd = 0; fd < nfds; fd++) {
177433d6423SLionel Sambuc 	/* Because the select() interface implicitly includes file descriptors
178433d6423SLionel Sambuc 	 * you might not want to select on, we have to figure out whether we're
179433d6423SLionel Sambuc 	 * interested in them. Typically, these file descriptors include fd's
180433d6423SLionel Sambuc 	 * inherited from the parent proc and file descriptors that have been
181433d6423SLionel Sambuc 	 * close()d, but had a lower fd than one in the current set.
182433d6423SLionel Sambuc 	 */
183433d6423SLionel Sambuc 	if (!(ops = tab2ops(fd, se)))
184433d6423SLionel Sambuc 		continue; /* No operations set; nothing to do for this fd */
185433d6423SLionel Sambuc 
186*722cbc61SDavid van Moolenbroek 	/* Get filp belonging to this fd. If this fails, there are two causes:
187*722cbc61SDavid van Moolenbroek 	 * either the given file descriptor was bad, or the associated filp is
188*722cbc61SDavid van Moolenbroek 	 * closed (in the FILP_CLOSED sense) as a result of invalidation. Only
189*722cbc61SDavid van Moolenbroek 	 * the former is a select error. The latter should result in operations
190*722cbc61SDavid van Moolenbroek 	 * being returned as ready on the file descriptor, since subsequent
191*722cbc61SDavid van Moolenbroek 	 * I/O calls are guaranteed to return I/O errors on such descriptors.
192*722cbc61SDavid van Moolenbroek 	 */
193433d6423SLionel Sambuc 	f = se->filps[fd] = get_filp(fd, VNODE_READ);
194*722cbc61SDavid van Moolenbroek 	if (f == NULL && err_code != EIO) {
195*722cbc61SDavid van Moolenbroek 		assert(err_code == EBADF);
196433d6423SLionel Sambuc 
197*722cbc61SDavid van Moolenbroek 		/* We may already have adjusted filp_selectors on previous
198*722cbc61SDavid van Moolenbroek 		 * file pointers in the set, so do not simply return here.
199*722cbc61SDavid van Moolenbroek 		 */
200*722cbc61SDavid van Moolenbroek 		se->error = EBADF;
201*722cbc61SDavid van Moolenbroek 		break;
202433d6423SLionel Sambuc 	}
203433d6423SLionel Sambuc 
204433d6423SLionel Sambuc 	/* Check file types. According to POSIX 2008:
205433d6423SLionel Sambuc 	 * "The pselect() and select() functions shall support regular files,
206433d6423SLionel Sambuc 	 * terminal and pseudo-terminal devices, FIFOs, pipes, and sockets. The
207433d6423SLionel Sambuc 	 * behavior of pselect() and select() on file descriptors that refer to
208433d6423SLionel Sambuc 	 * other types of file is unspecified."
209433d6423SLionel Sambuc 	 *
210433d6423SLionel Sambuc 	 * In our case, terminal and pseudo-terminal devices are handled by the
211e3b8d4bbSDavid van Moolenbroek 	 * TTY and PTY character drivers respectively.  Sockets are handled by
212e3b8d4bbSDavid van Moolenbroek 	 * by their respective socket drivers.  Additionally, we give other
213433d6423SLionel Sambuc 	 * character drivers the chance to handle select for any of their
214433d6423SLionel Sambuc 	 * device nodes. Some may not implement support for select and let
215433d6423SLionel Sambuc 	 * libchardriver return EBADF, which we then pass to the calling
216433d6423SLionel Sambuc 	 * process once we receive the reply.
217*722cbc61SDavid van Moolenbroek 	 *
218*722cbc61SDavid van Moolenbroek 	 * If we could not access the file pointer at all, it will have been
219*722cbc61SDavid van Moolenbroek 	 * closed due to invalidation after a service crash. In that case, we
220*722cbc61SDavid van Moolenbroek 	 * skip type matching and simply return pending operations as ready.
221433d6423SLionel Sambuc 	 */
222433d6423SLionel Sambuc 	se->type[fd] = -1;
223*722cbc61SDavid van Moolenbroek 	if (f == NULL)
224*722cbc61SDavid van Moolenbroek 		continue; /* closed, skip type matching */
225433d6423SLionel Sambuc 	for (type = 0; type < SEL_FDS; type++) {
226433d6423SLionel Sambuc 		if (fdtypes[type].type_match(f)) {
227433d6423SLionel Sambuc 			se->type[fd] = type;
228433d6423SLionel Sambuc 			se->nfds = fd+1;
229433d6423SLionel Sambuc 			se->filps[fd]->filp_selectors++;
230433d6423SLionel Sambuc 			break;
231433d6423SLionel Sambuc 		}
232433d6423SLionel Sambuc 	}
233433d6423SLionel Sambuc 	unlock_filp(f);
234433d6423SLionel Sambuc 	if (se->type[fd] == -1) { /* Type not found */
235*722cbc61SDavid van Moolenbroek 		se->error = EBADF;
236*722cbc61SDavid van Moolenbroek 		break;
237433d6423SLionel Sambuc 	}
238433d6423SLionel Sambuc   }
239433d6423SLionel Sambuc 
240*722cbc61SDavid van Moolenbroek   /* If an error occurred already, undo any changes so far and return. */
241*722cbc61SDavid van Moolenbroek   if (se->error != OK) {
242*722cbc61SDavid van Moolenbroek 	select_cancel_all(se);
243*722cbc61SDavid van Moolenbroek 	se->requestor = NULL;
244*722cbc61SDavid van Moolenbroek 	return(se->error);
245*722cbc61SDavid van Moolenbroek   }
246*722cbc61SDavid van Moolenbroek 
247433d6423SLionel Sambuc   /* Check all file descriptors in the set whether one is 'ready' now */
248433d6423SLionel Sambuc   for (fd = 0; fd < nfds; fd++) {
249433d6423SLionel Sambuc 	/* Again, check for involuntarily selected fd's */
250433d6423SLionel Sambuc 	if (!(ops = tab2ops(fd, se)))
251433d6423SLionel Sambuc 		continue; /* No operations set; nothing to do for this fd */
252433d6423SLionel Sambuc 
253433d6423SLionel Sambuc 	/* File descriptors selected for reading that are not opened for
254433d6423SLionel Sambuc 	 * reading should be marked as readable, as read calls would fail
255*722cbc61SDavid van Moolenbroek 	 * immediately. The same applies to writing. For file descriptors for
256*722cbc61SDavid van Moolenbroek 	 * which the file pointer is already closed (f==NULL), return readable
257*722cbc61SDavid van Moolenbroek 	 * and writable operations (if requested) and skip the rest.
258433d6423SLionel Sambuc 	 */
259433d6423SLionel Sambuc 	f = se->filps[fd];
260*722cbc61SDavid van Moolenbroek 	if (f == NULL) {
261*722cbc61SDavid van Moolenbroek 		ops2tab(SEL_RD | SEL_WR, fd, se);
262*722cbc61SDavid van Moolenbroek 		continue;
263*722cbc61SDavid van Moolenbroek 	}
264433d6423SLionel Sambuc 	if ((ops & SEL_RD) && !(f->filp_mode & R_BIT)) {
265433d6423SLionel Sambuc 		ops2tab(SEL_RD, fd, se);
266433d6423SLionel Sambuc 		ops &= ~SEL_RD;
267433d6423SLionel Sambuc 	}
268433d6423SLionel Sambuc 	if ((ops & SEL_WR) && !(f->filp_mode & W_BIT)) {
269433d6423SLionel Sambuc 		ops2tab(SEL_WR, fd, se);
270433d6423SLionel Sambuc 		ops &= ~SEL_WR;
271433d6423SLionel Sambuc 	}
272433d6423SLionel Sambuc 	/* Test filp for select operations if not already done so. e.g.,
273433d6423SLionel Sambuc 	 * processes sharing a filp and both doing a select on that filp. */
274433d6423SLionel Sambuc 	if ((f->filp_select_ops & ops) != ops) {
275433d6423SLionel Sambuc 		int wantops;
276433d6423SLionel Sambuc 
277433d6423SLionel Sambuc 		wantops = (f->filp_select_ops |= ops);
278433d6423SLionel Sambuc 		type = se->type[fd];
279*722cbc61SDavid van Moolenbroek 		assert(type >= 0);
280433d6423SLionel Sambuc 		select_lock_filp(f, wantops);
28127d0ecdbSDavid van Moolenbroek 		r = fdtypes[type].select_request(f, &wantops, se->block, fp);
282433d6423SLionel Sambuc 		unlock_filp(f);
283433d6423SLionel Sambuc 		if (r != OK && r != SUSPEND) {
284433d6423SLionel Sambuc 			se->error = r;
285433d6423SLionel Sambuc 			break; /* Error or bogus return code; abort */
286433d6423SLionel Sambuc 		}
287433d6423SLionel Sambuc 
288433d6423SLionel Sambuc 		/* The select request above might have turned on/off some
289433d6423SLionel Sambuc 		 * operations because they were 'ready' or not meaningful.
290433d6423SLionel Sambuc 		 * Either way, we might have a result and we need to store them
291433d6423SLionel Sambuc 		 * in the select table entry. */
292433d6423SLionel Sambuc 		if (wantops & ops) ops2tab(wantops, fd, se);
293433d6423SLionel Sambuc 	}
294433d6423SLionel Sambuc   }
295433d6423SLionel Sambuc 
296433d6423SLionel Sambuc   /* At this point there won't be any blocking calls anymore. */
297433d6423SLionel Sambuc   se->starting = FALSE;
298433d6423SLionel Sambuc 
299433d6423SLionel Sambuc   if ((se->nreadyfds > 0 || se->error != OK || !se->block) &&
300433d6423SLionel Sambuc 		!is_deferred(se)) {
301433d6423SLionel Sambuc 	/* An error occurred, or fd's were found that were ready to go right
302433d6423SLionel Sambuc 	 * away, and/or we were instructed not to block at all. Must return
303433d6423SLionel Sambuc 	 * immediately. Do not copy FD sets if an error occurred.
304433d6423SLionel Sambuc 	 */
305433d6423SLionel Sambuc 	if (se->error != OK)
306433d6423SLionel Sambuc 		r = se->error;
307433d6423SLionel Sambuc 	else
308433d6423SLionel Sambuc 		r = copy_fdsets(se, se->nfds, TO_PROC);
309433d6423SLionel Sambuc 	select_cancel_all(se);
310433d6423SLionel Sambuc 	se->requestor = NULL;
311433d6423SLionel Sambuc 
312433d6423SLionel Sambuc 	if (r != OK)
313433d6423SLionel Sambuc 		return(r);
314433d6423SLionel Sambuc 	return(se->nreadyfds);
315433d6423SLionel Sambuc   }
316433d6423SLionel Sambuc 
317433d6423SLionel Sambuc   /* Convert timeval to ticks and set the timer. If it fails, undo
318433d6423SLionel Sambuc    * all, return error.
319433d6423SLionel Sambuc    */
320cfd712b4SDavid van Moolenbroek   if (do_timeout && se->block) {
321433d6423SLionel Sambuc 	/* Open Group:
322433d6423SLionel Sambuc 	 * "If the requested timeout interval requires a finer
323433d6423SLionel Sambuc 	 * granularity than the implementation supports, the
324433d6423SLionel Sambuc 	 * actual timeout interval shall be rounded up to the next
325433d6423SLionel Sambuc 	 * supported value."
326433d6423SLionel Sambuc 	 */
327cfd712b4SDavid van Moolenbroek 	if (timeout.tv_sec >= (TMRDIFF_MAX - 1) / system_hz) {
328cfd712b4SDavid van Moolenbroek 		ticks = TMRDIFF_MAX; /* silently truncate */
329cfd712b4SDavid van Moolenbroek 	} else {
330433d6423SLionel Sambuc 		ticks = timeout.tv_sec * system_hz +
331433d6423SLionel Sambuc 		    (timeout.tv_usec * system_hz + USECPERSEC-1) / USECPERSEC;
332cfd712b4SDavid van Moolenbroek 	}
333cfd712b4SDavid van Moolenbroek 	assert(ticks != 0 && ticks <= TMRDIFF_MAX);
334433d6423SLionel Sambuc 	se->expiry = ticks;
335433d6423SLionel Sambuc 	set_timer(&se->timer, ticks, select_timeout_check, s);
336433d6423SLionel Sambuc   }
337433d6423SLionel Sambuc 
338433d6423SLionel Sambuc   /* process now blocked */
339433d6423SLionel Sambuc   suspend(FP_BLOCKED_ON_SELECT);
340433d6423SLionel Sambuc   return(SUSPEND);
341433d6423SLionel Sambuc }
342433d6423SLionel Sambuc 
343433d6423SLionel Sambuc /*===========================================================================*
344433d6423SLionel Sambuc  *				is_deferred				     *
345433d6423SLionel Sambuc  *===========================================================================*/
is_deferred(struct selectentry * se)346433d6423SLionel Sambuc static int is_deferred(struct selectentry *se)
347433d6423SLionel Sambuc {
348433d6423SLionel Sambuc /* Find out whether this select has pending initial replies */
349433d6423SLionel Sambuc 
350433d6423SLionel Sambuc   int fd;
351433d6423SLionel Sambuc   struct filp *f;
352433d6423SLionel Sambuc 
353433d6423SLionel Sambuc   /* The select call must have finished its initialization at all. */
354433d6423SLionel Sambuc   if (se->starting) return(TRUE);
355433d6423SLionel Sambuc 
356433d6423SLionel Sambuc   for (fd = 0; fd < se->nfds; fd++) {
357433d6423SLionel Sambuc 	if ((f = se->filps[fd]) == NULL) continue;
358433d6423SLionel Sambuc 	if (f->filp_select_flags & (FSF_UPDATE|FSF_BUSY)) return(TRUE);
359433d6423SLionel Sambuc   }
360433d6423SLionel Sambuc 
361433d6423SLionel Sambuc   return(FALSE);
362433d6423SLionel Sambuc }
363433d6423SLionel Sambuc 
364433d6423SLionel Sambuc 
365433d6423SLionel Sambuc /*===========================================================================*
366433d6423SLionel Sambuc  *				is_regular_file				     *
367433d6423SLionel Sambuc  *===========================================================================*/
is_regular_file(struct filp * f)368433d6423SLionel Sambuc static int is_regular_file(struct filp *f)
369433d6423SLionel Sambuc {
370433d6423SLionel Sambuc   return(f && f->filp_vno && S_ISREG(f->filp_vno->v_mode));
371433d6423SLionel Sambuc }
372433d6423SLionel Sambuc 
373433d6423SLionel Sambuc /*===========================================================================*
374433d6423SLionel Sambuc  *				is_pipe					     *
375433d6423SLionel Sambuc  *===========================================================================*/
is_pipe(struct filp * f)376433d6423SLionel Sambuc static int is_pipe(struct filp *f)
377433d6423SLionel Sambuc {
378433d6423SLionel Sambuc /* Recognize either anonymous pipe or named pipe (FIFO) */
379433d6423SLionel Sambuc   return(f && f->filp_vno && S_ISFIFO(f->filp_vno->v_mode));
380433d6423SLionel Sambuc }
381433d6423SLionel Sambuc 
382433d6423SLionel Sambuc /*===========================================================================*
383433d6423SLionel Sambuc  *				is_char_device				     *
384433d6423SLionel Sambuc  *===========================================================================*/
is_char_device(struct filp * f)385433d6423SLionel Sambuc static int is_char_device(struct filp *f)
386433d6423SLionel Sambuc {
387433d6423SLionel Sambuc /* See if this filp is a handle on a character device. This function MUST NOT
388433d6423SLionel Sambuc  * block its calling thread. The given filp may or may not be locked.
389433d6423SLionel Sambuc  */
390433d6423SLionel Sambuc 
391433d6423SLionel Sambuc   return (f && f->filp_vno && S_ISCHR(f->filp_vno->v_mode));
392433d6423SLionel Sambuc }
393433d6423SLionel Sambuc 
394433d6423SLionel Sambuc /*===========================================================================*
395e3b8d4bbSDavid van Moolenbroek  *				is_sock_device				     *
396e3b8d4bbSDavid van Moolenbroek  *===========================================================================*/
is_sock_device(struct filp * f)397e3b8d4bbSDavid van Moolenbroek static int is_sock_device(struct filp *f)
398e3b8d4bbSDavid van Moolenbroek {
399e3b8d4bbSDavid van Moolenbroek /* See if this filp is a handle on a socket device. This function MUST NOT
400e3b8d4bbSDavid van Moolenbroek  * block its calling thread. The given filp may or may not be locked.
401e3b8d4bbSDavid van Moolenbroek  */
402e3b8d4bbSDavid van Moolenbroek 
403e3b8d4bbSDavid van Moolenbroek   return (f && f->filp_vno && S_ISSOCK(f->filp_vno->v_mode));
404e3b8d4bbSDavid van Moolenbroek }
405e3b8d4bbSDavid van Moolenbroek 
406e3b8d4bbSDavid van Moolenbroek /*===========================================================================*
407e3b8d4bbSDavid van Moolenbroek  *				select_filter				     *
408e3b8d4bbSDavid van Moolenbroek  *===========================================================================*/
select_filter(struct filp * f,int * ops,int block)409e3b8d4bbSDavid van Moolenbroek static int select_filter(struct filp *f, int *ops, int block)
410e3b8d4bbSDavid van Moolenbroek {
411e3b8d4bbSDavid van Moolenbroek /* Determine which select operations can be satisfied immediately and which
412e3b8d4bbSDavid van Moolenbroek  * should be requested.  Used for character and socket devices.  This function
413e3b8d4bbSDavid van Moolenbroek  * MUST NOT block its calling thread.
414e3b8d4bbSDavid van Moolenbroek  */
415e3b8d4bbSDavid van Moolenbroek   int rops;
416e3b8d4bbSDavid van Moolenbroek 
417e3b8d4bbSDavid van Moolenbroek   rops = *ops;
418e3b8d4bbSDavid van Moolenbroek 
419e3b8d4bbSDavid van Moolenbroek   /* By default, nothing to do */
420e3b8d4bbSDavid van Moolenbroek   *ops = 0;
421e3b8d4bbSDavid van Moolenbroek 
422e3b8d4bbSDavid van Moolenbroek   /*
423e3b8d4bbSDavid van Moolenbroek    * If we have previously asked the driver to notify us about certain ready
424e3b8d4bbSDavid van Moolenbroek    * operations, but it has not notified us yet, then we can safely assume that
425e3b8d4bbSDavid van Moolenbroek    * those operations are not ready right now.  Therefore, if this call is not
426e3b8d4bbSDavid van Moolenbroek    * supposed to block, we can disregard the pending operations as not ready.
427e3b8d4bbSDavid van Moolenbroek    * We must make absolutely sure that the flags are "stable" right now though:
428e3b8d4bbSDavid van Moolenbroek    * we are neither waiting to query the driver about them (FSF_UPDATE) nor
429e3b8d4bbSDavid van Moolenbroek    * querying the driver about them right now (FSF_BUSY).  This is a dangerous
430e3b8d4bbSDavid van Moolenbroek    * case of premature optimization and may be removed altogether if it proves
431e3b8d4bbSDavid van Moolenbroek    * to continue to be a source of bugs.
432e3b8d4bbSDavid van Moolenbroek    */
433e3b8d4bbSDavid van Moolenbroek   if (!block && !(f->filp_select_flags & (FSF_UPDATE | FSF_BUSY)) &&
434e3b8d4bbSDavid van Moolenbroek       (f->filp_select_flags & FSF_BLOCKED)) {
435e3b8d4bbSDavid van Moolenbroek 	if ((rops & SEL_RD) && (f->filp_select_flags & FSF_RD_BLOCK))
436e3b8d4bbSDavid van Moolenbroek 		rops &= ~SEL_RD;
437e3b8d4bbSDavid van Moolenbroek 	if ((rops & SEL_WR) && (f->filp_select_flags & FSF_WR_BLOCK))
438e3b8d4bbSDavid van Moolenbroek 		rops &= ~SEL_WR;
439e3b8d4bbSDavid van Moolenbroek 	if ((rops & SEL_ERR) && (f->filp_select_flags & FSF_ERR_BLOCK))
440e3b8d4bbSDavid van Moolenbroek 		rops &= ~SEL_ERR;
441e3b8d4bbSDavid van Moolenbroek 	if (!(rops & (SEL_RD|SEL_WR|SEL_ERR)))
442e3b8d4bbSDavid van Moolenbroek 		return(0);
443e3b8d4bbSDavid van Moolenbroek   }
444e3b8d4bbSDavid van Moolenbroek 
445e3b8d4bbSDavid van Moolenbroek   f->filp_select_flags |= FSF_UPDATE;
446e3b8d4bbSDavid van Moolenbroek   if (block) {
447e3b8d4bbSDavid van Moolenbroek 	rops |= SEL_NOTIFY;
448e3b8d4bbSDavid van Moolenbroek 	if (rops & SEL_RD)	f->filp_select_flags |= FSF_RD_BLOCK;
449e3b8d4bbSDavid van Moolenbroek 	if (rops & SEL_WR)	f->filp_select_flags |= FSF_WR_BLOCK;
450e3b8d4bbSDavid van Moolenbroek 	if (rops & SEL_ERR)	f->filp_select_flags |= FSF_ERR_BLOCK;
451e3b8d4bbSDavid van Moolenbroek   }
452e3b8d4bbSDavid van Moolenbroek 
453e3b8d4bbSDavid van Moolenbroek   if (f->filp_select_flags & FSF_BUSY)
454e3b8d4bbSDavid van Moolenbroek 	return(SUSPEND);
455e3b8d4bbSDavid van Moolenbroek 
456e3b8d4bbSDavid van Moolenbroek   return rops;
457e3b8d4bbSDavid van Moolenbroek }
458e3b8d4bbSDavid van Moolenbroek 
459e3b8d4bbSDavid van Moolenbroek /*===========================================================================*
460433d6423SLionel Sambuc  *				select_request_char			     *
461433d6423SLionel Sambuc  *===========================================================================*/
select_request_char(struct filp * f,int * ops,int block,struct fproc * rfp)46227d0ecdbSDavid van Moolenbroek static int select_request_char(struct filp *f, int *ops, int block,
46327d0ecdbSDavid van Moolenbroek 	struct fproc *rfp)
464433d6423SLionel Sambuc {
465433d6423SLionel Sambuc /* Check readiness status on a character device. Unless suitable results are
466433d6423SLionel Sambuc  * available right now, this will only initiate the polling process, causing
467433d6423SLionel Sambuc  * result processing to be deferred. This function MUST NOT block its calling
468433d6423SLionel Sambuc  * thread. The given filp may or may not be locked.
469433d6423SLionel Sambuc  */
47027d0ecdbSDavid van Moolenbroek   dev_t dev;
4713b468884SDavid van Moolenbroek   int r, rops;
472433d6423SLionel Sambuc   struct dmap *dp;
473433d6423SLionel Sambuc 
47427d0ecdbSDavid van Moolenbroek   /* Start by remapping the device node number to a "real" device number. Those
47527d0ecdbSDavid van Moolenbroek    * two are different only for CTTY_MAJOR aka /dev/tty, but that one single
47627d0ecdbSDavid van Moolenbroek    * exception requires quite some extra effort here: the select code matches
47727d0ecdbSDavid van Moolenbroek    * character driver replies to their requests based on the device number, so
47827d0ecdbSDavid van Moolenbroek    * it needs to be aware that device numbers may be mapped. The idea is to
47927d0ecdbSDavid van Moolenbroek    * perform the mapping once and store the result in the filp object, so that
48027d0ecdbSDavid van Moolenbroek    * at least we don't run into problems when a process loses its controlling
48127d0ecdbSDavid van Moolenbroek    * terminal while doing a select (see also free_proc). It should be noted
48227d0ecdbSDavid van Moolenbroek    * that it is possible that multiple processes share the same /dev/tty filp,
48327d0ecdbSDavid van Moolenbroek    * and they may not all have a controlling terminal. The ctty-less processes
48427d0ecdbSDavid van Moolenbroek    * should never pass the mapping; a more problematic case is checked below.
48527d0ecdbSDavid van Moolenbroek    *
48627d0ecdbSDavid van Moolenbroek    * The cdev_map call also checks the major number for rough validity, so that
48727d0ecdbSDavid van Moolenbroek    * we can use it to index the dmap array safely a bit later.
48827d0ecdbSDavid van Moolenbroek    */
48927d0ecdbSDavid van Moolenbroek   if ((dev = cdev_map(f->filp_vno->v_sdev, rfp)) == NO_DEV)
49027d0ecdbSDavid van Moolenbroek 	return(ENXIO);
49127d0ecdbSDavid van Moolenbroek 
492e3b8d4bbSDavid van Moolenbroek   if (f->filp_select_dev != NO_DEV && f->filp_select_dev != dev) {
49327d0ecdbSDavid van Moolenbroek 	/* Currently, this case can occur as follows: a process with a
49427d0ecdbSDavid van Moolenbroek 	 * controlling terminal opens /dev/tty and forks, the new child starts
49527d0ecdbSDavid van Moolenbroek 	 * a new session, opens a new controlling terminal, and both parent and
49627d0ecdbSDavid van Moolenbroek 	 * child call select on the /dev/tty file descriptor. If this case ever
49727d0ecdbSDavid van Moolenbroek 	 * becomes real, a better solution may be to force-close a filp for
49827d0ecdbSDavid van Moolenbroek 	 * /dev/tty when a new controlling terminal is opened.
49927d0ecdbSDavid van Moolenbroek 	 */
50027d0ecdbSDavid van Moolenbroek 	printf("VFS: file pointer has multiple controlling TTYs!\n");
50127d0ecdbSDavid van Moolenbroek 	return(EIO);
50227d0ecdbSDavid van Moolenbroek   }
503e3b8d4bbSDavid van Moolenbroek   f->filp_select_dev = dev; /* set before possibly suspending */
504433d6423SLionel Sambuc 
505e3b8d4bbSDavid van Moolenbroek   if ((rops = select_filter(f, ops, block)) <= 0)
506e3b8d4bbSDavid van Moolenbroek 	return(rops); /* OK or suspend: nothing to do for now */
507433d6423SLionel Sambuc 
50827d0ecdbSDavid van Moolenbroek   dp = &dmap[major(dev)];
509433d6423SLionel Sambuc   if (dp->dmap_sel_busy)
510433d6423SLionel Sambuc 	return(SUSPEND);
511433d6423SLionel Sambuc 
512433d6423SLionel Sambuc   f->filp_select_flags &= ~FSF_UPDATE;
51327d0ecdbSDavid van Moolenbroek   r = cdev_select(dev, rops);
514433d6423SLionel Sambuc   if (r != OK)
515433d6423SLionel Sambuc 	return(r);
516433d6423SLionel Sambuc 
517433d6423SLionel Sambuc   dp->dmap_sel_busy = TRUE;
518433d6423SLionel Sambuc   dp->dmap_sel_filp = f;
519433d6423SLionel Sambuc   f->filp_select_flags |= FSF_BUSY;
520433d6423SLionel Sambuc 
521433d6423SLionel Sambuc   return(SUSPEND);
522433d6423SLionel Sambuc }
523433d6423SLionel Sambuc 
524433d6423SLionel Sambuc /*===========================================================================*
525e3b8d4bbSDavid van Moolenbroek  *				select_request_sock			     *
526e3b8d4bbSDavid van Moolenbroek  *===========================================================================*/
select_request_sock(struct filp * f,int * ops,int block,struct fproc * rfp __unused)527e3b8d4bbSDavid van Moolenbroek static int select_request_sock(struct filp *f, int *ops, int block,
528e3b8d4bbSDavid van Moolenbroek 	struct fproc *rfp __unused)
529e3b8d4bbSDavid van Moolenbroek {
530e3b8d4bbSDavid van Moolenbroek /* Check readiness status on a socket device. Unless suitable results are
531e3b8d4bbSDavid van Moolenbroek  * available right now, this will only initiate the polling process, causing
532e3b8d4bbSDavid van Moolenbroek  * result processing to be deferred. This function MUST NOT block its calling
533e3b8d4bbSDavid van Moolenbroek  * thread. The given filp may or may not be locked.
534e3b8d4bbSDavid van Moolenbroek  */
535e3b8d4bbSDavid van Moolenbroek   struct smap *sp;
536e3b8d4bbSDavid van Moolenbroek   dev_t dev;
537e3b8d4bbSDavid van Moolenbroek   int r, rops;
538e3b8d4bbSDavid van Moolenbroek 
539e3b8d4bbSDavid van Moolenbroek   dev = f->filp_vno->v_sdev;
540e3b8d4bbSDavid van Moolenbroek 
541e3b8d4bbSDavid van Moolenbroek   if ((sp = get_smap_by_dev(dev, NULL)) == NULL)
542e3b8d4bbSDavid van Moolenbroek 	return(ENXIO);	/* this should not happen */
543e3b8d4bbSDavid van Moolenbroek 
544e3b8d4bbSDavid van Moolenbroek   f->filp_select_dev = dev; /* set before possibly suspending */
545e3b8d4bbSDavid van Moolenbroek 
546e3b8d4bbSDavid van Moolenbroek   if ((rops = select_filter(f, ops, block)) <= 0)
547e3b8d4bbSDavid van Moolenbroek 	return(rops); /* OK or suspend: nothing to do for now */
548e3b8d4bbSDavid van Moolenbroek 
549e3b8d4bbSDavid van Moolenbroek   if (sp->smap_sel_busy)
550e3b8d4bbSDavid van Moolenbroek 	return(SUSPEND);
551e3b8d4bbSDavid van Moolenbroek 
552e3b8d4bbSDavid van Moolenbroek   f->filp_select_flags &= ~FSF_UPDATE;
553e3b8d4bbSDavid van Moolenbroek   r = sdev_select(dev, rops);
554e3b8d4bbSDavid van Moolenbroek   if (r != OK)
555e3b8d4bbSDavid van Moolenbroek 	return(r);
556e3b8d4bbSDavid van Moolenbroek 
557e3b8d4bbSDavid van Moolenbroek   sp->smap_sel_busy = TRUE;
558e3b8d4bbSDavid van Moolenbroek   sp->smap_sel_filp = f;
559e3b8d4bbSDavid van Moolenbroek   f->filp_select_flags |= FSF_BUSY;
560e3b8d4bbSDavid van Moolenbroek 
561e3b8d4bbSDavid van Moolenbroek   return(SUSPEND);
562e3b8d4bbSDavid van Moolenbroek }
563e3b8d4bbSDavid van Moolenbroek 
564e3b8d4bbSDavid van Moolenbroek /*===========================================================================*
565433d6423SLionel Sambuc  *				select_request_file			     *
566433d6423SLionel Sambuc  *===========================================================================*/
select_request_file(struct filp * UNUSED (f),int * UNUSED (ops),int UNUSED (block),struct fproc * UNUSED (rfp))567433d6423SLionel Sambuc static int select_request_file(struct filp *UNUSED(f), int *UNUSED(ops),
56827d0ecdbSDavid van Moolenbroek   int UNUSED(block), struct fproc *UNUSED(rfp))
569433d6423SLionel Sambuc {
570433d6423SLionel Sambuc   /* Files are always ready, so output *ops is input *ops */
571433d6423SLionel Sambuc   return(OK);
572433d6423SLionel Sambuc }
573433d6423SLionel Sambuc 
574433d6423SLionel Sambuc /*===========================================================================*
575433d6423SLionel Sambuc  *				select_request_pipe			     *
576433d6423SLionel Sambuc  *===========================================================================*/
select_request_pipe(struct filp * f,int * ops,int block,struct fproc * UNUSED (rfp))57727d0ecdbSDavid van Moolenbroek static int select_request_pipe(struct filp *f, int *ops, int block,
57827d0ecdbSDavid van Moolenbroek 	struct fproc *UNUSED(rfp))
579433d6423SLionel Sambuc {
580433d6423SLionel Sambuc /* Check readiness status on a pipe. The given filp is locked. This function
581433d6423SLionel Sambuc  * may block its calling thread if necessary.
582433d6423SLionel Sambuc  */
583433d6423SLionel Sambuc   int orig_ops, r = 0, err;
584433d6423SLionel Sambuc 
585433d6423SLionel Sambuc   orig_ops = *ops;
586433d6423SLionel Sambuc 
587433d6423SLionel Sambuc   if ((*ops & (SEL_RD|SEL_ERR))) {
588433d6423SLionel Sambuc 	/* Check if we can read 1 byte */
589433d6423SLionel Sambuc 	err = pipe_check(f, READING, f->filp_flags & ~O_NONBLOCK, 1,
590433d6423SLionel Sambuc 			 1 /* Check only */);
591433d6423SLionel Sambuc 
592433d6423SLionel Sambuc 	if (err != SUSPEND)
593433d6423SLionel Sambuc 		r |= SEL_RD;
594433d6423SLionel Sambuc 	if (err < 0 && err != SUSPEND)
595433d6423SLionel Sambuc 		r |= SEL_ERR;
596433d6423SLionel Sambuc   }
597433d6423SLionel Sambuc 
598433d6423SLionel Sambuc   if ((*ops & (SEL_WR|SEL_ERR))) {
599433d6423SLionel Sambuc 	/* Check if we can write 1 byte */
600433d6423SLionel Sambuc 	err = pipe_check(f, WRITING, f->filp_flags & ~O_NONBLOCK, 1,
601433d6423SLionel Sambuc 			 1 /* Check only */);
602433d6423SLionel Sambuc 
603433d6423SLionel Sambuc 	if (err != SUSPEND)
604433d6423SLionel Sambuc 		r |= SEL_WR;
605433d6423SLionel Sambuc 	if (err < 0 && err != SUSPEND)
606433d6423SLionel Sambuc 		r |= SEL_ERR;
607433d6423SLionel Sambuc   }
608433d6423SLionel Sambuc 
609433d6423SLionel Sambuc   /* Some options we collected might not be requested. */
610433d6423SLionel Sambuc   *ops = r & orig_ops;
611433d6423SLionel Sambuc 
612433d6423SLionel Sambuc   if (!*ops && block)
613433d6423SLionel Sambuc 	f->filp_pipe_select_ops |= orig_ops;
614433d6423SLionel Sambuc 
615433d6423SLionel Sambuc   return(OK);
616433d6423SLionel Sambuc }
617433d6423SLionel Sambuc 
618433d6423SLionel Sambuc /*===========================================================================*
619433d6423SLionel Sambuc  *				tab2ops					     *
620433d6423SLionel Sambuc  *===========================================================================*/
tab2ops(int fd,struct selectentry * e)621433d6423SLionel Sambuc static int tab2ops(int fd, struct selectentry *e)
622433d6423SLionel Sambuc {
623433d6423SLionel Sambuc   int ops = 0;
624433d6423SLionel Sambuc   if (FD_ISSET(fd, &e->readfds))  ops |= SEL_RD;
625433d6423SLionel Sambuc   if (FD_ISSET(fd, &e->writefds)) ops |= SEL_WR;
626433d6423SLionel Sambuc   if (FD_ISSET(fd, &e->errorfds)) ops |= SEL_ERR;
627433d6423SLionel Sambuc 
628433d6423SLionel Sambuc   return(ops);
629433d6423SLionel Sambuc }
630433d6423SLionel Sambuc 
631433d6423SLionel Sambuc 
632433d6423SLionel Sambuc /*===========================================================================*
633433d6423SLionel Sambuc  *				ops2tab					     *
634433d6423SLionel Sambuc  *===========================================================================*/
ops2tab(int ops,int fd,struct selectentry * e)635433d6423SLionel Sambuc static void ops2tab(int ops, int fd, struct selectentry *e)
636433d6423SLionel Sambuc {
637433d6423SLionel Sambuc   if ((ops & SEL_RD) && e->vir_readfds && FD_ISSET(fd, &e->readfds) &&
638433d6423SLionel Sambuc       !FD_ISSET(fd, &e->ready_readfds)) {
639433d6423SLionel Sambuc 	FD_SET(fd, &e->ready_readfds);
640433d6423SLionel Sambuc 	e->nreadyfds++;
641433d6423SLionel Sambuc   }
642433d6423SLionel Sambuc 
643433d6423SLionel Sambuc   if ((ops & SEL_WR) && e->vir_writefds && FD_ISSET(fd, &e->writefds) &&
644433d6423SLionel Sambuc       !FD_ISSET(fd, &e->ready_writefds)) {
645433d6423SLionel Sambuc 	FD_SET(fd, &e->ready_writefds);
646433d6423SLionel Sambuc 	e->nreadyfds++;
647433d6423SLionel Sambuc   }
648433d6423SLionel Sambuc 
649433d6423SLionel Sambuc   if ((ops & SEL_ERR) && e->vir_errorfds && FD_ISSET(fd, &e->errorfds) &&
650433d6423SLionel Sambuc       !FD_ISSET(fd, &e->ready_errorfds)) {
651433d6423SLionel Sambuc 	FD_SET(fd, &e->ready_errorfds);
652433d6423SLionel Sambuc 	e->nreadyfds++;
653433d6423SLionel Sambuc   }
654433d6423SLionel Sambuc }
655433d6423SLionel Sambuc 
656433d6423SLionel Sambuc 
657433d6423SLionel Sambuc /*===========================================================================*
658433d6423SLionel Sambuc  *				copy_fdsets				     *
659433d6423SLionel Sambuc  *===========================================================================*/
copy_fdsets(struct selectentry * se,int nfds,int direction)660433d6423SLionel Sambuc static int copy_fdsets(struct selectentry *se, int nfds, int direction)
661433d6423SLionel Sambuc {
662433d6423SLionel Sambuc /* Copy FD sets from or to the user process calling select(2). This function
663433d6423SLionel Sambuc  * MUST NOT block the calling thread.
664433d6423SLionel Sambuc  */
665433d6423SLionel Sambuc   int r;
666433d6423SLionel Sambuc   size_t fd_setsize;
667433d6423SLionel Sambuc   endpoint_t src_e, dst_e;
668433d6423SLionel Sambuc   fd_set *src_fds, *dst_fds;
669433d6423SLionel Sambuc 
670433d6423SLionel Sambuc   if (nfds < 0 || nfds > OPEN_MAX)
671433d6423SLionel Sambuc 	panic("select copy_fdsets: nfds wrong: %d", nfds);
672433d6423SLionel Sambuc 
673433d6423SLionel Sambuc   /* Only copy back as many bits as the user expects. */
674433d6423SLionel Sambuc   fd_setsize = (size_t) (howmany(nfds, __NFDBITS) * sizeof(__fd_mask));
675433d6423SLionel Sambuc 
676433d6423SLionel Sambuc   /* Set source and destination endpoints */
677433d6423SLionel Sambuc   src_e = (direction == FROM_PROC) ? se->req_endpt : SELF;
678433d6423SLionel Sambuc   dst_e = (direction == FROM_PROC) ? SELF : se->req_endpt;
679433d6423SLionel Sambuc 
680433d6423SLionel Sambuc   /* read set */
681433d6423SLionel Sambuc   src_fds = (direction == FROM_PROC) ? se->vir_readfds : &se->ready_readfds;
682433d6423SLionel Sambuc   dst_fds = (direction == FROM_PROC) ? &se->readfds : se->vir_readfds;
683433d6423SLionel Sambuc   if (se->vir_readfds) {
684433d6423SLionel Sambuc 	r = sys_datacopy_wrapper(src_e, (vir_bytes) src_fds, dst_e,
685433d6423SLionel Sambuc 			(vir_bytes) dst_fds, fd_setsize);
686433d6423SLionel Sambuc 	if (r != OK) return(r);
687433d6423SLionel Sambuc   }
688433d6423SLionel Sambuc 
689433d6423SLionel Sambuc   /* write set */
690433d6423SLionel Sambuc   src_fds = (direction == FROM_PROC) ? se->vir_writefds : &se->ready_writefds;
691433d6423SLionel Sambuc   dst_fds = (direction == FROM_PROC) ? &se->writefds : se->vir_writefds;
692433d6423SLionel Sambuc   if (se->vir_writefds) {
693433d6423SLionel Sambuc 	r = sys_datacopy_wrapper(src_e, (vir_bytes) src_fds, dst_e,
694433d6423SLionel Sambuc 			(vir_bytes) dst_fds, fd_setsize);
695433d6423SLionel Sambuc 	if (r != OK) return(r);
696433d6423SLionel Sambuc   }
697433d6423SLionel Sambuc 
698433d6423SLionel Sambuc   /* error set */
699433d6423SLionel Sambuc   src_fds = (direction == FROM_PROC) ? se->vir_errorfds : &se->ready_errorfds;
700433d6423SLionel Sambuc   dst_fds = (direction == FROM_PROC) ? &se->errorfds : se->vir_errorfds;
701433d6423SLionel Sambuc   if (se->vir_errorfds) {
702433d6423SLionel Sambuc 	r = sys_datacopy_wrapper(src_e, (vir_bytes) src_fds, dst_e,
703433d6423SLionel Sambuc 			(vir_bytes) dst_fds, fd_setsize);
704433d6423SLionel Sambuc 	if (r != OK) return(r);
705433d6423SLionel Sambuc   }
706433d6423SLionel Sambuc 
707433d6423SLionel Sambuc   return(OK);
708433d6423SLionel Sambuc }
709433d6423SLionel Sambuc 
710433d6423SLionel Sambuc 
711433d6423SLionel Sambuc /*===========================================================================*
712433d6423SLionel Sambuc  *				select_cancel_all			     *
713433d6423SLionel Sambuc  *===========================================================================*/
select_cancel_all(struct selectentry * se)714433d6423SLionel Sambuc static void select_cancel_all(struct selectentry *se)
715433d6423SLionel Sambuc {
716433d6423SLionel Sambuc /* Cancel select, possibly on success. Decrease select usage and cancel timer.
717433d6423SLionel Sambuc  * This function MUST NOT block its calling thread.
718433d6423SLionel Sambuc  */
719433d6423SLionel Sambuc 
720433d6423SLionel Sambuc   int fd;
721433d6423SLionel Sambuc   struct filp *f;
722433d6423SLionel Sambuc 
723433d6423SLionel Sambuc   for (fd = 0; fd < se->nfds; fd++) {
724433d6423SLionel Sambuc 	if ((f = se->filps[fd]) == NULL) continue;
725433d6423SLionel Sambuc 	se->filps[fd] = NULL;
726433d6423SLionel Sambuc 	select_cancel_filp(f);
727433d6423SLionel Sambuc   }
728433d6423SLionel Sambuc 
729433d6423SLionel Sambuc   if (se->expiry > 0) {
730433d6423SLionel Sambuc 	cancel_timer(&se->timer);
731433d6423SLionel Sambuc 	se->expiry = 0;
732433d6423SLionel Sambuc   }
733433d6423SLionel Sambuc 
734433d6423SLionel Sambuc   se->requestor = NULL;
735433d6423SLionel Sambuc }
736433d6423SLionel Sambuc 
737433d6423SLionel Sambuc /*===========================================================================*
738433d6423SLionel Sambuc  *				select_cancel_filp			     *
739433d6423SLionel Sambuc  *===========================================================================*/
select_cancel_filp(struct filp * f)740433d6423SLionel Sambuc static void select_cancel_filp(struct filp *f)
741433d6423SLionel Sambuc {
742433d6423SLionel Sambuc /* Reduce the number of select users of this filp. This function MUST NOT block
743433d6423SLionel Sambuc  * its calling thread.
744433d6423SLionel Sambuc  */
7453b468884SDavid van Moolenbroek   devmajor_t major;
746e3b8d4bbSDavid van Moolenbroek   struct smap *sp;
747433d6423SLionel Sambuc 
748433d6423SLionel Sambuc   assert(f);
749433d6423SLionel Sambuc   assert(f->filp_selectors > 0);
750433d6423SLionel Sambuc   assert(f->filp_count > 0);
751433d6423SLionel Sambuc 
752433d6423SLionel Sambuc   f->filp_selectors--;
753433d6423SLionel Sambuc   if (f->filp_selectors == 0) {
754433d6423SLionel Sambuc 	/* No one selecting on this filp anymore, forget about select state */
755433d6423SLionel Sambuc 	f->filp_select_ops = 0;
756433d6423SLionel Sambuc 	f->filp_select_flags = 0;
757433d6423SLionel Sambuc 	f->filp_pipe_select_ops = 0;
758433d6423SLionel Sambuc 
759433d6423SLionel Sambuc 	/* If this filp is the subject of an ongoing select query to a
760e3b8d4bbSDavid van Moolenbroek 	 * character or socket device, mark the query as stale, so that this
761e3b8d4bbSDavid van Moolenbroek 	 * filp will not be checked when the result arrives. The filp select
762e3b8d4bbSDavid van Moolenbroek 	 * device may still be NO_DEV if do_select fails on the initial fd
763e3b8d4bbSDavid van Moolenbroek 	 * check.
764433d6423SLionel Sambuc 	 */
765e3b8d4bbSDavid van Moolenbroek 	if (is_char_device(f) && f->filp_select_dev != NO_DEV) {
766e3b8d4bbSDavid van Moolenbroek 		major = major(f->filp_select_dev);
767433d6423SLionel Sambuc 		if (dmap[major].dmap_sel_busy &&
768433d6423SLionel Sambuc 			dmap[major].dmap_sel_filp == f)
769433d6423SLionel Sambuc 			dmap[major].dmap_sel_filp = NULL; /* leave _busy set */
770e3b8d4bbSDavid van Moolenbroek 		f->filp_select_dev = NO_DEV;
771e3b8d4bbSDavid van Moolenbroek 	} else if (is_sock_device(f) && f->filp_select_dev != NO_DEV) {
772e3b8d4bbSDavid van Moolenbroek 		if ((sp = get_smap_by_dev(f->filp_select_dev, NULL)) != NULL &&
773e3b8d4bbSDavid van Moolenbroek 		    sp->smap_sel_busy && sp->smap_sel_filp == f)
774e3b8d4bbSDavid van Moolenbroek 			sp->smap_sel_filp = NULL;	/* leave _busy set */
775e3b8d4bbSDavid van Moolenbroek 		f->filp_select_dev = NO_DEV;
776433d6423SLionel Sambuc 	}
777433d6423SLionel Sambuc   }
778433d6423SLionel Sambuc }
779433d6423SLionel Sambuc 
780433d6423SLionel Sambuc /*===========================================================================*
781433d6423SLionel Sambuc  *				select_return				     *
782433d6423SLionel Sambuc  *===========================================================================*/
select_return(struct selectentry * se)783433d6423SLionel Sambuc static void select_return(struct selectentry *se)
784433d6423SLionel Sambuc {
785433d6423SLionel Sambuc /* Return the results of a select call to the user process and revive the
786433d6423SLionel Sambuc  * process. This function MUST NOT block its calling thread.
787433d6423SLionel Sambuc  */
788433d6423SLionel Sambuc   int r;
789433d6423SLionel Sambuc 
790433d6423SLionel Sambuc   assert(!is_deferred(se));	/* Not done yet, first wait for async reply */
791433d6423SLionel Sambuc 
792433d6423SLionel Sambuc   select_cancel_all(se);
793433d6423SLionel Sambuc 
794433d6423SLionel Sambuc   if (se->error != OK)
795433d6423SLionel Sambuc 	r = se->error;
796433d6423SLionel Sambuc   else
797433d6423SLionel Sambuc 	r = copy_fdsets(se, se->nfds, TO_PROC);
798433d6423SLionel Sambuc   if (r == OK)
799433d6423SLionel Sambuc 	r = se->nreadyfds;
800433d6423SLionel Sambuc 
801433d6423SLionel Sambuc   revive(se->req_endpt, r);
802433d6423SLionel Sambuc }
803433d6423SLionel Sambuc 
804433d6423SLionel Sambuc 
805433d6423SLionel Sambuc /*===========================================================================*
806433d6423SLionel Sambuc  *				select_callback			             *
807433d6423SLionel Sambuc  *===========================================================================*/
select_callback(struct filp * f,int status)808433d6423SLionel Sambuc void select_callback(struct filp *f, int status)
809433d6423SLionel Sambuc {
810433d6423SLionel Sambuc /* The status of a filp has changed, with the given ready operations or error.
811433d6423SLionel Sambuc  * This function is currently called only for pipes, and holds the lock to
812433d6423SLionel Sambuc  * the filp.
813433d6423SLionel Sambuc  */
814433d6423SLionel Sambuc 
815433d6423SLionel Sambuc   filp_status(f, status);
816433d6423SLionel Sambuc }
817433d6423SLionel Sambuc 
818433d6423SLionel Sambuc /*===========================================================================*
819433d6423SLionel Sambuc  *				init_select  				     *
820433d6423SLionel Sambuc  *===========================================================================*/
init_select(void)821433d6423SLionel Sambuc void init_select(void)
822433d6423SLionel Sambuc {
823433d6423SLionel Sambuc   int s;
824433d6423SLionel Sambuc 
825433d6423SLionel Sambuc   for (s = 0; s < MAXSELECTS; s++)
826433d6423SLionel Sambuc 	init_timer(&selecttab[s].timer);
827433d6423SLionel Sambuc }
828433d6423SLionel Sambuc 
829433d6423SLionel Sambuc 
830433d6423SLionel Sambuc /*===========================================================================*
831433d6423SLionel Sambuc  *				select_forget			             *
832433d6423SLionel Sambuc  *===========================================================================*/
select_forget(void)833433d6423SLionel Sambuc void select_forget(void)
834433d6423SLionel Sambuc {
835433d6423SLionel Sambuc /* The calling thread's associated process is expected to be unpaused, due to
836433d6423SLionel Sambuc  * a signal that is supposed to interrupt the current system call. Totally
837433d6423SLionel Sambuc  * forget about the select(). This function may block its calling thread if
838433d6423SLionel Sambuc  * necessary (but it doesn't).
839433d6423SLionel Sambuc  */
840433d6423SLionel Sambuc   int slot;
841433d6423SLionel Sambuc   struct selectentry *se;
842433d6423SLionel Sambuc 
843433d6423SLionel Sambuc   for (slot = 0; slot < MAXSELECTS; slot++) {
844433d6423SLionel Sambuc 	se = &selecttab[slot];
845433d6423SLionel Sambuc 	if (se->requestor == fp)
846433d6423SLionel Sambuc 		break;
847433d6423SLionel Sambuc   }
848433d6423SLionel Sambuc 
849433d6423SLionel Sambuc   if (slot >= MAXSELECTS) return;	/* Entry not found */
850433d6423SLionel Sambuc 
851433d6423SLionel Sambuc   assert(se->starting == FALSE);
852433d6423SLionel Sambuc 
853433d6423SLionel Sambuc   /* Do NOT test on is_deferred here. We can safely cancel ongoing queries. */
854433d6423SLionel Sambuc   select_cancel_all(se);
855433d6423SLionel Sambuc }
856433d6423SLionel Sambuc 
857433d6423SLionel Sambuc 
858433d6423SLionel Sambuc /*===========================================================================*
859433d6423SLionel Sambuc  *				select_timeout_check	  	     	     *
860433d6423SLionel Sambuc  *===========================================================================*/
select_timeout_check(int s)861cfd712b4SDavid van Moolenbroek void select_timeout_check(int s)
862433d6423SLionel Sambuc {
863433d6423SLionel Sambuc /* An alarm has gone off for one of the select queries. This function MUST NOT
864433d6423SLionel Sambuc  * block its calling thread.
865433d6423SLionel Sambuc  */
866433d6423SLionel Sambuc   struct selectentry *se;
867433d6423SLionel Sambuc 
868433d6423SLionel Sambuc   if (s < 0 || s >= MAXSELECTS) return;	/* Entry does not exist */
869433d6423SLionel Sambuc 
870433d6423SLionel Sambuc   se = &selecttab[s];
871433d6423SLionel Sambuc   if (se->requestor == NULL) return;
872cfd712b4SDavid van Moolenbroek   if (se->expiry == 0) return;	/* Strange, did we even ask for a timeout? */
873433d6423SLionel Sambuc   se->expiry = 0;
874de95c84dSDavid van Moolenbroek   if (!is_deferred(se))
875433d6423SLionel Sambuc 	select_return(se);
876de95c84dSDavid van Moolenbroek   else
877de95c84dSDavid van Moolenbroek 	se->block = 0;	/* timer triggered "too soon", treat as nonblocking */
878433d6423SLionel Sambuc }
879433d6423SLionel Sambuc 
880433d6423SLionel Sambuc 
881433d6423SLionel Sambuc /*===========================================================================*
882433d6423SLionel Sambuc  *				select_unsuspend_by_endpt  	     	     *
883433d6423SLionel Sambuc  *===========================================================================*/
select_unsuspend_by_endpt(endpoint_t proc_e)884433d6423SLionel Sambuc void select_unsuspend_by_endpt(endpoint_t proc_e)
885433d6423SLionel Sambuc {
886433d6423SLionel Sambuc /* Revive blocked processes when a driver has disappeared */
887e3b8d4bbSDavid van Moolenbroek   struct dmap *dp;
888e3b8d4bbSDavid van Moolenbroek   struct smap *sp;
8893b468884SDavid van Moolenbroek   devmajor_t major;
890*722cbc61SDavid van Moolenbroek   int fd, s, is_driver, restart;
891433d6423SLionel Sambuc   struct selectentry *se;
892433d6423SLionel Sambuc   struct filp *f;
893433d6423SLionel Sambuc 
894e3b8d4bbSDavid van Moolenbroek   /* Either or both of these may be NULL. */
895e3b8d4bbSDavid van Moolenbroek   dp = get_dmap_by_endpt(proc_e);
896e3b8d4bbSDavid van Moolenbroek   sp = get_smap_by_endpt(proc_e);
897e3b8d4bbSDavid van Moolenbroek 
898e3b8d4bbSDavid van Moolenbroek   is_driver = (dp != NULL || sp != NULL);
899e3b8d4bbSDavid van Moolenbroek 
900433d6423SLionel Sambuc   for (s = 0; s < MAXSELECTS; s++) {
901433d6423SLionel Sambuc 	se = &selecttab[s];
902433d6423SLionel Sambuc 	if (se->requestor == NULL) continue;
903433d6423SLionel Sambuc 	if (se->requestor->fp_endpoint == proc_e) {
904433d6423SLionel Sambuc 		assert(se->requestor->fp_flags & FP_EXITING);
905433d6423SLionel Sambuc 		select_cancel_all(se);
906433d6423SLionel Sambuc 		continue;
907433d6423SLionel Sambuc 	}
908433d6423SLionel Sambuc 
909e3b8d4bbSDavid van Moolenbroek 	/* Skip the more expensive "driver died" checks for non-drivers. */
910e3b8d4bbSDavid van Moolenbroek 	if (!is_driver)
911433d6423SLionel Sambuc 		continue;
912433d6423SLionel Sambuc 
913*722cbc61SDavid van Moolenbroek 	restart = FALSE;
914*722cbc61SDavid van Moolenbroek 
915e3b8d4bbSDavid van Moolenbroek 	for (fd = 0; fd < se->nfds; fd++) {
916e3b8d4bbSDavid van Moolenbroek 		if ((f = se->filps[fd]) == NULL)
917e3b8d4bbSDavid van Moolenbroek 			continue;
918e3b8d4bbSDavid van Moolenbroek 		if (is_char_device(f)) {
919e3b8d4bbSDavid van Moolenbroek 			assert(f->filp_select_dev != NO_DEV);
920e3b8d4bbSDavid van Moolenbroek 			major = major(f->filp_select_dev);
921433d6423SLionel Sambuc 			if (dmap_driver_match(proc_e, major)) {
922*722cbc61SDavid van Moolenbroek 				ops2tab(SEL_RD | SEL_WR, fd, se);
923433d6423SLionel Sambuc 				se->filps[fd] = NULL;
924433d6423SLionel Sambuc 				select_cancel_filp(f);
925*722cbc61SDavid van Moolenbroek 				restart = TRUE;
926433d6423SLionel Sambuc 			}
927e3b8d4bbSDavid van Moolenbroek 		} else if (sp != NULL && is_sock_device(f)) {
928e3b8d4bbSDavid van Moolenbroek 			assert(f->filp_select_dev != NO_DEV);
929e3b8d4bbSDavid van Moolenbroek 			if (get_smap_by_dev(f->filp_select_dev, NULL) == sp) {
930*722cbc61SDavid van Moolenbroek 				ops2tab(SEL_RD | SEL_WR, fd, se);
931e3b8d4bbSDavid van Moolenbroek 				se->filps[fd] = NULL;
932e3b8d4bbSDavid van Moolenbroek 				select_cancel_filp(f);
933*722cbc61SDavid van Moolenbroek 				restart = TRUE;
934e3b8d4bbSDavid van Moolenbroek 			}
935e3b8d4bbSDavid van Moolenbroek 		}
936433d6423SLionel Sambuc 	}
937433d6423SLionel Sambuc 
938*722cbc61SDavid van Moolenbroek 	if (restart)
939*722cbc61SDavid van Moolenbroek 		restart_proc(se);
940433d6423SLionel Sambuc   }
941e3b8d4bbSDavid van Moolenbroek 
942e3b8d4bbSDavid van Moolenbroek   /* Any outstanding queries will never be answered, so forget about them. */
943e3b8d4bbSDavid van Moolenbroek   if (dp != NULL) {
944e3b8d4bbSDavid van Moolenbroek 	assert(dp->dmap_sel_filp == NULL);
945e3b8d4bbSDavid van Moolenbroek 	dp->dmap_sel_busy = FALSE;
946e3b8d4bbSDavid van Moolenbroek   }
947e3b8d4bbSDavid van Moolenbroek   if (sp != NULL) {
948e3b8d4bbSDavid van Moolenbroek 	assert(sp->smap_sel_filp == NULL);
949e3b8d4bbSDavid van Moolenbroek 	sp->smap_sel_busy = FALSE;
950e3b8d4bbSDavid van Moolenbroek   }
951433d6423SLionel Sambuc }
952433d6423SLionel Sambuc 
953433d6423SLionel Sambuc /*===========================================================================*
954433d6423SLionel Sambuc  *				select_reply1				     *
955433d6423SLionel Sambuc  *===========================================================================*/
select_reply1(struct filp * f,int status)956e3b8d4bbSDavid van Moolenbroek static void select_reply1(struct filp *f, int status)
957433d6423SLionel Sambuc {
958e3b8d4bbSDavid van Moolenbroek /* Handle the initial reply to a character or socket select request.  This
959e3b8d4bbSDavid van Moolenbroek  * function MUST NOT block its calling thread.
960433d6423SLionel Sambuc  */
961433d6423SLionel Sambuc 
962433d6423SLionel Sambuc   assert(f->filp_count >= 1);
963433d6423SLionel Sambuc   assert(f->filp_select_flags & FSF_BUSY);
964433d6423SLionel Sambuc 
965433d6423SLionel Sambuc   f->filp_select_flags &= ~FSF_BUSY;
966433d6423SLionel Sambuc 
967433d6423SLionel Sambuc   /* The select call is done now, except when
968433d6423SLionel Sambuc    * - another process started a select on the same filp with possibly a
969433d6423SLionel Sambuc    *   different set of operations.
970433d6423SLionel Sambuc    * - a process does a select on the same filp but using different file
971433d6423SLionel Sambuc    *   descriptors.
972433d6423SLionel Sambuc    * - the select has a timeout. Upon receiving this reply the operations
973433d6423SLionel Sambuc    *   might not be ready yet, so we want to wait for that to ultimately
974433d6423SLionel Sambuc    *   happen.
975433d6423SLionel Sambuc    *   Therefore we need to keep remembering what the operations are.
976433d6423SLionel Sambuc    */
977433d6423SLionel Sambuc   if (!(f->filp_select_flags & (FSF_UPDATE|FSF_BLOCKED)))
978433d6423SLionel Sambuc 	f->filp_select_ops = 0;		/* done selecting */
979433d6423SLionel Sambuc   else if (status > 0 && !(f->filp_select_flags & FSF_UPDATE))
980433d6423SLionel Sambuc 	/* there may be operations pending */
981433d6423SLionel Sambuc 	f->filp_select_ops &= ~status;
982433d6423SLionel Sambuc 
983433d6423SLionel Sambuc   /* Record new filp status */
984433d6423SLionel Sambuc   if (!(status == 0 && (f->filp_select_flags & FSF_BLOCKED))) {
985433d6423SLionel Sambuc 	if (status > 0) {	/* operations ready */
986433d6423SLionel Sambuc 		if (status & SEL_RD)
987433d6423SLionel Sambuc 			f->filp_select_flags &= ~FSF_RD_BLOCK;
988433d6423SLionel Sambuc 		if (status & SEL_WR)
989433d6423SLionel Sambuc 			f->filp_select_flags &= ~FSF_WR_BLOCK;
990433d6423SLionel Sambuc 		if (status & SEL_ERR)
991433d6423SLionel Sambuc 			f->filp_select_flags &= ~FSF_ERR_BLOCK;
992433d6423SLionel Sambuc 	} else if (status < 0) { /* error */
993433d6423SLionel Sambuc 		/* Always unblock upon error */
994433d6423SLionel Sambuc 		f->filp_select_flags &= ~FSF_BLOCKED;
995433d6423SLionel Sambuc 	}
996433d6423SLionel Sambuc   }
997433d6423SLionel Sambuc 
998433d6423SLionel Sambuc   filp_status(f, status); /* Tell filp owners about the results */
999433d6423SLionel Sambuc }
1000433d6423SLionel Sambuc 
1001433d6423SLionel Sambuc /*===========================================================================*
1002e3b8d4bbSDavid van Moolenbroek  *				select_cdev_reply1			     *
1003433d6423SLionel Sambuc  *===========================================================================*/
select_cdev_reply1(endpoint_t driver_e,devminor_t minor,int status)1004e3b8d4bbSDavid van Moolenbroek void select_cdev_reply1(endpoint_t driver_e, devminor_t minor, int status)
1005433d6423SLionel Sambuc {
1006e3b8d4bbSDavid van Moolenbroek /* Handle the initial reply to a CDEV_SELECT request. This function MUST NOT
1007e3b8d4bbSDavid van Moolenbroek  * block its calling thread.
1008433d6423SLionel Sambuc  */
10093b468884SDavid van Moolenbroek   devmajor_t major;
1010433d6423SLionel Sambuc   dev_t dev;
1011433d6423SLionel Sambuc   struct filp *f;
1012433d6423SLionel Sambuc   struct dmap *dp;
1013433d6423SLionel Sambuc 
1014433d6423SLionel Sambuc   /* Figure out which device is replying */
1015e3b8d4bbSDavid van Moolenbroek   if ((dp = get_dmap_by_endpt(driver_e)) == NULL) return;
1016e3b8d4bbSDavid van Moolenbroek 
1017433d6423SLionel Sambuc   major = dp-dmap;
1018433d6423SLionel Sambuc   dev = makedev(major, minor);
1019433d6423SLionel Sambuc 
1020e3b8d4bbSDavid van Moolenbroek   /* Get filp belonging to character special file */
1021e3b8d4bbSDavid van Moolenbroek   if (!dp->dmap_sel_busy) {
1022e3b8d4bbSDavid van Moolenbroek 	printf("VFS (%s:%d): major %d was not expecting a CDEV_SELECT reply\n",
1023e3b8d4bbSDavid van Moolenbroek 		__FILE__, __LINE__, major);
1024e3b8d4bbSDavid van Moolenbroek 	return;
1025e3b8d4bbSDavid van Moolenbroek   }
1026e3b8d4bbSDavid van Moolenbroek 
1027e3b8d4bbSDavid van Moolenbroek   /* The select filp may have been set to NULL if the requestor has been
1028e3b8d4bbSDavid van Moolenbroek    * unpaused in the meantime. In that case, we ignore the result, but we do
1029e3b8d4bbSDavid van Moolenbroek    * look for other filps to restart later.
1030e3b8d4bbSDavid van Moolenbroek    */
1031e3b8d4bbSDavid van Moolenbroek   if ((f = dp->dmap_sel_filp) != NULL) {
1032e3b8d4bbSDavid van Moolenbroek 	/* Find vnode and check we got a reply from the device we expected */
1033e3b8d4bbSDavid van Moolenbroek 	assert(is_char_device(f));
1034e3b8d4bbSDavid van Moolenbroek 	assert(f->filp_select_dev != NO_DEV);
1035e3b8d4bbSDavid van Moolenbroek 	if (f->filp_select_dev != dev) {
1036e3b8d4bbSDavid van Moolenbroek 		/* This should never happen. The driver may be misbehaving.
1037e3b8d4bbSDavid van Moolenbroek 		 * For now we assume that the reply we want will arrive later..
1038e3b8d4bbSDavid van Moolenbroek 		 */
1039e3b8d4bbSDavid van Moolenbroek 		printf("VFS (%s:%d): expected reply from dev %llx not %llx\n",
1040e3b8d4bbSDavid van Moolenbroek 			__FILE__, __LINE__, f->filp_select_dev, dev);
1041e3b8d4bbSDavid van Moolenbroek 		return;
1042e3b8d4bbSDavid van Moolenbroek 	}
1043e3b8d4bbSDavid van Moolenbroek   }
1044e3b8d4bbSDavid van Moolenbroek 
1045e3b8d4bbSDavid van Moolenbroek   /* No longer waiting for a reply from this device */
1046e3b8d4bbSDavid van Moolenbroek   dp->dmap_sel_busy = FALSE;
1047e3b8d4bbSDavid van Moolenbroek   dp->dmap_sel_filp = NULL;
1048e3b8d4bbSDavid van Moolenbroek 
1049e3b8d4bbSDavid van Moolenbroek   /* Process the status change, if still applicable. */
1050e3b8d4bbSDavid van Moolenbroek   if (f != NULL)
1051e3b8d4bbSDavid van Moolenbroek 	select_reply1(f, status);
1052e3b8d4bbSDavid van Moolenbroek 
1053e3b8d4bbSDavid van Moolenbroek   /* See if we should send a select request for another filp now. */
1054e3b8d4bbSDavid van Moolenbroek   select_restart_filps();
1055e3b8d4bbSDavid van Moolenbroek }
1056e3b8d4bbSDavid van Moolenbroek 
1057e3b8d4bbSDavid van Moolenbroek /*===========================================================================*
1058e3b8d4bbSDavid van Moolenbroek  *				select_sdev_reply1			     *
1059e3b8d4bbSDavid van Moolenbroek  *===========================================================================*/
select_sdev_reply1(dev_t dev,int status)1060e3b8d4bbSDavid van Moolenbroek void select_sdev_reply1(dev_t dev, int status)
1061e3b8d4bbSDavid van Moolenbroek {
1062e3b8d4bbSDavid van Moolenbroek /* Handle the initial reply to a SDEV_SELECT request. This function MUST NOT
1063e3b8d4bbSDavid van Moolenbroek  * block its calling thread.
1064e3b8d4bbSDavid van Moolenbroek  */
1065e3b8d4bbSDavid van Moolenbroek   struct smap *sp;
1066e3b8d4bbSDavid van Moolenbroek   struct filp *f;
1067e3b8d4bbSDavid van Moolenbroek 
1068e3b8d4bbSDavid van Moolenbroek   if ((sp = get_smap_by_dev(dev, NULL)) == NULL)
1069e3b8d4bbSDavid van Moolenbroek 	return;
1070e3b8d4bbSDavid van Moolenbroek 
1071e3b8d4bbSDavid van Moolenbroek   /* Get the file pointer for the socket device. */
1072e3b8d4bbSDavid van Moolenbroek   if (!sp->smap_sel_busy) {
1073e3b8d4bbSDavid van Moolenbroek 	printf("VFS: was not expecting a SDEV_SELECT reply from %d\n",
1074e3b8d4bbSDavid van Moolenbroek 	    sp->smap_endpt);
1075e3b8d4bbSDavid van Moolenbroek 	return;
1076e3b8d4bbSDavid van Moolenbroek   }
1077e3b8d4bbSDavid van Moolenbroek 
1078e3b8d4bbSDavid van Moolenbroek   /* The select filp may have been set to NULL if the requestor has been
1079e3b8d4bbSDavid van Moolenbroek    * unpaused in the meantime. In that case, we ignore the result, but we do
1080e3b8d4bbSDavid van Moolenbroek    * look for other filps to restart later.
1081e3b8d4bbSDavid van Moolenbroek    */
1082e3b8d4bbSDavid van Moolenbroek   if ((f = sp->smap_sel_filp) != NULL) {
1083e3b8d4bbSDavid van Moolenbroek 	/* Find vnode and check we got a reply from the device we expected */
1084e3b8d4bbSDavid van Moolenbroek 	assert(is_sock_device(f));
1085e3b8d4bbSDavid van Moolenbroek 	assert(f->filp_select_dev != NO_DEV);
1086e3b8d4bbSDavid van Moolenbroek 	if (f->filp_select_dev != dev) {
1087e3b8d4bbSDavid van Moolenbroek 		/* This should never happen. The driver may be misbehaving.
1088e3b8d4bbSDavid van Moolenbroek 		 * For now we assume that the reply we want will arrive later..
1089e3b8d4bbSDavid van Moolenbroek 		 */
1090e3b8d4bbSDavid van Moolenbroek 		printf("VFS: expected reply from sock dev %llx, not %llx\n",
1091e3b8d4bbSDavid van Moolenbroek 		    f->filp_select_dev, dev);
1092e3b8d4bbSDavid van Moolenbroek 		return;
1093e3b8d4bbSDavid van Moolenbroek 	}
1094e3b8d4bbSDavid van Moolenbroek   }
1095e3b8d4bbSDavid van Moolenbroek 
1096e3b8d4bbSDavid van Moolenbroek   /* We are no longer waiting for a reply from this socket driver. */
1097e3b8d4bbSDavid van Moolenbroek   sp->smap_sel_busy = FALSE;
1098e3b8d4bbSDavid van Moolenbroek   sp->smap_sel_filp = NULL;
1099e3b8d4bbSDavid van Moolenbroek 
1100e3b8d4bbSDavid van Moolenbroek   /* Process the status change, if still applicable. */
1101e3b8d4bbSDavid van Moolenbroek   if (f != NULL)
1102e3b8d4bbSDavid van Moolenbroek 	select_reply1(f, status);
1103e3b8d4bbSDavid van Moolenbroek 
1104e3b8d4bbSDavid van Moolenbroek   /* See if we should send a select request for another filp now. */
1105e3b8d4bbSDavid van Moolenbroek   select_restart_filps();
1106e3b8d4bbSDavid van Moolenbroek }
1107e3b8d4bbSDavid van Moolenbroek 
1108e3b8d4bbSDavid van Moolenbroek /*===========================================================================*
1109e3b8d4bbSDavid van Moolenbroek  *				select_reply2				     *
1110e3b8d4bbSDavid van Moolenbroek  *===========================================================================*/
select_reply2(int is_char,dev_t dev,int status)1111e3b8d4bbSDavid van Moolenbroek static void select_reply2(int is_char, dev_t dev, int status)
1112e3b8d4bbSDavid van Moolenbroek {
1113e3b8d4bbSDavid van Moolenbroek /* Find all file descriptors selecting for the given character (is_char==TRUE)
1114e3b8d4bbSDavid van Moolenbroek  * or socket (is_char==FALSE) device, update their statuses, and resume
1115e3b8d4bbSDavid van Moolenbroek  * activities accordingly.
1116e3b8d4bbSDavid van Moolenbroek  */
1117e3b8d4bbSDavid van Moolenbroek   int slot, found, fd;
1118e3b8d4bbSDavid van Moolenbroek   struct filp *f;
1119e3b8d4bbSDavid van Moolenbroek   struct selectentry *se;
1120e3b8d4bbSDavid van Moolenbroek 
1121433d6423SLionel Sambuc   for (slot = 0; slot < MAXSELECTS; slot++) {
1122433d6423SLionel Sambuc 	se = &selecttab[slot];
1123433d6423SLionel Sambuc 	if (se->requestor == NULL) continue;	/* empty slot */
1124433d6423SLionel Sambuc 
1125433d6423SLionel Sambuc 	found = FALSE;
1126433d6423SLionel Sambuc 	for (fd = 0; fd < se->nfds; fd++) {
1127433d6423SLionel Sambuc 		if ((f = se->filps[fd]) == NULL) continue;
1128e3b8d4bbSDavid van Moolenbroek 		if (is_char && !is_char_device(f)) continue;
1129e3b8d4bbSDavid van Moolenbroek 		if (!is_char && !is_sock_device(f)) continue;
1130e3b8d4bbSDavid van Moolenbroek 		assert(f->filp_select_dev != NO_DEV);
1131e3b8d4bbSDavid van Moolenbroek 		if (f->filp_select_dev != dev) continue;
1132433d6423SLionel Sambuc 
1133433d6423SLionel Sambuc 		if (status > 0) {	/* Operations ready */
1134433d6423SLionel Sambuc 			/* Clear the replied bits from the request
1135433d6423SLionel Sambuc 			 * mask unless FSF_UPDATE is set.
1136433d6423SLionel Sambuc 			 */
1137433d6423SLionel Sambuc 			if (!(f->filp_select_flags & FSF_UPDATE))
1138433d6423SLionel Sambuc 				f->filp_select_ops &= ~status;
1139433d6423SLionel Sambuc 			if (status & SEL_RD)
1140433d6423SLionel Sambuc 				f->filp_select_flags &= ~FSF_RD_BLOCK;
1141433d6423SLionel Sambuc 			if (status & SEL_WR)
1142433d6423SLionel Sambuc 				f->filp_select_flags &= ~FSF_WR_BLOCK;
1143433d6423SLionel Sambuc 			if (status & SEL_ERR)
1144433d6423SLionel Sambuc 				f->filp_select_flags &= ~FSF_ERR_BLOCK;
1145433d6423SLionel Sambuc 
1146433d6423SLionel Sambuc 			ops2tab(status, fd, se);
1147433d6423SLionel Sambuc 		} else {
1148433d6423SLionel Sambuc 			f->filp_select_flags &= ~FSF_BLOCKED;
1149433d6423SLionel Sambuc 			se->error = status;
1150433d6423SLionel Sambuc 		}
1151433d6423SLionel Sambuc 		found = TRUE;
1152433d6423SLionel Sambuc 	}
1153433d6423SLionel Sambuc 	/* Even if 'found' is set now, nothing may have changed for this call,
1154433d6423SLionel Sambuc 	 * as it may not have been interested in the operations that were
1155433d6423SLionel Sambuc 	 * reported as ready. Let restart_proc check.
1156433d6423SLionel Sambuc 	 */
1157433d6423SLionel Sambuc 	if (found)
1158433d6423SLionel Sambuc 		restart_proc(se);
1159433d6423SLionel Sambuc   }
1160433d6423SLionel Sambuc 
1161433d6423SLionel Sambuc   select_restart_filps();
1162433d6423SLionel Sambuc }
1163433d6423SLionel Sambuc 
1164433d6423SLionel Sambuc /*===========================================================================*
1165e3b8d4bbSDavid van Moolenbroek  *				select_cdev_reply2			     *
1166e3b8d4bbSDavid van Moolenbroek  *===========================================================================*/
select_cdev_reply2(endpoint_t driver_e,devminor_t minor,int status)1167e3b8d4bbSDavid van Moolenbroek void select_cdev_reply2(endpoint_t driver_e, devminor_t minor, int status)
1168e3b8d4bbSDavid van Moolenbroek {
1169e3b8d4bbSDavid van Moolenbroek /* Handle a secondary reply to a CDEV_SELECT request. A secondary reply occurs
1170e3b8d4bbSDavid van Moolenbroek  * when the select request is 'blocking' until an operation becomes ready. This
1171e3b8d4bbSDavid van Moolenbroek  * function MUST NOT block its calling thread.
1172e3b8d4bbSDavid van Moolenbroek  */
1173e3b8d4bbSDavid van Moolenbroek   devmajor_t major;
1174e3b8d4bbSDavid van Moolenbroek   struct dmap *dp;
1175e3b8d4bbSDavid van Moolenbroek   dev_t dev;
1176e3b8d4bbSDavid van Moolenbroek 
1177e3b8d4bbSDavid van Moolenbroek   if (status == 0) {
1178e3b8d4bbSDavid van Moolenbroek 	printf("VFS (%s:%d): weird status (%d) to report\n",
1179e3b8d4bbSDavid van Moolenbroek 		__FILE__, __LINE__, status);
1180e3b8d4bbSDavid van Moolenbroek 	return;
1181e3b8d4bbSDavid van Moolenbroek   }
1182e3b8d4bbSDavid van Moolenbroek 
1183e3b8d4bbSDavid van Moolenbroek   /* Figure out which device is replying */
1184e3b8d4bbSDavid van Moolenbroek   if ((dp = get_dmap_by_endpt(driver_e)) == NULL) {
1185e3b8d4bbSDavid van Moolenbroek 	printf("VFS (%s:%d): endpoint %d is not a known driver endpoint\n",
1186e3b8d4bbSDavid van Moolenbroek 		__FILE__, __LINE__, driver_e);
1187e3b8d4bbSDavid van Moolenbroek 	return;
1188e3b8d4bbSDavid van Moolenbroek   }
1189e3b8d4bbSDavid van Moolenbroek   major = dp-dmap;
1190e3b8d4bbSDavid van Moolenbroek   dev = makedev(major, minor);
1191e3b8d4bbSDavid van Moolenbroek 
1192e3b8d4bbSDavid van Moolenbroek   select_reply2(TRUE /*is_char*/, dev, status);
1193e3b8d4bbSDavid van Moolenbroek }
1194e3b8d4bbSDavid van Moolenbroek 
1195e3b8d4bbSDavid van Moolenbroek /*===========================================================================*
1196e3b8d4bbSDavid van Moolenbroek  *				select_sdev_reply2			     *
1197e3b8d4bbSDavid van Moolenbroek  *===========================================================================*/
select_sdev_reply2(dev_t dev,int status)1198e3b8d4bbSDavid van Moolenbroek void select_sdev_reply2(dev_t dev, int status)
1199e3b8d4bbSDavid van Moolenbroek {
1200e3b8d4bbSDavid van Moolenbroek /* Handle a secondary reply to a SDEV_SELECT request. A secondary reply occurs
1201e3b8d4bbSDavid van Moolenbroek  * when the select request is 'blocking' until an operation becomes ready. This
1202e3b8d4bbSDavid van Moolenbroek  * function MUST NOT block its calling thread.
1203e3b8d4bbSDavid van Moolenbroek  */
1204e3b8d4bbSDavid van Moolenbroek 
1205e3b8d4bbSDavid van Moolenbroek   if (status == 0) {
1206e3b8d4bbSDavid van Moolenbroek 	printf("VFS: weird socket device status (%d)\n", status);
1207e3b8d4bbSDavid van Moolenbroek 
1208e3b8d4bbSDavid van Moolenbroek 	return;
1209e3b8d4bbSDavid van Moolenbroek   }
1210e3b8d4bbSDavid van Moolenbroek 
1211e3b8d4bbSDavid van Moolenbroek   select_reply2(FALSE /*is_char*/, dev, status);
1212e3b8d4bbSDavid van Moolenbroek }
1213e3b8d4bbSDavid van Moolenbroek 
1214e3b8d4bbSDavid van Moolenbroek /*===========================================================================*
1215433d6423SLionel Sambuc  *				select_restart_filps			     *
1216433d6423SLionel Sambuc  *===========================================================================*/
select_restart_filps(void)1217433d6423SLionel Sambuc static void select_restart_filps(void)
1218433d6423SLionel Sambuc {
1219433d6423SLionel Sambuc /* We got a result from a character driver, and now we need to check if we can
1220433d6423SLionel Sambuc  * restart deferred polling operations. This function MUST NOT block its
1221433d6423SLionel Sambuc  * calling thread.
1222433d6423SLionel Sambuc  */
1223433d6423SLionel Sambuc   int fd, slot;
1224433d6423SLionel Sambuc   struct filp *f;
1225433d6423SLionel Sambuc   struct selectentry *se;
1226433d6423SLionel Sambuc 
1227433d6423SLionel Sambuc   /* Locate filps that can be restarted */
1228433d6423SLionel Sambuc   for (slot = 0; slot < MAXSELECTS; slot++) {
1229433d6423SLionel Sambuc 	se = &selecttab[slot];
1230433d6423SLionel Sambuc 	if (se->requestor == NULL) continue; /* empty slot */
1231433d6423SLionel Sambuc 
1232433d6423SLionel Sambuc 	/* Only 'deferred' processes are eligible to restart */
1233433d6423SLionel Sambuc 	if (!is_deferred(se)) continue;
1234433d6423SLionel Sambuc 
1235433d6423SLionel Sambuc 	/* Find filps that are not waiting for a reply, but have an updated
1236433d6423SLionel Sambuc 	 * status (i.e., another select on the same filp with possibly a
1237433d6423SLionel Sambuc 	 * different set of operations is to be done), and thus requires the
1238433d6423SLionel Sambuc 	 * select request to be sent again).
1239433d6423SLionel Sambuc 	 */
1240433d6423SLionel Sambuc 	for (fd = 0; fd < se->nfds; fd++) {
1241433d6423SLionel Sambuc 		int r, wantops, ops;
1242433d6423SLionel Sambuc 		if ((f = se->filps[fd]) == NULL) continue;
1243433d6423SLionel Sambuc 		if (f->filp_select_flags & FSF_BUSY) /* Still waiting for */
1244433d6423SLionel Sambuc 			continue;		     /* initial reply */
1245433d6423SLionel Sambuc 		if (!(f->filp_select_flags & FSF_UPDATE)) /* Must be in  */
1246433d6423SLionel Sambuc 			continue;			  /* 'update' state */
1247433d6423SLionel Sambuc 
1248e3b8d4bbSDavid van Moolenbroek 		/* This function is suitable only for character and socket
1249e3b8d4bbSDavid van Moolenbroek 		 * devices. In particular, checking pipes the same way would
1250e3b8d4bbSDavid van Moolenbroek 		 * introduce a serious locking problem.
1251433d6423SLionel Sambuc 		 */
1252e3b8d4bbSDavid van Moolenbroek 		assert(is_char_device(f) || is_sock_device(f));
1253433d6423SLionel Sambuc 
1254433d6423SLionel Sambuc 		wantops = ops = f->filp_select_ops;
1255e3b8d4bbSDavid van Moolenbroek 		if (is_char_device(f))
1256e3b8d4bbSDavid van Moolenbroek 			r = select_request_char(f, &wantops, se->block,
1257e3b8d4bbSDavid van Moolenbroek 			    se->requestor);
1258e3b8d4bbSDavid van Moolenbroek 		else
1259e3b8d4bbSDavid van Moolenbroek 			r = select_request_sock(f, &wantops, se->block,
1260e3b8d4bbSDavid van Moolenbroek 			    se->requestor);
1261433d6423SLionel Sambuc 		if (r != OK && r != SUSPEND) {
1262433d6423SLionel Sambuc 			se->error = r;
1263433d6423SLionel Sambuc 			restart_proc(se);
1264433d6423SLionel Sambuc 			break; /* Error or bogus return code; abort */
1265433d6423SLionel Sambuc 		}
1266433d6423SLionel Sambuc 		if (wantops & ops) ops2tab(wantops, fd, se);
1267433d6423SLionel Sambuc 	}
1268433d6423SLionel Sambuc   }
1269433d6423SLionel Sambuc }
1270433d6423SLionel Sambuc 
1271433d6423SLionel Sambuc /*===========================================================================*
1272433d6423SLionel Sambuc  *				filp_status				     *
1273433d6423SLionel Sambuc  *===========================================================================*/
1274a0814afbSRichard Sailer static void
filp_status(struct filp * f,int status)1275a0814afbSRichard Sailer filp_status(struct filp *f, int status)
1276433d6423SLionel Sambuc {
1277433d6423SLionel Sambuc /* Tell processes that need to know about the status of this filp. This
1278433d6423SLionel Sambuc  * function MUST NOT block its calling thread.
1279433d6423SLionel Sambuc  */
1280433d6423SLionel Sambuc   int fd, slot, found;
1281433d6423SLionel Sambuc   struct selectentry *se;
1282433d6423SLionel Sambuc 
1283433d6423SLionel Sambuc   for (slot = 0; slot < MAXSELECTS; slot++) {
1284433d6423SLionel Sambuc 	se = &selecttab[slot];
1285433d6423SLionel Sambuc 	if (se->requestor == NULL) continue; /* empty slot */
1286433d6423SLionel Sambuc 
1287433d6423SLionel Sambuc 	found = FALSE;
1288433d6423SLionel Sambuc 	for (fd = 0; fd < se->nfds; fd++) {
1289433d6423SLionel Sambuc 		if (se->filps[fd] != f) continue;
1290433d6423SLionel Sambuc 		if (status < 0)
1291433d6423SLionel Sambuc 			se->error = status;
1292433d6423SLionel Sambuc 		else
1293433d6423SLionel Sambuc 			ops2tab(status, fd, se);
1294433d6423SLionel Sambuc 		found = TRUE;
1295433d6423SLionel Sambuc 	}
1296433d6423SLionel Sambuc 	if (found)
1297433d6423SLionel Sambuc 		restart_proc(se);
1298433d6423SLionel Sambuc   }
1299433d6423SLionel Sambuc }
1300433d6423SLionel Sambuc 
1301433d6423SLionel Sambuc /*===========================================================================*
1302433d6423SLionel Sambuc  *				restart_proc				     *
1303433d6423SLionel Sambuc  *===========================================================================*/
1304a0814afbSRichard Sailer static void
restart_proc(struct selectentry * se)1305a0814afbSRichard Sailer restart_proc(struct selectentry *se)
1306433d6423SLionel Sambuc {
1307433d6423SLionel Sambuc /* Tell process about select results (if any) unless there are still results
1308433d6423SLionel Sambuc  * pending. This function MUST NOT block its calling thread.
1309433d6423SLionel Sambuc  */
1310433d6423SLionel Sambuc 
1311433d6423SLionel Sambuc   if ((se->nreadyfds > 0 || se->error != OK || !se->block) && !is_deferred(se))
1312433d6423SLionel Sambuc 	select_return(se);
1313433d6423SLionel Sambuc }
1314433d6423SLionel Sambuc 
1315433d6423SLionel Sambuc /*===========================================================================*
1316433d6423SLionel Sambuc  *				wipe_select				     *
1317433d6423SLionel Sambuc  *===========================================================================*/
wipe_select(struct selectentry * se)1318433d6423SLionel Sambuc static void wipe_select(struct selectentry *se)
1319433d6423SLionel Sambuc {
1320433d6423SLionel Sambuc   se->nfds = 0;
1321433d6423SLionel Sambuc   se->nreadyfds = 0;
1322433d6423SLionel Sambuc   se->error = OK;
1323433d6423SLionel Sambuc   se->block = 0;
1324433d6423SLionel Sambuc   memset(se->filps, 0, sizeof(se->filps));
1325433d6423SLionel Sambuc 
1326433d6423SLionel Sambuc   FD_ZERO(&se->readfds);
1327433d6423SLionel Sambuc   FD_ZERO(&se->writefds);
1328433d6423SLionel Sambuc   FD_ZERO(&se->errorfds);
1329433d6423SLionel Sambuc   FD_ZERO(&se->ready_readfds);
1330433d6423SLionel Sambuc   FD_ZERO(&se->ready_writefds);
1331433d6423SLionel Sambuc   FD_ZERO(&se->ready_errorfds);
1332433d6423SLionel Sambuc }
1333433d6423SLionel Sambuc 
1334433d6423SLionel Sambuc /*===========================================================================*
1335433d6423SLionel Sambuc  *				select_lock_filp			     *
1336433d6423SLionel Sambuc  *===========================================================================*/
select_lock_filp(struct filp * f,int ops)1337433d6423SLionel Sambuc static void select_lock_filp(struct filp *f, int ops)
1338433d6423SLionel Sambuc {
1339433d6423SLionel Sambuc /* Lock a filp and vnode based on which operations are requested. This function
1340433d6423SLionel Sambuc  * may block its calling thread, obviously.
1341433d6423SLionel Sambuc  */
1342433d6423SLionel Sambuc   tll_access_t locktype;
1343433d6423SLionel Sambuc 
1344433d6423SLionel Sambuc   locktype = VNODE_READ; /* By default */
1345433d6423SLionel Sambuc 
1346433d6423SLionel Sambuc   if (ops & (SEL_WR|SEL_ERR))
1347433d6423SLionel Sambuc 	/* Selecting for error or writing requires exclusive access */
1348433d6423SLionel Sambuc 	locktype = VNODE_WRITE;
1349433d6423SLionel Sambuc 
1350433d6423SLionel Sambuc   lock_filp(f, locktype);
1351433d6423SLionel Sambuc }
135263faa8feSDavid van Moolenbroek 
135363faa8feSDavid van Moolenbroek /*
135463faa8feSDavid van Moolenbroek  * Dump the state of the entire select table, for debugging purposes.
135563faa8feSDavid van Moolenbroek  */
135663faa8feSDavid van Moolenbroek void
select_dump(void)135763faa8feSDavid van Moolenbroek select_dump(void)
135863faa8feSDavid van Moolenbroek {
135963faa8feSDavid van Moolenbroek 	struct selectentry *se;
136063faa8feSDavid van Moolenbroek 	struct filp *f;
136163faa8feSDavid van Moolenbroek 	struct dmap *dp;
1362e3b8d4bbSDavid van Moolenbroek 	struct smap *sp;
136363faa8feSDavid van Moolenbroek 	dev_t dev;
1364e3b8d4bbSDavid van Moolenbroek 	sockid_t sockid;
136563faa8feSDavid van Moolenbroek 	int s, fd;
136663faa8feSDavid van Moolenbroek 
136763faa8feSDavid van Moolenbroek 	for (s = 0; s < MAXSELECTS; s++) {
136863faa8feSDavid van Moolenbroek 		se = &selecttab[s];
136963faa8feSDavid van Moolenbroek 		if (se->requestor == NULL)
137063faa8feSDavid van Moolenbroek 			continue;
137163faa8feSDavid van Moolenbroek 
137263faa8feSDavid van Moolenbroek 		printf("select %d: endpt %d nfds %d nreadyfds %d error %d "
137363faa8feSDavid van Moolenbroek 		    "block %d starting %d expiry %u is_deferred %d\n",
137463faa8feSDavid van Moolenbroek 		    s, se->req_endpt, se->nfds, se->nreadyfds, se->error,
137563faa8feSDavid van Moolenbroek 		    se->block, se->starting, se->expiry, is_deferred(se));
137663faa8feSDavid van Moolenbroek 
137763faa8feSDavid van Moolenbroek 		for (fd = 0; !se->starting && fd < se->nfds; fd++) {
137863faa8feSDavid van Moolenbroek 			/* Save on output: do not print NULL filps at all. */
137963faa8feSDavid van Moolenbroek 			if ((f = se->filps[fd]) == NULL)
138063faa8feSDavid van Moolenbroek 				continue;
138163faa8feSDavid van Moolenbroek 
138263faa8feSDavid van Moolenbroek 			printf("- [%d] filp %p flags %x type ", fd, f,
138363faa8feSDavid van Moolenbroek 			    f->filp_select_flags);
138463faa8feSDavid van Moolenbroek 			if (is_regular_file(f))
138563faa8feSDavid van Moolenbroek 				printf("regular\n");
138663faa8feSDavid van Moolenbroek 			else if (is_pipe(f))
138763faa8feSDavid van Moolenbroek 				printf("pipe\n");
138863faa8feSDavid van Moolenbroek 			else if (is_char_device(f)) {
138963faa8feSDavid van Moolenbroek 				dev = cdev_map(f->filp_vno->v_sdev,
139063faa8feSDavid van Moolenbroek 				    se->requestor);
139163faa8feSDavid van Moolenbroek 				printf("char (dev <%d,%d>, dmap ",
139263faa8feSDavid van Moolenbroek 				    major(dev), minor(dev));
139363faa8feSDavid van Moolenbroek 				if (dev != NO_DEV) {
139463faa8feSDavid van Moolenbroek 					dp = &dmap[major(dev)];
139563faa8feSDavid van Moolenbroek 					printf("busy %d filp %p)\n",
139663faa8feSDavid van Moolenbroek 					    dp->dmap_sel_busy,
139763faa8feSDavid van Moolenbroek 					    dp->dmap_sel_filp);
139863faa8feSDavid van Moolenbroek 				} else
139963faa8feSDavid van Moolenbroek 					printf("unknown)\n");
1400e3b8d4bbSDavid van Moolenbroek 			} else if (is_sock_device(f)) {
1401e3b8d4bbSDavid van Moolenbroek 				dev = f->filp_vno->v_sdev;
1402e3b8d4bbSDavid van Moolenbroek 				printf("sock (dev ");
1403e3b8d4bbSDavid van Moolenbroek 				sp = get_smap_by_dev(dev, &sockid);
1404e3b8d4bbSDavid van Moolenbroek 				if (sp != NULL) {
1405e3b8d4bbSDavid van Moolenbroek 					printf("<%d,%d>, smap busy %d filp "
1406e3b8d4bbSDavid van Moolenbroek 					    "%p)\n", sp->smap_num, sockid,
1407e3b8d4bbSDavid van Moolenbroek 					    sp->smap_sel_busy,
1408e3b8d4bbSDavid van Moolenbroek 					    sp->smap_sel_filp);
1409e3b8d4bbSDavid van Moolenbroek 				} else
1410e3b8d4bbSDavid van Moolenbroek 					printf("<0x%"PRIx64">, smap "
1411e3b8d4bbSDavid van Moolenbroek 					    "unknown)\n", dev);
141263faa8feSDavid van Moolenbroek 			} else
141363faa8feSDavid van Moolenbroek 				printf("unknown\n");
141463faa8feSDavid van Moolenbroek 		}
141563faa8feSDavid van Moolenbroek 	}
141663faa8feSDavid van Moolenbroek }
1417