Started select() implementation.

Added interface to select() for pipes (also named pipes), and select()
stubs for regular files.

Added timer library in FS that select() is the first customer of.


This is unfinished, but committed anyway to get a new release out to
Al and testers.
This commit is contained in:
Ben Gras 2005-06-17 13:41:12 +00:00
parent 471e0448c3
commit 9664ba0c4f
13 changed files with 845 additions and 54 deletions

View file

@ -11,12 +11,12 @@ h = $i/minix
CC = exec cc
CFLAGS = -I$i
LDFLAGS = -i
LIBS = -lsys -lutils
LIBS = -lsys -lutils -ltimers
OBJ = main.o open.o read.o write.o pipe.o dmap.o \
device.o path.o mount.o link.o super.o inode.o \
cache.o cache2.o filedes.o stadir.o protect.o time.o \
cmostime.o lock.o misc.o utility.o select.o table.o
cmostime.o lock.o misc.o utility.o select.o timers.o table.o
# build local binary
all build: $(SERVER)

View file

@ -60,6 +60,7 @@ int only_search; /* if NO_READ, don't read, else act normal */
/* Block needed has been found. */
if (bp->b_count == 0) rm_lru(bp);
bp->b_count++; /* record that block is in use */
return(bp);
} else {
/* This block is not the one sought. */
@ -114,7 +115,9 @@ int only_search; /* if NO_READ, don't read, else act normal */
#endif
if (only_search == PREFETCH) bp->b_dev = NO_DEV;
else
if (only_search == NORMAL) rw_block(bp, READING);
if (only_search == NORMAL) {
rw_block(bp, READING);
}
}
return(bp); /* return the newly acquired block */
}
@ -175,8 +178,9 @@ int block_type; /* INODE_BLOCK, DIRECTORY_BLOCK, or whatever */
* should be written to the disk immediately to avoid messing up the file
* system in the event of a crash.
*/
if ((block_type & WRITE_IMMED) && bp->b_dirt==DIRTY && bp->b_dev != NO_DEV)
rw_block(bp, WRITING);
if ((block_type & WRITE_IMMED) && bp->b_dirt==DIRTY && bp->b_dev != NO_DEV) {
rw_block(bp, WRITING);
}
}

View file

@ -36,9 +36,10 @@
#define NO_READ 1 /* prevents get_block from doing disk read */
#define PREFETCH 2 /* tells get_block not to read or mark dev */
#define XPIPE (-NR_TASKS-1) /* used in fp_task when susp'd on pipe */
#define XLOCK (-NR_TASKS-2) /* used in fp_task when susp'd on lock */
#define XPOPEN (-NR_TASKS-3) /* used in fp_task when susp'd on pipe open */
#define XPIPE (-NR_TASKS-1) /* used in fp_task when susp'd on pipe */
#define XLOCK (-NR_TASKS-2) /* used in fp_task when susp'd on lock */
#define XPOPEN (-NR_TASKS-3) /* used in fp_task when susp'd on pipe open */
#define XSELECT (-NR_TASKS-4) /* used in fp_task when susp'd on select */
#define NO_BIT ((bit_t) 0) /* returned by alloc_bit() to signal failure */

View file

