source: branches/minix3-book/servers/fs/select.c@ 12

Last change on this file since 12 was 4, checked in by Mattia Monga, 14 years ago

Importazione sorgenti libro

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