fix packman sort col, and make sort case-insensitive
[minix3.git] / servers / vfs / select.c
blob05fd10c796b66e154b40c943ff10a2d7f3f39511
1 /* Implement entry point to select system call.
3 * The entry points into this file are
4 * do_select: perform the SELECT system call
5 * select_callback: notify select system of possible fd operation
6 * select_notified: low-level entry for device notifying select
7 * select_unsuspend_by_endpt: cancel a blocking select on exiting driver
8 *
9 * Changes:
10 * 6 june 2005 Created (Ben Gras)
13 #define DEBUG_SELECT 0
15 #include "fs.h"
16 #include "select.h"
17 #include "file.h"
18 #include "vnode.h"
20 #include <sys/time.h>
21 #include <sys/select.h>
22 #include <minix/com.h>
23 #include <minix/u64.h>
24 #include <string.h>
26 /* max. number of simultaneously pending select() calls */
27 #define MAXSELECTS 25
29 PRIVATE struct selectentry {
30 struct fproc *requestor; /* slot is free iff this is NULL */
31 int req_endpt;
32 fd_set readfds, writefds, errorfds;
33 fd_set ready_readfds, ready_writefds, ready_errorfds;
34 fd_set *vir_readfds, *vir_writefds, *vir_errorfds;
35 struct filp *filps[OPEN_MAX];
36 int type[OPEN_MAX];
37 int nfds, nreadyfds;
38 clock_t expiry;
39 timer_t timer; /* if expiry > 0 */
40 } selecttab[MAXSELECTS];
42 FORWARD _PROTOTYPE(int select_reevaluate, (struct filp *fp));
44 FORWARD _PROTOTYPE(int select_request_file,
45 (struct filp *f, int *ops, int block));
46 FORWARD _PROTOTYPE(int select_match_file, (struct filp *f));
48 FORWARD _PROTOTYPE(int select_request_general,
49 (struct filp *f, int *ops, int block));
50 FORWARD _PROTOTYPE(int select_major_match,
51 (int match_major, struct filp *file));
53 FORWARD _PROTOTYPE(void select_cancel_all, (struct selectentry *e));
54 FORWARD _PROTOTYPE(void select_wakeup, (struct selectentry *e, int r));
55 FORWARD _PROTOTYPE(void select_return, (struct selectentry *, int));
57 /* The Open Group:
58 * "The pselect() and select() functions shall support
59 * regular files, terminal and pseudo-terminal devices,
60 * STREAMS-based files, FIFOs, pipes, and sockets."
63 PRIVATE struct fdtype {
64 int (*select_request)(struct filp *, int *ops, int block);
65 int (*select_match)(struct filp *);
66 int select_major;
67 } fdtypes[] = {
68 { select_request_file, select_match_file, 0 },
69 { select_request_general, NULL, TTY_MAJOR },
70 { select_request_general, NULL, INET_MAJOR },
71 { select_request_pipe, select_match_pipe, 0 },
72 { select_request_general, NULL, LOG_MAJOR },
74 #define SEL_FDS (sizeof(fdtypes) / sizeof(fdtypes[0]))
76 /* Open Group:
77 * "File descriptors associated with regular files shall always select true
78 * for ready to read, ready to write, and error conditions."
81 /*===========================================================================*
82 * select_request_file *
83 *===========================================================================*/
84 PRIVATE int select_request_file(struct filp *f, int *ops, int block)
86 /* output *ops is input *ops */
87 return SEL_OK;
90 /*===========================================================================*
91 * select_match_file *
92 *===========================================================================*/
93 PRIVATE int select_match_file(struct filp *file)
95 if (file && file->filp_vno && (file->filp_vno->v_mode & I_REGULAR))
96 return 1;
97 return 0;
100 /*===========================================================================*
101 * select_request_general *
102 *===========================================================================*/
103 PRIVATE int select_request_general(struct filp *f, int *ops, int block)
105 int rops = *ops;
106 if (block) rops |= SEL_NOTIFY;
107 *ops = dev_io(VFS_DEV_SELECT, f->filp_vno->v_sdev, rops, NULL,
108 cvu64(0), 0, 0);
109 if (*ops < 0)
110 return SEL_ERR;
111 return SEL_OK;
114 /*===========================================================================*
115 * select_major_match *
116 *===========================================================================*/
117 PRIVATE int select_major_match(int match_major, struct filp *file)
119 int major;
120 if (!(file && file->filp_vno &&
121 (file->filp_vno->v_mode & I_TYPE) == I_CHAR_SPECIAL))
122 return 0;
123 major = (file->filp_vno->v_sdev >> MAJOR) & BYTE;
124 if (major == match_major)
125 return 1;
126 return 0;
129 /*===========================================================================*
130 * tab2ops *
131 *===========================================================================*/
132 PRIVATE int tab2ops(int fd, struct selectentry *e)
134 return (FD_ISSET(fd, &e->readfds) ? SEL_RD : 0) |
135 (FD_ISSET(fd, &e->writefds) ? SEL_WR : 0) |
136 (FD_ISSET(fd, &e->errorfds) ? SEL_ERR : 0);
139 /*===========================================================================*
140 * ops2tab *
141 *===========================================================================*/
142 PRIVATE void ops2tab(int ops, int fd, struct selectentry *e)
144 if ((ops & SEL_RD) && e->vir_readfds && FD_ISSET(fd, &e->readfds)
145 && !FD_ISSET(fd, &e->ready_readfds)) {
146 FD_SET(fd, &e->ready_readfds);
147 e->nreadyfds++;
149 if ((ops & SEL_WR) && e->vir_writefds && FD_ISSET(fd, &e->writefds)
150 && !FD_ISSET(fd, &e->ready_writefds)) {
151 FD_SET(fd, &e->ready_writefds);
152 e->nreadyfds++;
154 if ((ops & SEL_ERR) && e->vir_errorfds && FD_ISSET(fd, &e->errorfds)
155 && !FD_ISSET(fd, &e->ready_errorfds)) {
156 FD_SET(fd, &e->ready_errorfds);
157 e->nreadyfds++;
160 return;
163 /*===========================================================================*
164 * copy_fdsets *
165 *===========================================================================*/
166 PRIVATE void copy_fdsets(struct selectentry *e)
168 int fd_setsize;
169 if(e->nfds < 0 || e->nfds > OPEN_MAX)
170 panic(__FILE__, "select copy_fdsets: e->nfds wrong", e->nfds);
172 /* Only copy back as many bits as the user expects. */
173 fd_setsize = _FDSETWORDS(e->nfds)*_FDSETBITSPERWORD/8;
175 if (e->vir_readfds)
176 sys_vircopy(SELF, D, (vir_bytes) &e->ready_readfds,
177 e->req_endpt, D, (vir_bytes) e->vir_readfds, fd_setsize);
178 if (e->vir_writefds)
179 sys_vircopy(SELF, D, (vir_bytes) &e->ready_writefds,
180 e->req_endpt, D, (vir_bytes) e->vir_writefds, fd_setsize);
181 if (e->vir_errorfds)
182 sys_vircopy(SELF, D, (vir_bytes) &e->ready_errorfds,
183 e->req_endpt, D, (vir_bytes) e->vir_errorfds, fd_setsize);
186 return;
189 /*===========================================================================*
190 * do_select *
191 *===========================================================================*/
192 PUBLIC int do_select(void)
194 int r, nfds, is_timeout = 1, nonzero_timeout = 0,
195 fd, s, block = 0, fd_setsize;
196 struct timeval timeout;
197 nfds = m_in.SEL_NFDS;
199 if (nfds < 0 || nfds > OPEN_MAX)
200 return EINVAL;
202 for(s = 0; s < MAXSELECTS; s++)
203 if (!selecttab[s].requestor)
204 break;
206 if (s >= MAXSELECTS)
207 return ENOSPC;
209 selecttab[s].req_endpt = who_e;
210 selecttab[s].nfds = 0;
211 selecttab[s].nreadyfds = 0;
212 memset(selecttab[s].filps, 0, sizeof(selecttab[s].filps));
214 /* defaults */
215 FD_ZERO(&selecttab[s].readfds);
216 FD_ZERO(&selecttab[s].writefds);
217 FD_ZERO(&selecttab[s].errorfds);
218 FD_ZERO(&selecttab[s].ready_readfds);
219 FD_ZERO(&selecttab[s].ready_writefds);
220 FD_ZERO(&selecttab[s].ready_errorfds);
222 selecttab[s].vir_readfds = (fd_set *) m_in.SEL_READFDS;
223 selecttab[s].vir_writefds = (fd_set *) m_in.SEL_WRITEFDS;
224 selecttab[s].vir_errorfds = (fd_set *) m_in.SEL_ERRORFDS;
227 /* Copy args. Our storage size is zeroed above. Only copy
228 * as many bits as user has supplied (nfds).
229 * Could be compiled with a different OPEN_MAX or FD_SETSIZE.
230 * If nfds is too large, we have already returned above.
233 fd_setsize = _FDSETWORDS(nfds)*_FDSETBITSPERWORD/8;
234 if (selecttab[s].vir_readfds
235 && (r=sys_vircopy(who_e, D, (vir_bytes) m_in.SEL_READFDS,
236 SELF, D, (vir_bytes) &selecttab[s].readfds, fd_setsize)) != OK)
237 return r;
239 if (selecttab[s].vir_writefds
240 && (r=sys_vircopy(who_e, D, (vir_bytes) m_in.SEL_WRITEFDS,
241 SELF, D, (vir_bytes) &selecttab[s].writefds, fd_setsize)) != OK)
242 return r;
244 if (selecttab[s].vir_errorfds
245 && (r=sys_vircopy(who_e, D, (vir_bytes) m_in.SEL_ERRORFDS,
246 SELF, D, (vir_bytes) &selecttab[s].errorfds, fd_setsize)) != OK)
247 return r;
249 if (!m_in.SEL_TIMEOUT)
250 is_timeout = nonzero_timeout = 0;
251 else
252 if ((r=sys_vircopy(who_e, D, (vir_bytes) m_in.SEL_TIMEOUT,
253 SELF, D, (vir_bytes) &timeout, sizeof(timeout))) != OK)
254 return r;
256 /* No nonsense in the timeval please. */
257 if (is_timeout && (timeout.tv_sec < 0 || timeout.tv_usec < 0))
258 return EINVAL;
260 /* if is_timeout if 0, we block forever. otherwise, if nonzero_timeout
261 * is 0, we do a poll (don't block). otherwise, we block up to the
262 * specified time interval.
264 if (is_timeout && (timeout.tv_sec > 0 || timeout.tv_usec > 0))
265 nonzero_timeout = 1;
267 if (nonzero_timeout || !is_timeout)
268 block = 1;
269 else
270 block = 0; /* timeout set as (0,0) - this effects a poll */
272 /* no timeout set (yet) */
273 selecttab[s].expiry = 0;
275 for(fd = 0; fd < nfds; fd++) {
276 int orig_ops, ops, t, type = -1, r;
277 struct filp *filp;
279 if (!(orig_ops = ops = tab2ops(fd, &selecttab[s])))
280 continue;
281 if (!(filp = selecttab[s].filps[fd] = get_filp(fd))) {
282 select_cancel_all(&selecttab[s]);
283 return EBADF;
286 for(t = 0; t < SEL_FDS; t++) {
287 if (fdtypes[t].select_match) {
288 if (fdtypes[t].select_match(filp)) {
289 #if DEBUG_SELECT
290 printf("select: fd %d is type %d ", fd, t);
291 #endif
292 if (type != -1)
293 printf("select: double match\n");
294 type = t;
296 } else if (select_major_match(fdtypes[t].select_major, filp)) {
297 type = t;
301 /* Open Group:
302 * "The pselect() and select() functions shall support
303 * regular files, terminal and pseudo-terminal devices,
304 * STREAMS-based files, FIFOs, pipes, and sockets. The
305 * behavior of pselect() and select() on file descriptors
306 * that refer to other types of file is unspecified."
308 * If all types are implemented, then this is another
309 * type of file and we get to do whatever we want.
311 if (type == -1)
313 #if DEBUG_SELECT
314 printf("do_select: bad type\n");
315 #endif
316 return EBADF;
319 selecttab[s].type[fd] = type;
321 if ((selecttab[s].filps[fd]->filp_select_ops & ops) != ops) {
322 int wantops;
323 /* Request the select on this fd. */
324 #if DEBUG_SELECT
325 printf("%p requesting ops %d -> ",
326 selecttab[s].filps[fd],
327 selecttab[s].filps[fd]->filp_select_ops);
328 #endif
329 wantops = (selecttab[s].filps[fd]->filp_select_ops |= ops);
330 #if DEBUG_SELECT
331 printf("%d\n", selecttab[s].filps[fd]->filp_select_ops);
332 #endif
333 if ((r = fdtypes[type].select_request(filp,
334 &wantops, block)) != SEL_OK) {
335 /* error or bogus return code.. backpaddle */
336 select_cancel_all(&selecttab[s]);
337 printf("select: select_request returned error\n");
338 return EINVAL;
340 if (wantops) {
341 if (wantops & ops) {
342 /* operations that were just requested
343 * are ready to go right away
345 ops2tab(wantops, fd, &selecttab[s]);
347 /* if there are any other select()s blocking
348 * on these operations of this fp, they can
349 * be awoken too
351 select_callback(filp, ops);
353 #if DEBUG_SELECT
354 printf("select request ok; ops returned %d\n", wantops);
355 #endif
356 } else {
357 #if DEBUG_SELECT
358 printf("select already happening on that filp\n");
359 #endif
362 selecttab[s].nfds = fd+1;
363 selecttab[s].filps[fd]->filp_selectors++;
365 #if DEBUG_SELECT
366 printf("[fd %d ops: %d] ", fd, ops);
367 #endif
370 if (selecttab[s].nreadyfds > 0 || !block) {
371 /* fd's were found that were ready to go right away, and/or
372 * we were instructed not to block at all. Must return
373 * immediately.
375 copy_fdsets(&selecttab[s]);
376 select_cancel_all(&selecttab[s]);
377 selecttab[s].requestor = NULL;
379 /* Open Group:
380 * "Upon successful completion, the pselect() and select()
381 * functions shall return the total number of bits
382 * set in the bit masks."
384 #if DEBUG_SELECT
385 printf("returning\n");
386 #endif
388 return selecttab[s].nreadyfds;
390 #if DEBUG_SELECT
391 printf("not returning (%d, %d)\n", selecttab[s].nreadyfds, block);
392 #endif
394 /* Convert timeval to ticks and set the timer. If it fails, undo
395 * all, return error.
397 if (is_timeout) {
398 int ticks;
399 /* Open Group:
400 * "If the requested timeout interval requires a finer
401 * granularity than the implementation supports, the
402 * actual timeout interval shall be rounded up to the next
403 * supported value."
405 #define USECPERSEC 1000000
406 while(timeout.tv_usec >= USECPERSEC) {
407 /* this is to avoid overflow with *HZ below */
408 timeout.tv_usec -= USECPERSEC;
409 timeout.tv_sec++;
411 ticks = timeout.tv_sec * HZ +
412 (timeout.tv_usec * HZ + USECPERSEC-1) / USECPERSEC;
413 selecttab[s].expiry = ticks;
414 fs_set_timer(&selecttab[s].timer, ticks, select_timeout_check, s);
415 #if DEBUG_SELECT
416 printf("%d: blocking %d ticks\n", s, ticks);
417 #endif
420 /* if we're blocking, the table entry is now valid. */
421 selecttab[s].requestor = fp;
423 /* process now blocked */
424 suspend(XSELECT);
425 return SUSPEND;
428 /*===========================================================================*
429 * select_cancel_all *
430 *===========================================================================*/
431 PRIVATE void select_cancel_all(struct selectentry *e)
433 int fd;
435 for(fd = 0; fd < e->nfds; fd++) {
436 struct filp *fp;
437 fp = e->filps[fd];
438 if (!fp) {
439 #if DEBUG_SELECT
440 printf("[ fd %d/%d NULL ] ", fd, e->nfds);
441 #endif
442 continue;
444 if (fp->filp_selectors < 1) {
445 #if DEBUG_SELECT
446 printf("select: %d selectors?!\n", fp->filp_selectors);
447 #endif
448 continue;
450 fp->filp_selectors--;
451 e->filps[fd] = NULL;
452 select_reevaluate(fp);
455 if (e->expiry > 0) {
456 #if DEBUG_SELECT
457 printf("cancelling timer %d\n", e - selecttab);
458 #endif
459 fs_cancel_timer(&e->timer);
460 e->expiry = 0;
463 return;
466 /*===========================================================================*
467 * select_wakeup *
468 *===========================================================================*/
469 PRIVATE void select_wakeup(struct selectentry *e, int r)
471 revive(e->req_endpt, r);
474 /*===========================================================================*
475 * select_reevaluate *
476 *===========================================================================*/
477 PRIVATE int select_reevaluate(struct filp *fp)
479 int s, remain_ops = 0, fd, type = -1;
481 if (!fp) {
482 printf("fs: select: reevalute NULL fp\n");
483 return 0;
486 for(s = 0; s < MAXSELECTS; s++) {
487 if (!selecttab[s].requestor)
488 continue;
489 for(fd = 0; fd < selecttab[s].nfds; fd++)
490 if (fp == selecttab[s].filps[fd]) {
491 remain_ops |= tab2ops(fd, &selecttab[s]);
492 type = selecttab[s].type[fd];
496 /* If there are any select()s open that want any operations on
497 * this fd that haven't been satisfied by this callback, then we're
498 * still in the market for it.
500 fp->filp_select_ops = remain_ops;
501 #if DEBUG_SELECT
502 printf("remaining operations on fp are %d\n", fp->filp_select_ops);
503 #endif
505 return remain_ops;
508 /*===========================================================================*
509 * select_return *
510 *===========================================================================*/
511 PRIVATE void select_return(struct selectentry *s, int r)
513 select_cancel_all(s);
514 copy_fdsets(s);
515 select_wakeup(s, r ? r : s->nreadyfds);
516 s->requestor = NULL;
519 /*===========================================================================*
520 * select_callback *
521 *===========================================================================*/
522 PUBLIC int select_callback(struct filp *fp, int ops)
524 int s, fd, want_ops, type;
526 /* We are being notified that file pointer fp is available for
527 * operations 'ops'. We must re-register the select for
528 * operations that we are still interested in, if any.
531 want_ops = 0;
532 type = -1;
533 for(s = 0; s < MAXSELECTS; s++) {
534 int wakehim = 0;
535 if (!selecttab[s].requestor)
536 continue;
537 for(fd = 0; fd < selecttab[s].nfds; fd++) {
538 if (!selecttab[s].filps[fd])
539 continue;
540 if (selecttab[s].filps[fd] == fp) {
541 int this_want_ops;
542 this_want_ops = tab2ops(fd, &selecttab[s]);
543 want_ops |= this_want_ops;
544 if (this_want_ops & ops) {
545 /* this select() has been satisfied. */
546 ops2tab(ops, fd, &selecttab[s]);
547 wakehim = 1;
549 type = selecttab[s].type[fd];
552 if (wakehim)
553 select_return(&selecttab[s], 0);
556 return 0;
559 /*===========================================================================*
560 * select_notified *
561 *===========================================================================*/
562 PUBLIC int select_notified(int major, int minor, int selected_ops)
564 int s, f, t;
566 #if DEBUG_SELECT
567 printf("select callback: %d, %d: %d\n", major, minor, selected_ops);
568 #endif
570 for(t = 0; t < SEL_FDS; t++)
571 if (!fdtypes[t].select_match && fdtypes[t].select_major == major)
572 break;
574 if (t >= SEL_FDS) {
575 #if DEBUG_SELECT
576 printf("select callback: no fdtype found for device %d\n", major);
577 #endif
578 return OK;
581 /* We have a select callback from major device no.
582 * d, which corresponds to our select type t.
585 for(s = 0; s < MAXSELECTS; s++) {
586 int s_minor, ops;
587 if (!selecttab[s].requestor)
588 continue;
589 for(f = 0; f < selecttab[s].nfds; f++) {
590 if (!selecttab[s].filps[f] ||
591 !select_major_match(major, selecttab[s].filps[f]))
592 continue;
593 ops = tab2ops(f, &selecttab[s]);
594 s_minor =
595 (selecttab[s].filps[f]->filp_vno->v_sdev >> MINOR)
596 & BYTE;
597 if ((s_minor == minor) &&
598 (selected_ops & ops)) {
599 select_callback(selecttab[s].filps[f], (selected_ops & ops));
604 return OK;
607 /*===========================================================================*
608 * init_select *
609 *===========================================================================*/
610 PUBLIC void init_select(void)
612 int s;
614 for(s = 0; s < MAXSELECTS; s++)
615 fs_init_timer(&selecttab[s].timer);
618 /*===========================================================================*
619 * select_forget *
620 *===========================================================================*/
621 PUBLIC void select_forget(int proc_e)
623 /* something has happened (e.g. signal delivered that interrupts
624 * select()). totally forget about the select().
626 int s;
628 for(s = 0; s < MAXSELECTS; s++) {
629 if (selecttab[s].requestor &&
630 selecttab[s].req_endpt == proc_e) {
631 break;
636 if (s >= MAXSELECTS) {
637 #if DEBUG_SELECT
638 printf("select: cancelled select() not found");
639 #endif
640 return;
643 select_cancel_all(&selecttab[s]);
644 selecttab[s].requestor = NULL;
646 return;
649 /*===========================================================================*
650 * select_timeout_check *
651 *===========================================================================*/
652 PUBLIC void select_timeout_check(timer_t *timer)
654 int s;
656 s = tmr_arg(timer)->ta_int;
658 if (s < 0 || s >= MAXSELECTS) {
659 #if DEBUG_SELECT
660 printf("select: bogus slot arg to watchdog %d\n", s);
661 #endif
662 return;
665 if (!selecttab[s].requestor) {
666 #if DEBUG_SELECT
667 printf("select: no requestor in watchdog\n");
668 #endif
669 return;
672 if (selecttab[s].expiry <= 0) {
673 #if DEBUG_SELECT
674 printf("select: strange expiry value in watchdog\n", s);
675 #endif
676 return;
679 selecttab[s].expiry = 0;
680 select_return(&selecttab[s], 0);
682 return;
685 /*===========================================================================*
686 * select_unsuspend_by_endpt *
687 *===========================================================================*/
688 PUBLIC void select_unsuspend_by_endpt(int proc_e)
690 int fd, s;
692 for(s = 0; s < MAXSELECTS; s++) {
693 if (!selecttab[s].requestor)
694 continue;
695 for(fd = 0; fd < selecttab[s].nfds; fd++) {
696 int maj;
697 if (!selecttab[s].filps[fd] || !selecttab[s].filps[fd]->filp_vno)
698 continue;
699 maj = (selecttab[s].filps[fd]->filp_vno->v_sdev >> MAJOR)&BYTE;
700 if(dmap_driver_match(proc_e, maj)) {
701 select_return(&selecttab[s], EAGAIN);
706 return;