@ -282,14 +282,17 @@ message *mess_ptr; /* pointer to message for task */
* trying to send a REVIVE message for an earlier request.
* Handle it and go try again.
*/
if ((r = receive(task_nr, &local_m)) != OK) break;
if ((r = receive(task_nr, &local_m)) != OK) {
break;
}
/* If we're trying to send a cancel message to a task which has just
* sent a completion reply, ignore the reply and abort the cancel
* request. The caller will do the revive for the process.
*/
if (mess_ptr->m_type == CANCEL && local_m.REP_PROC_NR == proc_nr)
if (mess_ptr->m_type == CANCEL && local_m.REP_PROC_NR == proc_nr) {
return;
}
/* Otherwise it should be a REVIVE. */
if (local_m.m_type != REVIVE) {
@ -313,7 +316,9 @@ message *mess_ptr; /* pointer to message for task */
}
/* Did the process we did the sendrec() for get a result? */
if (mess_ptr->REP_PROC_NR == proc_nr) break;
if (mess_ptr->REP_PROC_NR == proc_nr) {
break;
}
/* Otherwise it should be a REVIVE. */
if (mess_ptr->m_type != REVIVE) {

View file

@ -8,6 +8,15 @@ EXTERN struct filp {
int filp_count; /* how many file descriptors share this slot?*/
struct inode *filp_ino; /* pointer to the inode */
off_t filp_pos; /* file position */
/* the following fields are for select() and are owned by the generic
* select() code (i.e., fd-type-specific select() code can't touch these).
*/
int filp_selectors; /* select()ing processes blocking on this fd */
int filp_select_ops; /* interested in these SEL_* operations */
/* following are for fd-type-specific select() */
int filp_pipe_select_ops;
} filp[NR_FILPS];
#define FILP_CLOSED 0 /* filp_mode: associated device closed */

View file

@ -47,6 +47,9 @@ struct filp **fpt; /* place to return filp slot */
if (f->filp_count == 0) {
f->filp_mode = bits;
f->filp_pos = 0L;
f->filp_selectors = 0;
f->filp_select_ops = 0;
f->filp_pipe_select_ops = 0;
f->filp_flags = 0;
*fpt = f;
return(OK);

3
servers/fs/fs_timers.h Normal file
View file

@ -0,0 +1,3 @@
#include <timers.h>

View file

@ -64,22 +64,29 @@ PUBLIC void main()
if (call_nr == HARD_STOP) {
do_sync();
sys_exit(0); /* never returns */
}
} else if(call_nr == SYN_ALARM) {
/* Not a user request; system has expired one of our timers,
* currently only in use for select(). Check it.
*/
fs_expire_timers(m_in.NOTIFY_ARG);
} else if(call_nr == DEV_SELECTED) {
/* device notify()s us of fd that has become usable */
select_notified(&m_in);
} else {
/* Call the internal function that does the work. */
if (call_nr < 0 || call_nr >= NCALLS) {
error = ENOSYS;
printf("FS, warning illegal %d system call by %d\n", call_nr, who);
} else {
error = (*call_vec[call_nr])();
}
/* Call the internal function that does the work. */
if (call_nr < 0 || call_nr >= NCALLS) {
error = ENOSYS;
printf("FS, warning illegal %d system call by %d\n", call_nr, who);
} else {
error = (*call_vec[call_nr])();
/* Copy the results back to the user and send reply. */
if (error != SUSPEND) { reply(who, error); }
if (rdahed_inode != NIL_INODE) {
read_ahead(); /* do block read ahead */
}
}
/* Copy the results back to the user and send reply. */
if (error != SUSPEND) { reply(who, error); }
if (rdahed_inode != NIL_INODE) {
read_ahead(); /* do block read ahead */
}
}
}
@ -158,7 +165,7 @@ int result; /* result of the call (usually OK or error #) */
int s;
m_out.reply_type = result;
s = send(whom, &m_out);
if (s != OK) printf("FS: couldn't send reply: %d\n", s);
if (s != OK) printf("FS: couldn't send reply %d: %d\n", result, s);
}

View file

