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
10 * 6 june 2005 Created (Ben Gras)
13 #define DEBUG_SELECT 0
21 #include <sys/select.h>
22 #include <minix/com.h>
23 #include <minix/u64.h>
26 /* max. number of simultaneously pending select() calls */
29 PRIVATE
struct selectentry
{
30 struct fproc
*requestor
; /* slot is free iff this is NULL */
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
];
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));
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
*);
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]))
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 */
90 /*===========================================================================*
92 *===========================================================================*/
93 PRIVATE
int select_match_file(struct filp
*file
)
95 if (file
&& file
->filp_vno
&& (file
->filp_vno
->v_mode
& I_REGULAR
))
100 /*===========================================================================*
101 * select_request_general *
102 *===========================================================================*/
103 PRIVATE
int select_request_general(struct filp
*f
, int *ops
, int block
)
106 if (block
) rops
|= SEL_NOTIFY
;
107 *ops
= dev_io(VFS_DEV_SELECT
, f
->filp_vno
->v_sdev
, rops
, NULL
,
114 /*===========================================================================*
115 * select_major_match *
116 *===========================================================================*/
117 PRIVATE
int select_major_match(int match_major
, struct filp
*file
)
120 if (!(file
&& file
->filp_vno
&&
121 (file
->filp_vno
->v_mode
& I_TYPE
) == I_CHAR_SPECIAL
))
123 major
= (file
->filp_vno
->v_sdev
>> MAJOR
) & BYTE
;
124 if (major
== match_major
)
129 /*===========================================================================*
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 /*===========================================================================*
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
);
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
);
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
);
163 /*===========================================================================*
165 *===========================================================================*/
166 PRIVATE
void copy_fdsets(struct selectentry
*e
)
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;
176 sys_vircopy(SELF
, D
, (vir_bytes
) &e
->ready_readfds
,
177 e
->req_endpt
, D
, (vir_bytes
) e
->vir_readfds
, fd_setsize
);
179 sys_vircopy(SELF
, D
, (vir_bytes
) &e
->ready_writefds
,
180 e
->req_endpt
, D
, (vir_bytes
) e
->vir_writefds
, fd_setsize
);
182 sys_vircopy(SELF
, D
, (vir_bytes
) &e
->ready_errorfds
,
183 e
->req_endpt
, D
, (vir_bytes
) e
->vir_errorfds
, fd_setsize
);
189 /*===========================================================================*
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
)
202 for(s
= 0; s
< MAXSELECTS
; s
++)
203 if (!selecttab
[s
].requestor
)
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
));
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
)
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
)
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
)
249 if (!m_in
.SEL_TIMEOUT
)
250 is_timeout
= nonzero_timeout
= 0;
252 if ((r
=sys_vircopy(who_e
, D
, (vir_bytes
) m_in
.SEL_TIMEOUT
,
253 SELF
, D
, (vir_bytes
) &timeout
, sizeof(timeout
))) != OK
)
256 /* No nonsense in the timeval please. */
257 if (is_timeout
&& (timeout
.tv_sec
< 0 || timeout
.tv_usec
< 0))
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))
267 if (nonzero_timeout
|| !is_timeout
)
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
;
279 if (!(orig_ops
= ops
= tab2ops(fd
, &selecttab
[s
])))
281 if (!(filp
= selecttab
[s
].filps
[fd
] = get_filp(fd
))) {
282 select_cancel_all(&selecttab
[s
]);
286 for(t
= 0; t
< SEL_FDS
; t
++) {
287 if (fdtypes
[t
].select_match
) {
288 if (fdtypes
[t
].select_match(filp
)) {
290 printf("select: fd %d is type %d ", fd
, t
);
293 printf("select: double match\n");
296 } else if (select_major_match(fdtypes
[t
].select_major
, filp
)) {
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.
314 printf("do_select: bad type\n");
319 selecttab
[s
].type
[fd
] = type
;
321 if ((selecttab
[s
].filps
[fd
]->filp_select_ops
& ops
) != ops
) {
323 /* Request the select on this fd. */
325 printf("%p requesting ops %d -> ",
326 selecttab
[s
].filps
[fd
],
327 selecttab
[s
].filps
[fd
]->filp_select_ops
);
329 wantops
= (selecttab
[s
].filps
[fd
]->filp_select_ops
|= ops
);
331 printf("%d\n", selecttab
[s
].filps
[fd
]->filp_select_ops
);
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");
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
351 select_callback(filp
, ops
);
354 printf("select request ok; ops returned %d\n", wantops
);
358 printf("select already happening on that filp\n");
362 selecttab
[s
].nfds
= fd
+1;
363 selecttab
[s
].filps
[fd
]->filp_selectors
++;
366 printf("[fd %d ops: %d] ", fd
, ops
);
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
375 copy_fdsets(&selecttab
[s
]);
376 select_cancel_all(&selecttab
[s
]);
377 selecttab
[s
].requestor
= NULL
;
380 * "Upon successful completion, the pselect() and select()
381 * functions shall return the total number of bits
382 * set in the bit masks."
385 printf("returning\n");
388 return selecttab
[s
].nreadyfds
;
391 printf("not returning (%d, %d)\n", selecttab
[s
].nreadyfds
, block
);
394 /* Convert timeval to ticks and set the timer. If it fails, undo
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
405 #define USECPERSEC 1000000
406 while(timeout
.tv_usec
>= USECPERSEC
) {
407 /* this is to avoid overflow with *HZ below */
408 timeout
.tv_usec
-= USECPERSEC
;
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
);
416 printf("%d: blocking %d ticks\n", s
, ticks
);
420 /* if we're blocking, the table entry is now valid. */
421 selecttab
[s
].requestor
= fp
;
423 /* process now blocked */
428 /*===========================================================================*
429 * select_cancel_all *
430 *===========================================================================*/
431 PRIVATE
void select_cancel_all(struct selectentry
*e
)
435 for(fd
= 0; fd
< e
->nfds
; fd
++) {
440 printf("[ fd %d/%d NULL ] ", fd
, e
->nfds
);
444 if (fp
->filp_selectors
< 1) {
446 printf("select: %d selectors?!\n", fp
->filp_selectors
);
450 fp
->filp_selectors
--;
452 select_reevaluate(fp
);
457 printf("cancelling timer %d\n", e
- selecttab
);
459 fs_cancel_timer(&e
->timer
);
466 /*===========================================================================*
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;
482 printf("fs: select: reevalute NULL fp\n");
486 for(s
= 0; s
< MAXSELECTS
; s
++) {
487 if (!selecttab
[s
].requestor
)
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
;
502 printf("remaining operations on fp are %d\n", fp
->filp_select_ops
);
508 /*===========================================================================*
510 *===========================================================================*/
511 PRIVATE
void select_return(struct selectentry
*s
, int r
)
513 select_cancel_all(s
);
515 select_wakeup(s
, r
? r
: s
->nreadyfds
);
519 /*===========================================================================*
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.
533 for(s
= 0; s
< MAXSELECTS
; s
++) {
535 if (!selecttab
[s
].requestor
)
537 for(fd
= 0; fd
< selecttab
[s
].nfds
; fd
++) {
538 if (!selecttab
[s
].filps
[fd
])
540 if (selecttab
[s
].filps
[fd
] == fp
) {
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
]);
549 type
= selecttab
[s
].type
[fd
];
553 select_return(&selecttab
[s
], 0);
559 /*===========================================================================*
561 *===========================================================================*/
562 PUBLIC
int select_notified(int major
, int minor
, int selected_ops
)
567 printf("select callback: %d, %d: %d\n", major
, minor
, selected_ops
);
570 for(t
= 0; t
< SEL_FDS
; t
++)
571 if (!fdtypes
[t
].select_match
&& fdtypes
[t
].select_major
== major
)
576 printf("select callback: no fdtype found for device %d\n", major
);
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
++) {
587 if (!selecttab
[s
].requestor
)
589 for(f
= 0; f
< selecttab
[s
].nfds
; f
++) {
590 if (!selecttab
[s
].filps
[f
] ||
591 !select_major_match(major
, selecttab
[s
].filps
[f
]))
593 ops
= tab2ops(f
, &selecttab
[s
]);
595 (selecttab
[s
].filps
[f
]->filp_vno
->v_sdev
>> MINOR
)
597 if ((s_minor
== minor
) &&
598 (selected_ops
& ops
)) {
599 select_callback(selecttab
[s
].filps
[f
], (selected_ops
& ops
));
607 /*===========================================================================*
609 *===========================================================================*/
610 PUBLIC
void init_select(void)
614 for(s
= 0; s
< MAXSELECTS
; s
++)
615 fs_init_timer(&selecttab
[s
].timer
);
618 /*===========================================================================*
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().
628 for(s
= 0; s
< MAXSELECTS
; s
++) {
629 if (selecttab
[s
].requestor
&&
630 selecttab
[s
].req_endpt
== proc_e
) {
636 if (s
>= MAXSELECTS
) {
638 printf("select: cancelled select() not found");
643 select_cancel_all(&selecttab
[s
]);
644 selecttab
[s
].requestor
= NULL
;
649 /*===========================================================================*
650 * select_timeout_check *
651 *===========================================================================*/
652 PUBLIC
void select_timeout_check(timer_t
*timer
)
656 s
= tmr_arg(timer
)->ta_int
;
658 if (s
< 0 || s
>= MAXSELECTS
) {
660 printf("select: bogus slot arg to watchdog %d\n", s
);
665 if (!selecttab
[s
].requestor
) {
667 printf("select: no requestor in watchdog\n");
672 if (selecttab
[s
].expiry
<= 0) {
674 printf("select: strange expiry value in watchdog\n", s
);
679 selecttab
[s
].expiry
= 0;
680 select_return(&selecttab
[s
], 0);
685 /*===========================================================================*
686 * select_unsuspend_by_endpt *
687 *===========================================================================*/
688 PUBLIC
void select_unsuspend_by_endpt(int proc_e
)
692 for(s
= 0; s
< MAXSELECTS
; s
++) {
693 if (!selecttab
[s
].requestor
)
695 for(fd
= 0; fd
< selecttab
[s
].nfds
; fd
++) {
697 if (!selecttab
[s
].filps
[fd
] || !selecttab
[s
].filps
[fd
]->filp_vno
)
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
);