source: trunk/minix/servers/fs/select.c@ 9

Last change on this file since 9 was 9, checked in by Mattia Monga, 13 years ago

Minix 3.1.2a

File size: 19.2 KB
Line 
1/* Implement entry point to select system call.
2 *
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)
11 */
12
13#define DEBUG_SELECT 0
14
15#include "fs.h"
16#include "select.h"
17#include "file.h"
18#include "inode.h"
19
20#include <sys/time.h>
21#include <sys/select.h>
22#include <minix/com.h>
23#include <string.h>
24
25/* max. number of simultaneously pending select() calls */
26#define MAXSELECTS 25
27
28PRIVATE struct selectentry {
29 struct fproc *requestor; /* slot is free iff this is NULL */
30 int req_endpt;
31 fd_set readfds, writefds, errorfds;
32 fd_set ready_readfds, ready_writefds, ready_errorfds;
33 fd_set *vir_readfds, *vir_writefds, *vir_errorfds;
34 struct filp *filps[FD_SETSIZE];
35 int type[FD_SETSIZE];
36 int nfds, nreadyfds;
37 clock_t expiry;
38 timer_t timer; /* if expiry > 0 */
39} selecttab[MAXSELECTS];
40
41#define SELFD_FILE 0
42#define SELFD_PIPE 1
43#define SELFD_TTY 2
44#define SELFD_INET 3
45#define SELFD_LOG 4
46#define SEL_FDS 5
47
48FORWARD _PROTOTYPE(int select_reevaluate, (struct filp *fp));
49
50FORWARD _PROTOTYPE(int select_request_file,
51 (struct filp *f, int *ops, int block));
52FORWARD _PROTOTYPE(int select_match_file, (struct filp *f));
53
54FORWARD _PROTOTYPE(int select_request_general,
55 (struct filp *f, int *ops, int block));
56FORWARD _PROTOTYPE(int select_major_match,
57 (int match_major, struct filp *file));
58
59FORWARD _PROTOTYPE(void select_cancel_all, (struct selectentry *e));
60FORWARD _PROTOTYPE(void select_wakeup, (struct selectentry *e, int r));
61FORWARD _PROTOTYPE(void select_return, (struct selectentry *, int));
62
63/* The Open Group:
64 * "The pselect() and select() functions shall support
65 * regular files, terminal and pseudo-terminal devices,
66 * STREAMS-based files, FIFOs, pipes, and sockets."
67 */
68
69PRIVATE struct fdtype {
70 int (*select_request)(struct filp *, int *ops, int block);
71 int (*select_match)(struct filp *);
72 int select_major;
73} fdtypes[SEL_FDS] = {
74 /* SELFD_FILE */
75 { select_request_file, select_match_file, 0 },
76 /* SELFD_TTY (also PTY) */
77 { select_request_general, NULL, TTY_MAJOR },
78 /* SELFD_INET */
79 { select_request_general, NULL, INET_MAJOR },
80 /* SELFD_PIPE (pipe(2) pipes and FS FIFOs) */
81 { select_request_pipe, select_match_pipe, 0 },
82 /* SELFD_LOG (/dev/klog) */
83 { select_request_general, NULL, LOG_MAJOR },
84};
85
86/* Open Group:
87 * "File descriptors associated with regular files shall always select true
88 * for ready to read, ready to write, and error conditions."
89 */
90
91/*===========================================================================*
92 * select_request_file *
93 *===========================================================================*/
94PRIVATE int select_request_file(struct filp *f, int *ops, int block)
95{
96 /* output *ops is input *ops */
97 return SEL_OK;
98}
99
100/*===========================================================================*
101 * select_match_file *
102 *===========================================================================*/
103PRIVATE int select_match_file(struct filp *file)
104{
105 if (file && file->filp_ino && (file->filp_ino->i_mode & I_REGULAR))
106 return 1;
107 return 0;
108}
109
110/*===========================================================================*
111 * select_request_general *
112 *===========================================================================*/
113PRIVATE int select_request_general(struct filp *f, int *ops, int block)
114{
115 int rops = *ops;
116 if (block) rops |= SEL_NOTIFY;
117 *ops = dev_io(DEV_SELECT, f->filp_ino->i_zone[0], rops, NULL, 0, 0, 0);
118 if (*ops < 0)
119 return SEL_ERR;
120 return SEL_OK;
121}
122
123/*===========================================================================*
124 * select_major_match *
125 *===========================================================================*/
126PRIVATE int select_major_match(int match_major, struct filp *file)
127{
128 int major;
129 if (!(file && file->filp_ino &&
130 (file->filp_ino->i_mode & I_TYPE) == I_CHAR_SPECIAL))
131 return 0;
132 major = (file->filp_ino->i_zone[0] >> MAJOR) & BYTE;
133 if (major == match_major)
134 return 1;
135 return 0;
136}
137
138/*===========================================================================*
139 * tab2ops *
140 *===========================================================================*/
141PRIVATE int tab2ops(int fd, struct selectentry *e)
142{
143 return (FD_ISSET(fd, &e->readfds) ? SEL_RD : 0) |
144 (FD_ISSET(fd, &e->writefds) ? SEL_WR : 0) |
145 (FD_ISSET(fd, &e->errorfds) ? SEL_ERR : 0);
146}
147
148/*===========================================================================*
149 * ops2tab *
150 *===========================================================================*/
151PRIVATE void ops2tab(int ops, int fd, struct selectentry *e)
152{
153 if ((ops & SEL_RD) && e->vir_readfds && FD_ISSET(fd, &e->readfds)
154 && !FD_ISSET(fd, &e->ready_readfds)) {
155 FD_SET(fd, &e->ready_readfds);
156 e->nreadyfds++;
157 }
158 if ((ops & SEL_WR) && e->vir_writefds && FD_ISSET(fd, &e->writefds)
159 && !FD_ISSET(fd, &e->ready_writefds)) {
160 FD_SET(fd, &e->ready_writefds);
161 e->nreadyfds++;
162 }
163 if ((ops & SEL_ERR) && e->vir_errorfds && FD_ISSET(fd, &e->errorfds)
164 && !FD_ISSET(fd, &e->ready_errorfds)) {
165 FD_SET(fd, &e->ready_errorfds);
166 e->nreadyfds++;
167 }
168
169 return;
170}
171
172/*===========================================================================*
173 * copy_fdsets *
174 *===========================================================================*/
175PRIVATE void copy_fdsets(struct selectentry *e)
176{
177 if (e->vir_readfds)
178 sys_vircopy(SELF, D, (vir_bytes) &e->ready_readfds,
179 e->req_endpt, D, (vir_bytes) e->vir_readfds, sizeof(fd_set));
180 if (e->vir_writefds)
181 sys_vircopy(SELF, D, (vir_bytes) &e->ready_writefds,
182 e->req_endpt, D, (vir_bytes) e->vir_writefds, sizeof(fd_set));
183 if (e->vir_errorfds)
184 sys_vircopy(SELF, D, (vir_bytes) &e->ready_errorfds,
185 e->req_endpt, D, (vir_bytes) e->vir_errorfds, sizeof(fd_set));
186
187 return;
188}
189
190/*===========================================================================*
191 * do_select *
192 *===========================================================================*/
193PUBLIC int do_select(void)
194{
195 int r, nfds, is_timeout = 1, nonzero_timeout = 0,
196 fd, s, block = 0;
197 struct timeval timeout;
198 nfds = m_in.SEL_NFDS;
199
200 if (nfds < 0 || nfds > FD_SETSIZE)
201 return EINVAL;
202
203 for(s = 0; s < MAXSELECTS; s++)
204 if (!selecttab[s].requestor)
205 break;
206
207 if (s >= MAXSELECTS)
208 return ENOSPC;
209
210 selecttab[s].req_endpt = who_e;
211 selecttab[s].nfds = 0;
212 selecttab[s].nreadyfds = 0;
213 memset(selecttab[s].filps, 0, sizeof(selecttab[s].filps));
214
215 /* defaults */
216 FD_ZERO(&selecttab[s].readfds);
217 FD_ZERO(&selecttab[s].writefds);
218 FD_ZERO(&selecttab[s].errorfds);
219 FD_ZERO(&selecttab[s].ready_readfds);
220 FD_ZERO(&selecttab[s].ready_writefds);
221 FD_ZERO(&selecttab[s].ready_errorfds);
222
223 selecttab[s].vir_readfds = (fd_set *) m_in.SEL_READFDS;
224 selecttab[s].vir_writefds = (fd_set *) m_in.SEL_WRITEFDS;
225 selecttab[s].vir_errorfds = (fd_set *) m_in.SEL_ERRORFDS;
226
227 /* copy args */
228 if (selecttab[s].vir_readfds
229 && (r=sys_vircopy(who_e, D, (vir_bytes) m_in.SEL_READFDS,
230 SELF, D, (vir_bytes) &selecttab[s].readfds, sizeof(fd_set))) != OK)
231 return r;
232
233 if (selecttab[s].vir_writefds
234 && (r=sys_vircopy(who_e, D, (vir_bytes) m_in.SEL_WRITEFDS,
235 SELF, D, (vir_bytes) &selecttab[s].writefds, sizeof(fd_set))) != OK)
236 return r;
237
238 if (selecttab[s].vir_errorfds
239 && (r=sys_vircopy(who_e, D, (vir_bytes) m_in.SEL_ERRORFDS,
240 SELF, D, (vir_bytes) &selecttab[s].errorfds, sizeof(fd_set))) != OK)
241 return r;
242
243 if (!m_in.SEL_TIMEOUT)
244 is_timeout = nonzero_timeout = 0;
245 else
246 if ((r=sys_vircopy(who_e, D, (vir_bytes) m_in.SEL_TIMEOUT,
247 SELF, D, (vir_bytes) &timeout, sizeof(timeout))) != OK)
248 return r;
249
250 /* No nonsense in the timeval please. */
251 if (is_timeout && (timeout.tv_sec < 0 || timeout.tv_usec < 0))
252 return EINVAL;
253
254 /* if is_timeout if 0, we block forever. otherwise, if nonzero_timeout
255 * is 0, we do a poll (don't block). otherwise, we block up to the
256 * specified time interval.
257 */
258 if (is_timeout && (timeout.tv_sec > 0 || timeout.tv_usec > 0))
259 nonzero_timeout = 1;
260
261 if (nonzero_timeout || !is_timeout)
262 block = 1;
263 else
264 block = 0; /* timeout set as (0,0) - this effects a poll */
265
266 /* no timeout set (yet) */
267 selecttab[s].expiry = 0;
268
269 for(fd = 0; fd < nfds; fd++) {
270 int orig_ops, ops, t, type = -1, r;
271 struct filp *filp;
272
273 if (!(orig_ops = ops = tab2ops(fd, &selecttab[s])))
274 continue;
275 if (!(filp = selecttab[s].filps[fd] = get_filp(fd))) {
276 select_cancel_all(&selecttab[s]);
277 return EBADF;
278 }
279
280 for(t = 0; t < SEL_FDS; t++) {
281 if (fdtypes[t].select_match) {
282 if (fdtypes[t].select_match(filp)) {
283#if DEBUG_SELECT
284 printf("select: fd %d is type %d ", fd, t);
285#endif
286 if (type != -1)
287 printf("select: double match\n");
288 type = t;
289 }
290 } else if (select_major_match(fdtypes[t].select_major, filp)) {
291 type = t;
292 }
293 }
294
295 /* Open Group:
296 * "The pselect() and select() functions shall support
297 * regular files, terminal and pseudo-terminal devices,
298 * STREAMS-based files, FIFOs, pipes, and sockets. The
299 * behavior of pselect() and select() on file descriptors
300 * that refer to other types of file is unspecified."
301 *
302 * If all types are implemented, then this is another
303 * type of file and we get to do whatever we want.
304 */
305 if (type == -1)
306 {
307#if DEBUG_SELECT
308 printf("do_select: bad type\n");
309#endif
310 return EBADF;
311 }
312
313 selecttab[s].type[fd] = type;
314
315 if ((selecttab[s].filps[fd]->filp_select_ops & ops) != ops) {
316 int wantops;
317 /* Request the select on this fd. */
318#if DEBUG_SELECT
319 printf("%p requesting ops %d -> ",
320 selecttab[s].filps[fd],
321 selecttab[s].filps[fd]->filp_select_ops);
322#endif
323 wantops = (selecttab[s].filps[fd]->filp_select_ops |= ops);
324#if DEBUG_SELECT
325 printf("%d\n", selecttab[s].filps[fd]->filp_select_ops);
326#endif
327 if ((r = fdtypes[type].select_request(filp,
328 &wantops, block)) != SEL_OK) {
329 /* error or bogus return code.. backpaddle */
330 select_cancel_all(&selecttab[s]);
331 printf("select: select_request returned error\n");
332 return EINVAL;
333 }
334 if (wantops) {
335 if (wantops & ops) {
336 /* operations that were just requested
337 * are ready to go right away
338 */
339 ops2tab(wantops, fd, &selecttab[s]);
340 }
341 /* if there are any other select()s blocking
342 * on these operations of this fp, they can
343 * be awoken too
344 */
345 select_callback(filp, ops);
346 }
347#if DEBUG_SELECT
348 printf("select request ok; ops returned %d\n", wantops);
349#endif
350 } else {
351#if DEBUG_SELECT
352 printf("select already happening on that filp\n");
353#endif
354 }
355
356 selecttab[s].nfds = fd+1;
357 selecttab[s].filps[fd]->filp_selectors++;
358
359#if DEBUG_SELECT
360 printf("[fd %d ops: %d] ", fd, ops);
361#endif
362 }
363
364 if (selecttab[s].nreadyfds > 0 || !block) {
365 /* fd's were found that were ready to go right away, and/or
366 * we were instructed not to block at all. Must return
367 * immediately.
368 */
369 copy_fdsets(&selecttab[s]);
370 select_cancel_all(&selecttab[s]);
371 selecttab[s].requestor = NULL;
372
373 /* Open Group:
374 * "Upon successful completion, the pselect() and select()
375 * functions shall return the total number of bits
376 * set in the bit masks."
377 */
378#if DEBUG_SELECT
379 printf("returning\n");
380#endif
381
382 return selecttab[s].nreadyfds;
383 }
384#if DEBUG_SELECT
385 printf("not returning (%d, %d)\n", selecttab[s].nreadyfds, block);
386#endif
387
388 /* Convert timeval to ticks and set the timer. If it fails, undo
389 * all, return error.
390 */
391 if (is_timeout) {
392 int ticks;
393 /* Open Group:
394 * "If the requested timeout interval requires a finer
395 * granularity than the implementation supports, the
396 * actual timeout interval shall be rounded up to the next
397 * supported value."
398 */
399#define USECPERSEC 1000000
400 while(timeout.tv_usec >= USECPERSEC) {
401 /* this is to avoid overflow with *HZ below */
402 timeout.tv_usec -= USECPERSEC;
403 timeout.tv_sec++;
404 }
405 ticks = timeout.tv_sec * HZ +
406 (timeout.tv_usec * HZ + USECPERSEC-1) / USECPERSEC;
407 selecttab[s].expiry = ticks;
408 fs_set_timer(&selecttab[s].timer, ticks, select_timeout_check, s);
409#if DEBUG_SELECT
410 printf("%d: blocking %d ticks\n", s, ticks);
411#endif
412 }
413
414 /* if we're blocking, the table entry is now valid. */
415 selecttab[s].requestor = fp;
416
417 /* process now blocked */
418 suspend(XSELECT);
419 return SUSPEND;
420}
421
422/*===========================================================================*
423 * select_cancel_all *
424 *===========================================================================*/
425PRIVATE void select_cancel_all(struct selectentry *e)
426{
427 int fd;
428
429 for(fd = 0; fd < e->nfds; fd++) {
430 struct filp *fp;
431 fp = e->filps[fd];
432 if (!fp) {
433#if DEBUG_SELECT
434 printf("[ fd %d/%d NULL ] ", fd, e->nfds);
435#endif
436 continue;
437 }
438 if (fp->filp_selectors < 1) {
439#if DEBUG_SELECT
440 printf("select: %d selectors?!\n", fp->filp_selectors);
441#endif
442 continue;
443 }
444 fp->filp_selectors--;
445 e->filps[fd] = NULL;
446 select_reevaluate(fp);
447 }
448
449 if (e->expiry > 0) {
450#if DEBUG_SELECT
451 printf("cancelling timer %d\n", e - selecttab);
452#endif
453 fs_cancel_timer(&e->timer);
454 e->expiry = 0;
455 }
456
457 return;
458}
459
460/*===========================================================================*
461 * select_wakeup *
462 *===========================================================================*/
463PRIVATE void select_wakeup(struct selectentry *e, int r)
464{
465 revive(e->req_endpt, r);
466}
467
468/*===========================================================================*
469 * select_reevaluate *
470 *===========================================================================*/
471PRIVATE int select_reevaluate(struct filp *fp)
472{
473 int s, remain_ops = 0, fd, type = -1;
474
475 if (!fp) {
476 printf("fs: select: reevalute NULL fp\n");
477 return 0;
478 }
479
480 for(s = 0; s < MAXSELECTS; s++) {
481 if (!selecttab[s].requestor)
482 continue;
483 for(fd = 0; fd < selecttab[s].nfds; fd++)
484 if (fp == selecttab[s].filps[fd]) {
485 remain_ops |= tab2ops(fd, &selecttab[s]);
486 type = selecttab[s].type[fd];
487 }
488 }
489
490 /* If there are any select()s open that want any operations on
491 * this fd that haven't been satisfied by this callback, then we're
492 * still in the market for it.
493 */
494 fp->filp_select_ops = remain_ops;
495#if DEBUG_SELECT
496 printf("remaining operations on fp are %d\n", fp->filp_select_ops);
497#endif
498
499 return remain_ops;
500}
501
502/*===========================================================================*
503 * select_return *
504 *===========================================================================*/
505PRIVATE void select_return(struct selectentry *s, int r)
506{
507 select_cancel_all(s);
508 copy_fdsets(s);
509 select_wakeup(s, r ? r : s->nreadyfds);
510 s->requestor = NULL;
511}
512
513/*===========================================================================*
514 * select_callback *
515 *===========================================================================*/
516PUBLIC int select_callback(struct filp *fp, int ops)
517{
518 int s, fd, want_ops, type;
519
520 /* We are being notified that file pointer fp is available for
521 * operations 'ops'. We must re-register the select for
522 * operations that we are still interested in, if any.
523 */
524
525 want_ops = 0;
526 type = -1;
527 for(s = 0; s < MAXSELECTS; s++) {
528 int wakehim = 0;
529 if (!selecttab[s].requestor)
530 continue;
531 for(fd = 0; fd < selecttab[s].nfds; fd++) {
532 if (!selecttab[s].filps[fd])
533 continue;
534 if (selecttab[s].filps[fd] == fp) {
535 int this_want_ops;
536 this_want_ops = tab2ops(fd, &selecttab[s]);
537 want_ops |= this_want_ops;
538 if (this_want_ops & ops) {
539 /* this select() has been satisfied. */
540 ops2tab(ops, fd, &selecttab[s]);
541 wakehim = 1;
542 }
543 type = selecttab[s].type[fd];
544 }
545 }
546 if (wakehim)
547 select_return(&selecttab[s], 0);
548 }
549
550 return 0;
551}
552
553/*===========================================================================*
554 * select_notified *
555 *===========================================================================*/
556PUBLIC int select_notified(int major, int minor, int selected_ops)
557{
558 int s, f, t;
559
560#if DEBUG_SELECT
561 printf("select callback: %d, %d: %d\n", major, minor, selected_ops);
562#endif
563
564 for(t = 0; t < SEL_FDS; t++)
565 if (!fdtypes[t].select_match && fdtypes[t].select_major == major)
566 break;
567
568 if (t >= SEL_FDS) {
569#if DEBUG_SELECT
570 printf("select callback: no fdtype found for device %d\n", major);
571#endif
572 return OK;
573 }
574
575 /* We have a select callback from major device no.
576 * d, which corresponds to our select type t.
577 */
578
579 for(s = 0; s < MAXSELECTS; s++) {
580 int s_minor, ops;
581 if (!selecttab[s].requestor)
582 continue;
583 for(f = 0; f < selecttab[s].nfds; f++) {
584 if (!selecttab[s].filps[f] ||
585 !select_major_match(major, selecttab[s].filps[f]))
586 continue;
587 ops = tab2ops(f, &selecttab[s]);
588 s_minor =
589 (selecttab[s].filps[f]->filp_ino->i_zone[0] >> MINOR)
590 & BYTE;
591 if ((s_minor == minor) &&
592 (selected_ops & ops)) {
593 select_callback(selecttab[s].filps[f], (selected_ops & ops));
594 }
595 }
596 }
597
598 return OK;
599}
600
601/*===========================================================================*
602 * init_select *
603 *===========================================================================*/
604PUBLIC void init_select(void)
605{
606 int s;
607
608 for(s = 0; s < MAXSELECTS; s++)
609 fs_init_timer(&selecttab[s].timer);
610}
611
612/*===========================================================================*
613 * select_forget *
614 *===========================================================================*/
615PUBLIC void select_forget(int proc_e)
616{
617 /* something has happened (e.g. signal delivered that interrupts
618 * select()). totally forget about the select().
619 */
620 int s;
621
622 for(s = 0; s < MAXSELECTS; s++) {
623 if (selecttab[s].requestor &&
624 selecttab[s].req_endpt == proc_e) {
625 break;
626 }
627
628 }
629
630 if (s >= MAXSELECTS) {
631#if DEBUG_SELECT
632 printf("select: cancelled select() not found");
633#endif
634 return;
635 }
636
637 select_cancel_all(&selecttab[s]);
638 selecttab[s].requestor = NULL;
639
640 return;
641}
642
643/*===========================================================================*
644 * select_timeout_check *
645 *===========================================================================*/
646PUBLIC void select_timeout_check(timer_t *timer)
647{
648 int s;
649
650 s = tmr_arg(timer)->ta_int;
651
652 if (s < 0 || s >= MAXSELECTS) {
653#if DEBUG_SELECT
654 printf("select: bogus slot arg to watchdog %d\n", s);
655#endif
656 return;
657 }
658
659 if (!selecttab[s].requestor) {
660#if DEBUG_SELECT
661 printf("select: no requestor in watchdog\n");
662#endif
663 return;
664 }
665
666 if (selecttab[s].expiry <= 0) {
667#if DEBUG_SELECT
668 printf("select: strange expiry value in watchdog\n", s);
669#endif
670 return;
671 }
672
673 selecttab[s].expiry = 0;
674 select_return(&selecttab[s], 0);
675
676 return;
677}
678
679/*===========================================================================*
680 * select_unsuspend_by_endpt *
681 *===========================================================================*/
682PUBLIC void select_unsuspend_by_endpt(int proc_e)
683{
684 int fd, s;
685
686 for(s = 0; s < MAXSELECTS; s++) {
687 if (!selecttab[s].requestor)
688 continue;
689 for(fd = 0; fd < selecttab[s].nfds; fd++) {
690 int maj;
691 if (!selecttab[s].filps[fd] || !selecttab[s].filps[fd]->filp_ino)
692 continue;
693 maj = (selecttab[s].filps[fd]->filp_ino->i_zone[0] >> MAJOR)&BYTE;
694 if(dmap_driver_match(proc_e, maj)) {
695 select_return(&selecttab[s], EAGAIN);
696 }
697 }
698 }
699
700 return;
701}
702
Note: See TracBrowser for help on using the repository browser.