@ -18,12 +18,14 @@
#include <signal.h>
#include <minix/callnr.h>
#include <minix/com.h>
#include <sys/select.h>
#include "dmap.h"
#include "file.h"
#include "fproc.h"
#include "inode.h"
#include "param.h"
#include "super.h"
#include "select.h"
/*===========================================================================*
* do_pipe *
@ -82,13 +84,14 @@ PUBLIC int do_pipe()
/*===========================================================================*
* pipe_check *
*===========================================================================*/
PUBLIC int pipe_check(rip, rw_flag, oflags, bytes, position, canwrite)
PUBLIC int pipe_check(rip, rw_flag, oflags, bytes, position, canwrite, notouch)
register struct inode *rip; /* the inode of the pipe */
int rw_flag; /* READING or WRITING */
int oflags; /* flags set by open or fcntl */
register int bytes; /* bytes to be read or written (all chunks) */
register off_t position; /* current file position */
int *canwrite; /* return: number of bytes we can write */
int notouch; /* check only */
{
/* Pipes are a little different. If a process reads from an empty pipe for
* which a writer still exists, suspend the reader. If the pipe is empty
@ -106,11 +109,13 @@ int *canwrite; /* return: number of bytes we can write */
if (oflags & O_NONBLOCK) {
r = EAGAIN;
} else {
suspend(XPIPE); /* block reader */
if(!notouch)
suspend(XPIPE); /* block reader */
r = SUSPEND;
}
/* If need be, activate sleeping writers. */
if (susp_count > 0) release(rip, WRITE, susp_count);
if (susp_count > 0 && !notouch)
release(rip, WRITE, susp_count);
}
return(r);
}
@ -118,7 +123,8 @@ int *canwrite; /* return: number of bytes we can write */
/* Process is writing to a pipe. */
if (find_filp(rip, R_BIT) == NIL_FILP) {
/* Tell kernel to generate a SIGPIPE signal. */
sys_kill((int)(fp - fproc), SIGPIPE);
if(!notouch)
sys_kill((int)(fp - fproc), SIGPIPE);
return(EPIPE);
}
@ -128,7 +134,8 @@ int *canwrite; /* return: number of bytes we can write */
else if ((oflags & O_NONBLOCK) && bytes > PIPE_SIZE(rip->i_sp->s_block_size)) {
if ( (*canwrite = (PIPE_SIZE(rip->i_sp->s_block_size) - position)) > 0) {
/* Do a partial write. Need to wakeup reader */
release(rip, READ, susp_count);
if(!notouch)
release(rip, READ, susp_count);
return(1);
} else {
return(EAGAIN);
@ -143,12 +150,14 @@ int *canwrite; /* return: number of bytes we can write */
return(1);
}
}
suspend(XPIPE); /* stop writer -- pipe full */
if(!notouch)
suspend(XPIPE); /* stop writer -- pipe full */
return(SUSPEND);
}
/* Writing to an empty pipe. Search for suspended reader. */
if (position == 0) release(rip, READ, susp_count);
if (position == 0 && !notouch)
release(rip, READ, susp_count);
}
*canwrite = 0;
@ -197,6 +206,25 @@ int count; /* max number of processes to release */
*/
register struct fproc *rp;
struct filp *f;
/* Trying to perform the call also includes SELECTing on it with that
* operation.
*/
if(call_nr == READ || call_nr == WRITE) {
int op;
if(call_nr == READ)
op = SEL_RD;
else
op = SEL_WR;
for(f = &filp[0]; f < &filp[NR_FILPS]; f++) {
if(f->filp_count < 1 || !(f->filp_pipe_select_ops & op) ||
f->filp_ino != ip)
continue;
select_callback(f, op);
f->filp_pipe_select_ops &= ~op;
}
}
/* Search the proc table. */
for (rp = &fproc[0]; rp < &fproc[NR_PROCS]; rp++) {
@ -215,9 +243,9 @@ int count; /* max number of processes to release */
/*===========================================================================*
* revive *
*===========================================================================*/
PUBLIC void revive(proc_nr, bytes)
PUBLIC void revive(proc_nr, returned)
int proc_nr; /* process to revive */
int bytes; /* if hanging on task, how many bytes read */
int returned; /* if hanging on task, how many bytes read */
{
/* Revive a previously blocked process. When a process hangs on tty, this
* is the way it is eventually released.
@ -232,8 +260,8 @@ int bytes; /* if hanging on task, how many bytes read */
/* The 'reviving' flag only applies to pipes. Processes waiting for TTY get
* a message right away. The revival process is different for TTY and pipes.
* For TTY revival, the work is already done, for pipes it is not: the proc
* must be restarted so it can try again.
* For select and TTY revival, the work is already done, for pipes it is not:
* the proc must be restarted so it can try again.
*/
task = -rfp->fp_task;
if (task == XPIPE || task == XLOCK) {
@ -244,10 +272,12 @@ int bytes; /* if hanging on task, how many bytes read */
rfp->fp_suspended = NOT_SUSPENDED;
if (task == XPOPEN) /* process blocked in open or create */
reply(proc_nr, rfp->fp_fd>>8);
else {
else if(task == XSELECT) {
reply(proc_nr, returned);
} else {
/* Revive a process suspended on TTY or other device. */
rfp->fp_nbytes = bytes; /*pretend it wants only what there is*/
reply(proc_nr, bytes); /* unblock the process */
rfp->fp_nbytes = returned; /*pretend it wants only what there is*/
reply(proc_nr, returned); /* unblock the process */
}
}
}
@ -282,6 +312,10 @@ PUBLIC int do_unpause()
case XLOCK: /* process trying to set a lock with FCNTL */
break;
case XSELECT: /* process blocking on select() */
select_forget(proc_nr);
break;
case XPOPEN: /* process trying to open a fifo */
break;
@ -305,3 +339,46 @@ PUBLIC int do_unpause()
reply(proc_nr, EINTR); /* signal interrupted call */
return(OK);
}
/*===========================================================================*
* select_request_pipe *
*===========================================================================*/
PUBLIC int select_request_pipe(struct filp *f, int *ops, int block)
{
int orig_ops, r = 0, err, canwrite;
orig_ops = *ops;
if((*ops & SEL_RD)) {
if((err = pipe_check(f->filp_ino, READING, 0,
1, f->filp_pos, &canwrite, 1)) != SUSPEND)
r |= SEL_RD;
if(err < 0 && err != SUSPEND && (*ops & SEL_ERR))
r |= SEL_ERR;
}
if((*ops & SEL_WR)) {
if((err = pipe_check(f->filp_ino, WRITING, 0,
1, f->filp_pos, &canwrite, 1)) != SUSPEND)
r |= SEL_WR;
if(err < 0 && err != SUSPEND && (*ops & SEL_ERR))
r |= SEL_ERR;
}
*ops = r;
if(!r && block) {
f->filp_pipe_select_ops |= orig_ops;
}
return SEL_OK;
}
/*===========================================================================*
* select_match_pipe *
*===========================================================================*/
PUBLIC int select_match_pipe(struct filp *f)
{
/* recognize either pipe or named pipe (FIFO) */
if(f && f->filp_ino && (f->filp_ino->i_mode & I_NAMED_PIPE))
return 1;
return 0;
}

View file

@ -1,5 +1,7 @@
/* Function prototypes. */
#include "timers.h"
/* Structs used in prototypes must be declared as such first. */
struct buf;
struct filp;
@ -113,10 +115,13 @@ _PROTOTYPE( struct inode *last_dir, (char *path, char string [NAME_MAX]));
_PROTOTYPE( int do_pipe, (void) );
_PROTOTYPE( int do_unpause, (void) );
_PROTOTYPE( int pipe_check, (struct inode *rip, int rw_flag,
int oflags, int bytes, off_t position, int *canwrite));
int oflags, int bytes, off_t position, int *canwrite, int notouch));
_PROTOTYPE( void release, (struct inode *ip, int call_nr, int count) );
_PROTOTYPE( void revive, (int proc_nr, int bytes) );
_PROTOTYPE( void suspend, (int task) );
_PROTOTYPE( int select_request_pipe, (struct filp *f, int *ops, int bl) );
_PROTOTYPE( int select_cancel_pipe, (struct filp *f) );
_PROTOTYPE( int select_match_pipe, (struct filp *f) );
/* protect.c */
_PROTOTYPE( int do_access, (void) );
@ -174,4 +179,12 @@ _PROTOTYPE( void zero_block, (struct buf *bp) );
/* select.c */
_PROTOTYPE( int do_select, (void) );
_PROTOTYPE( int select_callback, (struct filp *, int ops) );
_PROTOTYPE( void select_forget, (int fproc) );
_PROTOTYPE( void select_timeout_check, (timer_t *) );
_PROTOTYPE( int select_notified, (message *) );
/* timers.c */
_PROTOTYPE( void fs_set_timer, (timer_t *tp, int delta, tmr_func_t watchdog, int arg));
_PROTOTYPE( void fs_expire_timers, (clock_t now));
_PROTOTYPE( void fs_cancel_timer, (timer_t *tp));

View file

@ -21,6 +21,13 @@
#include "param.h"
#include "super.h"
#define VVIRCOPY 0
#if VVIRCOPY
#define NOVVIRCOPY 0
#else
#define NOVVIRCOPY 1
#endif
FORWARD _PROTOTYPE( int rw_chunk, (struct inode *rip, off_t position,
unsigned off, int chunk, unsigned left, int rw_flag,
@ -79,8 +86,10 @@ int rw_flag; /* READING or WRITING */
* it means something has gone wrong we can't repair now.
*/
if(copy_queue_used != 0) {
panic(__FILE__,"copy queue size nonzero when entering read_write().",
copy_queue_used);
panic(__FILE__,"start - copy queue size nonzero", copy_queue_used);
}
if(bufs_in_use < 0) {
panic(__FILE__,"start - bufs_in_use negative", bufs_in_use);
}
/* MM loads segments by putting funny things in upper 10 bits of 'fd'. */
@ -167,8 +176,9 @@ int rw_flag; /* READING or WRITING */
/* Pipes are a little different. Check. */
if (rip->i_pipe == I_PIPE) {
r = pipe_check(rip,rw_flag,oflags,
m_in.nbytes,position,&partial_cnt);
struct filp *other_end;
r = pipe_check(rip, rw_flag, oflags,
m_in.nbytes, position, &partial_cnt, 0);
if (r <= 0) return(r);
}
@ -210,7 +220,7 @@ int rw_flag; /* READING or WRITING */
}
}
#if 0
#if VVIRCOPY
/* do copying to/from user space */
r2 = rw_chunk_finish(&completed);
#endif
@ -221,11 +231,14 @@ int rw_flag; /* READING or WRITING */
if (position > f_size) rip->i_size = position;
}
} else {
if (rip->i_pipe == I_PIPE && position >= rip->i_size) {
/* Reset pipe pointers. */
rip->i_size = 0; /* no data left */
position = 0; /* reset reader(s) */
if ( (wf = find_filp(rip, W_BIT)) != NIL_FILP) wf->filp_pos =0;
if (rip->i_pipe == I_PIPE) {
if( position >= rip->i_size) {
/* Reset pipe pointers. */
rip->i_size = 0; /* no data left */
position = 0; /* reset reader(s) */
wf = find_filp(rip, W_BIT);
if (wf != NIL_FILP) wf->filp_pos = 0;
}
}
}
f->filp_pos = position;
@ -262,6 +275,12 @@ int rw_flag; /* READING or WRITING */
fp->fp_cum_io_partial = 0;
return(cum_io);
}
if(copy_queue_used != 0) {
panic(__FILE__,"end - copy queue size nonzero", copy_queue_used);
}
if(bufs_in_use < 0) {
panic(__FILE__,"end - bufs_in_use negative", bufs_in_use);
}
return(r);
}
@ -332,7 +351,7 @@ int *completed; /* number of bytes copied */
zero_block(bp);
}
#if 1
#if NOVVIRCOPY
if (rw_flag == READING) {
/* Copy a chunk from the block buffer to user space. */
r = sys_vircopy(FS_PROC_NR, D, (phys_bytes) (bp->b_data+off),
@ -416,7 +435,6 @@ PRIVATE int rw_chunk_finish(int *completed)
user->segment = copy_queue[i].user_seg;
user->offset = copy_queue[i].user_offset;
total += copy_queue[i].chunk;
put_block(copy_queue[i].bp, copy_queue[i].blocktype);
}
m.m_type = SYS_VIRVCOPY;
@ -427,6 +445,10 @@ PRIVATE int rw_chunk_finish(int *completed)
panic(__FILE__,"rw_chunk_finish: virvcopy sendrec failed", r);
}
for(i = 0; i < copy_queue_used; i++) {
put_block(copy_queue[i].bp, copy_queue[i].blocktype);
}
*completed = total;
copy_queue_used = 0;

View file

@ -1,16 +1,653 @@
/* Implement entry point to select system call.
*
* The entry points into this file are
* do_select: perform the SELECT system call
* do_select: perform the SELECT system call
* select_callback: notify select system of possible fd operation
* select_notified: low-level entry for device notifying select
*/
/* TODO: check if close (pipe?) / exit works;
* some printf()s are serious errors;
* check combinations of cases listen in open group select
* spec (various NULLs and behaviours);
* pty support in tty
* make select cancel disappearing fp's
*/
#define DEBUG_SELECT 1
#include "fs.h"
#include "select.h"
#include "file.h"
#include "inode.h"
#include "fs_timers.h"
#include <sys/time.h>
#include <sys/select.h>
#include <minix/com.h>
#include <string.h>
/* max. number of simultaneously pending select() calls */
#define MAXSELECTS 25
PRIVATE struct selectentry {
struct fproc *requestor; /* slot is free iff this is NULL */
int req_procnr;
fd_set readfds, writefds, errorfds;
fd_set ready_readfds, ready_writefds, ready_errorfds;
fd_set *vir_readfds, *vir_writefds, *vir_errorfds;
struct filp *filps[FD_SETSIZE];
int type[FD_SETSIZE];
int nfds, nreadyfds;
clock_t expiry;
timer_t timer; /* if expiry > 0 */
} selecttab[MAXSELECTS];
#define SELFD_FILE 0
#define SELFD_PIPE 1
#define SELFD_TTY 2
#define SELFD_INET 3
#define SEL_FDS 4
FORWARD _PROTOTYPE(int select_reevaluate, (struct filp *fp));
FORWARD _PROTOTYPE(int select_request_file, (struct filp *f, int *ops, int block));
FORWARD _PROTOTYPE(int select_match_file, (struct filp *f));
FORWARD _PROTOTYPE(int select_request_tty, (struct filp *f, int *ops, int block));
FORWARD _PROTOTYPE(int select_match_tty, (struct filp *f));
FORWARD _PROTOTYPE(int select_request_inet, (struct filp *f, int *ops, int block));
FORWARD _PROTOTYPE(int select_match_inet, (struct filp *f));
FORWARD _PROTOTYPE(void select_cancel_all, (struct selectentry *e));
FORWARD _PROTOTYPE(int select_wakeup, (struct selectentry *e));
/* The Open Group:
* "The pselect() and select() functions shall support
* regular files, terminal and pseudo-terminal devices,
* STREAMS-based files, FIFOs, pipes, and sockets."
*/
PRIVATE struct fdtype {
int (*select_request)(struct filp *, int *ops, int block);
int (*select_match)(struct filp *);
} fdtypes[SEL_FDS] = {
/* SELFD_FILE */
{ select_request_file, select_match_file },
/* SELFD_TTY (also PTY) */
{ select_request_tty, select_match_tty },
/* SELFD_INET */
{ select_request_inet, select_match_inet },
/* SELFD_PIPE (pipe(2) pipes and FS FIFOs) */
{ select_request_pipe, select_match_pipe },
};
/* Open Group:
* "File descriptors associated with regular files shall always select true
* for ready to read, ready to write, and error conditions."
*/
/*===========================================================================*
* select_request_file *
*===========================================================================*/
PRIVATE int select_request_file(struct filp *f, int *ops, int block)
{
/* output *ops is input *ops */
return SEL_OK;
}
/*===========================================================================*
* select_match_file *
*===========================================================================*/
PRIVATE int select_match_file(struct filp *file)
{
if(file && file->filp_ino && (file->filp_ino->i_mode & I_REGULAR))
return 1;
return 0;
}
/*===========================================================================*
* select_request_tty *
*===========================================================================*/
PRIVATE int select_request_tty(struct filp *f, int *ops, int block)
{
int r, rops;
rops = *ops;
if(block) rops |= SEL_NOTIFY;
*ops = dev_io(DEV_SELECT, f->filp_ino->i_zone[0], rops, NULL, 0, 0, 0);
if(*ops < 0)
return SEL_ERR;
return SEL_OK;
}
/*===========================================================================*
* select_match_tty *
*===========================================================================*/
PRIVATE int select_match_tty(struct filp *file)
{
int major;
if(!(file && file->filp_ino &&
(file->filp_ino->i_mode & I_TYPE) == I_CHAR_SPECIAL))
return 0;
major = (file->filp_ino->i_zone[0] >> MAJOR) & BYTE;
if(major == TTY_MAJOR || major == CTTY_MAJOR)
return 1;
return 0;
}
/*===========================================================================*
* select_request_inet *
*===========================================================================*/
PRIVATE int select_request_inet(struct filp *f, int *ops, int block)
{
int r, rops;
rops = *ops;
if(block) rops |= SEL_NOTIFY;
*ops = dev_io(DEV_SELECT, f->filp_ino->i_zone[0], rops, NULL, 0, 0, 0);
if(*ops < 0)
return SEL_ERR;
return SEL_OK;
}
/*===========================================================================*
* select_match_inet *
*===========================================================================*/
PRIVATE int select_match_inet(struct filp *file)
{
int major;
if(!(file && file->filp_ino &&
(file->filp_ino->i_mode & I_TYPE) == I_CHAR_SPECIAL))
return 0;
major = (file->filp_ino->i_zone[0] >> MAJOR) & BYTE;
if(major == INET_MAJOR)
printf("inet minor: %d\n",
(file->filp_ino->i_zone[0] & BYTE));
/* return 1; */
return 0;
}
PRIVATE int tab2ops(int fd, struct selectentry *e)
{
return (FD_ISSET(fd, &e->readfds) ? SEL_RD : 0) |
(FD_ISSET(fd, &e->writefds) ? SEL_WR : 0) |
(FD_ISSET(fd, &e->errorfds) ? SEL_ERR : 0);
}
PRIVATE void ops2tab(int ops, int fd, struct selectentry *e)
{
if((ops & SEL_RD) && e->vir_readfds && FD_ISSET(fd, &e->readfds)
&& !FD_ISSET(fd, &e->ready_readfds)) {
FD_SET(fd, &e->ready_readfds);
e->nreadyfds++;
}
if((ops & SEL_WR) && e->vir_writefds && FD_ISSET(fd, &e->writefds)
&& !FD_ISSET(fd, &e->ready_writefds)) {
FD_SET(fd, &e->ready_writefds);
e->nreadyfds++;
}
if((ops & SEL_ERR) && e->vir_errorfds && FD_ISSET(fd, &e->errorfds)
&& !FD_ISSET(fd, &e->ready_errorfds)) {
FD_SET(fd, &e->ready_errorfds);
e->nreadyfds++;
}
return;
}
PRIVATE void copy_fdsets(struct selectentry *e)
{
if(e->vir_readfds)
sys_vircopy(SELF, D, (vir_bytes) &e->ready_readfds,
e->req_procnr, D, (vir_bytes) e->vir_readfds, sizeof(fd_set));
if(e->vir_writefds)
sys_vircopy(SELF, D, (vir_bytes) &e->ready_writefds,
e->req_procnr, D, (vir_bytes) e->vir_writefds, sizeof(fd_set));
if(e->vir_errorfds)
sys_vircopy(SELF, D, (vir_bytes) &e->ready_errorfds,
e->req_procnr, D, (vir_bytes) e->vir_errorfds, sizeof(fd_set));
return;
}
/*===========================================================================*
* do_select *
*===========================================================================*/
PUBLIC int do_select(void)
{
int r, nfds, is_timeout = 1, nonzero_timeout = 0,
fd, s, block = 0;
struct timeval timeout;
nfds = m_in.SEL_NFDS;
if(nfds < 0 || nfds > FD_SETSIZE)
return EINVAL;
for(s = 0; s < MAXSELECTS; s++)
if(!selecttab[s].requestor)
break;
if(s >= MAXSELECTS)
return ENOSPC;
selecttab[s].req_procnr = who;
selecttab[s].nfds = 0;
selecttab[s].nreadyfds = 0;
memset(selecttab[s].filps, 0, sizeof(selecttab[s].filps));
/* defaults */
FD_ZERO(&selecttab[s].readfds);
FD_ZERO(&selecttab[s].writefds);
FD_ZERO(&selecttab[s].errorfds);
FD_ZERO(&selecttab[s].ready_readfds);
FD_ZERO(&selecttab[s].ready_writefds);
FD_ZERO(&selecttab[s].ready_errorfds);
selecttab[s].vir_readfds = (fd_set *) m_in.SEL_READFDS;
selecttab[s].vir_writefds = (fd_set *) m_in.SEL_WRITEFDS;
selecttab[s].vir_errorfds = (fd_set *) m_in.SEL_ERRORFDS;
/* copy args */
if(selecttab[s].vir_readfds && (r=sys_vircopy(who, D, (vir_bytes) m_in.SEL_READFDS,
SELF, D, (vir_bytes) &selecttab[s].readfds, sizeof(fd_set))) != OK)
return r;
if(selecttab[s].vir_writefds && (r=sys_vircopy(who, D, (vir_bytes) m_in.SEL_WRITEFDS,
SELF, D, (vir_bytes) &selecttab[s].writefds, sizeof(fd_set))) != OK)
return r;
if(selecttab[s].vir_errorfds && (r=sys_vircopy(who, D, (vir_bytes) m_in.SEL_ERRORFDS,
SELF, D, (vir_bytes) &selecttab[s].errorfds, sizeof(fd_set))) != OK)
return r;
if(!m_in.SEL_TIMEOUT)
is_timeout = nonzero_timeout = 0;
else
if((r=sys_vircopy(who, D, (vir_bytes) m_in.SEL_TIMEOUT,
SELF, D, (vir_bytes) &timeout, sizeof(timeout))) != OK)
return r;
/* No nonsense in the timeval please. */
if(is_timeout && (timeout.tv_sec < 0 || timeout.tv_usec < 0))
return EINVAL;
/* if is_timeout if 0, we block forever. otherwise, if nonzero_timeout
* is 0, we do a poll (don't block). otherwise, we block up to the
* specified time interval.
*/
if(is_timeout && (timeout.tv_sec > 0 || timeout.tv_usec > 0))
nonzero_timeout = 1;
if(nonzero_timeout || !is_timeout)
block = 1;
else
block = 0; /* timeout set as (0,0) - this effects a poll */
/* no timeout set (yet) */
selecttab[s].expiry = 0;
for(fd = 0; fd < nfds; fd++) {
int orig_ops, ops, t, type = -1, r;
struct filp *filp;
if(!(orig_ops = ops = tab2ops(fd, &selecttab[s])))
continue;
if(!(filp = selecttab[s].filps[fd] = get_filp(fd))) {
select_cancel_all(&selecttab[s]);
return EBADF;
}
for(t = 0; t < SEL_FDS; t++) {
if(fdtypes[t].select_match(filp)) {
#if DEBUG_SELECT
printf("select: fd %d is type %d ", fd, t);
#endif
if(type != -1)
printf("select: double match\n");
type = t;
}
}
/* Open Group:
* "The pselect() and select() functions shall support
* regular files, terminal and pseudo-terminal devices,
* STREAMS-based files, FIFOs, pipes, and sockets. The
* behavior of pselect() and select() on file descriptors
* that refer to other types of file is unspecified."
*
* If all types are implemented, then this is another
* type of file and we get to do whatever we want.
*/
if(type == -1)
return EBADF;
selecttab[s].type[fd] = type;
if((selecttab[s].filps[fd]->filp_select_ops & ops) != ops) {
int wantops;
/* Request the select on this fd. */
#if DEBUG_SELECT
printf("%p requesting ops %d -> ",
selecttab[s].filps[fd],
selecttab[s].filps[fd]->filp_select_ops);
#endif
wantops = (selecttab[s].filps[fd]->filp_select_ops |= ops);
#if DEBUG_SELECT
printf("%d\n", selecttab[s].filps[fd]->filp_select_ops);
#endif
if((r = fdtypes[type].select_request(filp,
&wantops, block)) != SEL_OK) {
/* error or bogus return code.. backpaddle */
select_cancel_all(&selecttab[s]);
printf("select: select_request returned error\n");
return EINVAL;
}
if(wantops) {
if(wantops & ops) {
/* operations that were just requested
* are ready to go right away
*/
ops2tab(wantops, fd, &selecttab[s]);
}
/* if there are any other select()s blocking
* on these operations of this fp, they can
* be awoken too
*/
select_callback(filp, ops);
}
#if DEBUG_SELECT
printf("select request ok; ops returned %d\n", wantops);
#endif
} else {
#if DEBUG_SELECT
printf("select already happening on that filp\n");
#endif
}
selecttab[s].nfds = fd+1;
selecttab[s].filps[fd]->filp_selectors++;
#if DEBUG_SELECT
printf("[fd %d ops: %d] ", fd, ops);
#endif
}
if(selecttab[s].nreadyfds > 0 || !block) {
/* fd's were found that were ready to go right away, and/or
* we were instructed not to block at all. Must return
* immediately.
*/
copy_fdsets(&selecttab[s]);
select_cancel_all(&selecttab[s]);
selecttab[s].requestor = NULL;
/* Open Group:
* "Upon successful completion, the pselect() and select()
* functions shall return the total number of bits
* set in the bit masks."
*/
return selecttab[s].nreadyfds;
}
/* Convert timeval to ticks and set the timer. If it fails, undo
* all, return error.
*/
if(is_timeout) {
int ticks;
/* Open Group:
* "If the requested timeout interval requires a finer
* granularity than the implementation supports, the
* actual timeout interval shall be rounded up to the next
* supported value."
*/
#define USECPERSEC 1000000
while(timeout.tv_usec >= USECPERSEC) {
/* this is to avoid overflow with *HZ below */
timeout.tv_usec -= USECPERSEC;
timeout.tv_sec++;
}
ticks = timeout.tv_sec * HZ +
(timeout.tv_usec * HZ + USECPERSEC-1) / USECPERSEC;
selecttab[s].expiry = ticks;
fs_set_timer(&selecttab[s].timer, ticks, select_timeout_check, s);
#if DEBUG_SELECT
printf("%d: blocking %d ticks\n", s, ticks);
#endif
}
/* if we're blocking, the table entry is now valid. */
selecttab[s].requestor = fp;
/* process now blocked */
suspend(XSELECT);
return SUSPEND;
}
PRIVATE void select_cancel_all(struct selectentry *e)
{
int fd;
for(fd = 0; fd < e->nfds; fd++) {
struct filp *fp;
fp = e->filps[fd];
if(!fp) {
#if DEBUG_SELECT
printf("[ fd %d/%d NULL ] ", fd, e->nfds);
#endif
continue;
}
if(fp->filp_selectors < 1) {
#if DEBUG_SELECT
printf("select: %d selectors?!\n", fp->filp_selectors);
#endif
continue;
}
fp->filp_selectors--;
e->filps[fd] = NULL;
select_reevaluate(fp);
}
if(e->expiry > 0) {
#if DEBUG_SELECT
printf("cancelling timer %d\n", e - selecttab);
#endif
fs_cancel_timer(&e->timer);
e->expiry = 0;
}
return;
}
PRIVATE int select_wakeup(struct selectentry *e)
{
/* Open Group:
* "Upon successful completion, the pselect() and select()
* functions shall return the total number of bits
* set in the bit masks."
*/
revive(e->req_procnr, e->nreadyfds);
return;
}
PRIVATE int select_reevaluate(struct filp *fp)
{
int r;
int s, remain_ops = 0, fd, type = -1;
int want_ops;
if(!fp) {
printf("fs: select: reevalute NULL fp\n");
return 0;
}
for(s = 0; s < MAXSELECTS; s++) {
if(!selecttab[s].requestor)
continue;
for(fd = 0; fd < selecttab[s].nfds; fd++)
if(fp == selecttab[s].filps[fd]) {
remain_ops |= tab2ops(fd, &selecttab[s]);
type = selecttab[s].type[fd];
}
}
/* If there are any select()s open that want any operations on
* this fd that haven't been satisfied by this callback, then we're
* still in the market for it.
*/
fp->filp_select_ops = remain_ops;
#if DEBUG_SELECT
printf("remaining operations on fp are %d\n", fp->filp_select_ops);
#endif
return remain_ops;
}
/*===========================================================================*
* int select_callback *
*===========================================================================*/
PUBLIC int select_callback(struct filp *fp, int ops)
{
int s, f, fd, want_ops, remain_ops, type;
/* We are being notified that file pointer fp is available for
* operations 'ops'. We must re-register the select for
* operations that we are still interested in, if any.
*/
restart_callback:
want_ops = 0;
type = -1;
for(s = 0; s < MAXSELECTS; s++) {
int wakehim = 0;
if(!selecttab[s].requestor)
continue;
for(fd = 0; fd < selecttab[s].nfds; fd++) {
if(!selecttab[s].filps[fd])
continue;
if(selecttab[s].filps[fd] == fp) {
int this_want_ops;
this_want_ops = tab2ops(fd, &selecttab[s]);
want_ops |= this_want_ops;
if(this_want_ops & ops) {
/* this select() has been satisfied. */
ops2tab(ops, fd, &selecttab[s]);
wakehim = 1;
}
type = selecttab[s].type[fd];
}
}
if(wakehim) {
select_cancel_all(&selecttab[s]);
copy_fdsets(&selecttab[s]);
selecttab[s].requestor = NULL;
select_wakeup(&selecttab[s]);
}
}
return 0;
}
/*===========================================================================*
* int select_notified *
*===========================================================================*/
PUBLIC int select_notified(message *m)
{
int s, f;
switch(m->m_source) {
case TTY:
#if DEBUG_SELECT
printf("fs: select: tty notification\n");
#endif
for(s = 0; s < MAXSELECTS; s++) {
int line, ops;
if(!selecttab[s].requestor)
continue;
for(f = 0; f < selecttab[s].nfds; f++) {
if(!selecttab[s].filps[f] ||
!select_match_tty(selecttab[s].filps[f]))
continue;
ops = tab2ops(f, &selecttab[s]);
line = selecttab[s].filps[f]->filp_ino->i_zone[0] & BYTE;
if((line == m->NOTIFY_ARG) &&
(m->NOTIFY_FLAGS & ops)) {
#if DEBUG_SELECT
printf("fs: select: tty notification matched\n");
#endif
select_callback(selecttab[s].filps[f], ops);
}
}
}
break;
default:
printf("fs: select: unrecognized select reply\n");
}
return OK;
}
/*===========================================================================*
* int select_forget *
*===========================================================================*/
PUBLIC void select_forget(int proc)
{
/* something has happened (e.g. signal delivered that interrupts
* select()). totally forget about the select().
*/
int s;
for(s = 0; s < MAXSELECTS; s++) {
if(selecttab[s].requestor &&
selecttab[s].req_procnr == proc) {
break;
}
}
if(s >= MAXSELECTS) {
printf("select: cancelled select() not found");
return;
}
select_cancel_all(&selecttab[s]);
selecttab[s].requestor = NULL;
return;
}
/*===========================================================================*
* int select_timeout_check *
*===========================================================================*/
PUBLIC void select_timeout_check(timer_t *timer)
{
int s, r;
clock_t now;
s = tmr_arg(timer)->ta_int;
if(s < 0 || s >= MAXSELECTS) {
printf("select: bogus slot arg to watchdog %d\n", s);
return;
}
if(!selecttab[s].requestor) {
printf("select: no requestor in watchdog\n");
return;
}
if(selecttab[s].expiry <= 0) {
printf("select: strange expiry value in watchdog\n", s);
return;
}
selecttab[s].expiry = 0;
copy_fdsets(&selecttab[s]);
select_cancel_all(&selecttab[s]);
selecttab[s].requestor = NULL;
select_wakeup(&selecttab[s]);
return;
}

10
servers/fs/select.h Normal file
View file

@ -0,0 +1,10 @@
#ifndef _FS_SELECT_H
#define _FS_SELECT_H 1
/* return codes for select_request_* and select_cancel_* */
#define SEL_OK 0 /* ready */
#define SEL_ERROR 1 /* failed */
#endif