From 2ec762c60c5d404af041931ea467e794a35796cb Mon Sep 17 00:00:00 2001 From: Philip Homburg Date: Fri, 22 Feb 2008 15:41:07 +0000 Subject: [PATCH] Asynchronous communication with character specials. --- servers/vfs/device.c | 526 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 449 insertions(+), 77 deletions(-) diff --git a/servers/vfs/device.c b/servers/vfs/device.c index 36b449b2d..58ceabbe5 100644 --- a/servers/vfs/device.c +++ b/servers/vfs/device.c @@ -40,6 +40,7 @@ FORWARD _PROTOTYPE( int safe_io_conversion, (endpoint_t, void **, int *, vir_bytes, u32_t *)); FORWARD _PROTOTYPE( void safe_io_cleanup, (cp_grant_id_t, cp_grant_id_t *, int)); +FORWARD _PROTOTYPE( void restart_reopen, (int maj) ); extern int dmap_size; PRIVATE int dummyproc; @@ -66,7 +67,33 @@ int flags; /* mode bits and flags */ if (dp->dmap_driver == NONE) return ENXIO; r = (*dp->dmap_opcl)(DEV_OPEN, dev, proc, flags); - if (r == SUSPEND) panic(__FILE__,"suspend on open from", dp->dmap_driver); + return(r); +} + + +/*===========================================================================* + * dev_reopen * + *===========================================================================*/ +PUBLIC int dev_reopen(dev, filp_no, flags) +dev_t dev; /* device to open */ +int filp_no; /* filp to reopen for */ +int flags; /* mode bits and flags */ +{ + int major, r; + struct dmap *dp; + + /* Determine the major device number call the device class specific + * open/close routine. (This is the only routine that must check the + * device number for being in range. All others can trust this check.) + */ + major = (dev >> MAJOR) & BYTE; + if (major >= NR_DEVICES) major = 0; + dp = &dmap[major]; + if (dp->dmap_driver == NONE) + return ENXIO; + r = (*dp->dmap_opcl)(DEV_REOPEN, dev, filp_no, flags); + if (r == OK) panic(__FILE__,"OK on reopen from", dp->dmap_driver); + if (r == SUSPEND) r= OK; return(r); } @@ -74,14 +101,18 @@ int flags; /* mode bits and flags */ /*===========================================================================* * dev_close * *===========================================================================*/ -PUBLIC void dev_close(dev) +PUBLIC int dev_close(dev, filp_no) dev_t dev; /* device to close */ +int filp_no; { + int r; + /* See if driver is roughly valid. */ if (dmap[(dev >> MAJOR)].dmap_driver == NONE) { - return; + return ENXIO; } - (void) (*dmap[(dev >> MAJOR) & BYTE].dmap_opcl)(DEV_CLOSE, dev, 0, 0); + r= (*dmap[(dev >> MAJOR) & BYTE].dmap_opcl)(DEV_CLOSE, dev, filp_no, 0); + return r; } /*===========================================================================* @@ -122,6 +153,14 @@ PUBLIC void dev_status(message *m) if (d >= NR_DEVICES) return; + if (dmap[d].dmap_async_driver) + { + printf( + "dev_status: not doing dev_status for async driver %d\n", + m->m_source); + return; + } + do { int r; st.m_type = DEV_STATUS; @@ -307,7 +346,7 @@ int gids_size; /*===========================================================================* * dev_io * *===========================================================================*/ -PUBLIC int dev_io(op, dev, proc_e, buf, pos, bytes, flags) +PUBLIC int dev_io(op, dev, proc_e, buf, pos, bytes, flags, suspend_reopen) int op; /* DEV_READ, DEV_WRITE, DEV_IOCTL, etc. */ dev_t dev; /* major-minor device number */ int proc_e; /* in whose address space is buf? */ @@ -315,6 +354,7 @@ void *buf; /* virtual address of the buffer */ u64_t pos; /* byte position */ int bytes; /* how many bytes to transfer */ int flags; /* special flags, like O_NONBLOCK */ +int suspend_reopen; /* Just suspend the process */ { /* Read or write from a device. The parameter 'dev' tells which one. */ struct dmap *dp; @@ -339,6 +379,16 @@ int flags; /* special flags, like O_NONBLOCK */ return ENXIO; } + if (suspend_reopen) + { + /* Suspend user. */ + fp->fp_grant = GRANT_INVALID; + fp->fp_ioproc = NONE; + suspend(dp->dmap_driver); + fp->fp_flags |= SUSP_REOPEN; + return(SUSPEND); + } + if(isokendpt(dp->dmap_driver, &dummyproc) != OK) { printf("FS: dev_io: old driver for dev %x (%d)\n", dev, dp->dmap_driver); @@ -392,7 +442,7 @@ int flags; /* special flags, like O_NONBLOCK */ if(!fp) panic(__FILE__,"SUSPEND on NULL fp", NO_NUM); - if (flags & O_NONBLOCK) { + if ((flags & O_NONBLOCK) && !dp->dmap_async_driver) { /* Not supposed to block. */ dev_mess.m_type = CANCEL; dev_mess.IO_ENDPT = ioproc; @@ -413,6 +463,24 @@ int flags; /* special flags, like O_NONBLOCK */ assert(!GRANT_VALID(fp->fp_grant)); fp->fp_grant = gid; /* revoke this when unsuspended. */ fp->fp_ioproc = ioproc; + + if (flags & O_NONBLOCK) { + /* Not supposed to block, send cancel message */ + dev_mess.m_type = CANCEL; + dev_mess.IO_ENDPT = ioproc; + dev_mess.IO_GRANT = (char *) gid; + + /* This R_BIT/W_BIT check taken from suspend()/unpause() + * logic. Mode is expected in the COUNT field. + */ + dev_mess.COUNT = 0; + if(call_nr == READ) dev_mess.COUNT = R_BIT; + else if(call_nr == WRITE) dev_mess.COUNT = W_BIT; + dev_mess.DEVICE = (dev >> MINOR) & BYTE; + (*dp->dmap_io)(dp->dmap_driver, &dev_mess); + + /* Should do something about EINTR -> EAGAIN mapping */ + } return(SUSPEND); } } @@ -433,6 +501,7 @@ int proc_e; /* process to open/close for */ int flags; /* mode bits and flags */ { /* Called from the dmap struct in table.c on opens & closes of special files.*/ + int r; struct dmap *dp; message dev_mess; @@ -450,7 +519,8 @@ int flags; /* mode bits and flags */ } /* Call the task. */ - (*dp->dmap_io)(dp->dmap_driver, &dev_mess); + r= (*dp->dmap_io)(dp->dmap_driver, &dev_mess); + if (r != OK) return r; return(dev_mess.REP_STATUS); } @@ -534,6 +604,7 @@ PUBLIC int do_ioctl() { /* Perform the ioctl(ls_fd, request, argx) system call (uses m2 fmt). */ + int suspend_reopen; struct filp *f; register struct vnode *vp; dev_t dev; @@ -542,10 +613,11 @@ PUBLIC int do_ioctl() vp = f->filp_vno; /* get vnode pointer */ if ( (vp->v_mode & I_TYPE) != I_CHAR_SPECIAL && (vp->v_mode & I_TYPE) != I_BLOCK_SPECIAL) return(ENOTTY); + suspend_reopen= (f->filp_state != FS_NORMAL); dev = (dev_t) vp->v_sdev; return (dev_io(VFS_DEV_IOCTL, dev, who_e, m_in.ADDRESS, cvu64(0), - m_in.REQUEST, f->filp_flags)); + m_in.REQUEST, f->filp_flags, suspend_reopen)); } /*===========================================================================* @@ -591,6 +663,30 @@ message *mess_ptr; /* pointer to message for task */ return OK; } +/*===========================================================================* + * asyn_io * + *===========================================================================*/ +PUBLIC int asyn_io(task_nr, mess_ptr) +int task_nr; /* which task to call */ +message *mess_ptr; /* pointer to message for task */ +{ +/* All file system I/O ultimately comes down to I/O on major/minor device + * pairs. These lead to calls on the following routines via the dmap table. + */ + + int r, proc_e; + + proc_e = mess_ptr->IO_ENDPT; + + r= asynsend(task_nr, mess_ptr); + if (r != OK) + panic(__FILE__, "asyn_io: asynsend failed", r); + + /* Fake a SUSPEND */ + mess_ptr->REP_STATUS= SUSPEND; + return OK; +} + /*===========================================================================* * ctty_io * *===========================================================================*/ @@ -758,77 +854,353 @@ int flags; /* mode bits and flags */ *===========================================================================*/ PUBLIC void dev_up(int maj) { - /* A new device driver has been mapped in. This function - * checks if any filesystems are mounted on it, and if so, - * dev_open()s them so the filesystem can be reused. - */ - struct filp *fp; - struct vmnt *vmp; - int r, new_driver_e; - message m; - - /* Open a device once for every filp that's opened on it, - * and once for every filesystem mounted from it. - */ - new_driver_e = dmap[maj].dmap_driver; - - for (vmp = &vmnt[0]; vmp < &vmnt[NR_MNTS]; ++vmp) { - int minor; - if (vmp->m_dev == NO_DEV) continue; - if ( ((vmp->m_dev >> MAJOR) & BYTE) != maj) continue; - minor = ((vmp->m_dev >> MINOR) & BYTE); - - printf("VFS: re-opening dev: %d/%d\n", maj, minor); - - if ((r = dev_open(vmp->m_dev, FS_PROC_NR, - vmp->m_flags ? R_BIT : (R_BIT|W_BIT))) != OK) { - printf("VFS: mounted dev %d/%d re-open failed: %d.\n", - maj, minor, r); - } + /* A new device driver has been mapped in. This function + * checks if any filesystems are mounted on it, and if so, + * dev_open()s them so the filesystem can be reused. + */ + int r, new_driver_e, needs_reopen, fd_nr; + struct filp *fp; + struct vmnt *vmp; + struct fproc *rfp; + struct vnode *vp; + message m; + + /* Open a device once for every filp that's opened on it, + * and once for every filesystem mounted from it. + */ + new_driver_e = dmap[maj].dmap_driver; + + for (vmp = &vmnt[0]; vmp < &vmnt[NR_MNTS]; ++vmp) { + int minor; + if (vmp->m_dev == NO_DEV) continue; + if ( ((vmp->m_dev >> MAJOR) & BYTE) != maj) continue; + minor = ((vmp->m_dev >> MINOR) & BYTE); + + if ((r = dev_open(vmp->m_dev, FS_PROC_NR, + vmp->m_flags ? R_BIT : (R_BIT|W_BIT))) != OK) { + printf("VFS: mounted dev %d/%d re-open failed: %d.\n", + maj, minor, r); + } - /* Send new driver endpoint */ - printf("VFS: sending new driver for dev: %d, endpoint: %d, FS_e: %d\n", - vmp->m_dev, new_driver_e, vmp->m_fs_e); - - if (OK != req_newdriver(vmp->m_fs_e, vmp->m_dev, new_driver_e)) -printf("VFSdev_up: error sending new driver endpoint. FS_e: %d req_nr: %d\n", - vmp->m_fs_e, REQ_NEW_DRIVER); - else - vmp->m_driver_e = new_driver_e; - } + /* Send new driver endpoint */ + if (OK != req_newdriver(vmp->m_fs_e, vmp->m_dev, new_driver_e)) + printf( + "VFSdev_up: error sending new driver endpoint. FS_e: %d req_nr: %d\n", + vmp->m_fs_e, REQ_NEW_DRIVER); + else + vmp->m_driver_e = new_driver_e; + } - for (fp = filp; fp < &filp[NR_FILPS]; fp++) { - struct vnode *vp; - int minor; - - if(fp->filp_count < 1 || !(vp = fp->filp_vno)) continue; - if(((vp->v_sdev >> MAJOR) & BYTE) != maj) continue; - if(!(vp->v_mode & (I_BLOCK_SPECIAL|I_CHAR_SPECIAL))) continue; - - minor = ((vp->v_sdev >> MINOR) & BYTE); - - printf("vfs:dev_up: reopening special %d/%d..\n", maj, minor); - - printf("vfs:dev_up: before dev_open\n"); - r = dev_open(vp->v_sdev, FS_PROC_NR, vp->v_mode & (R_BIT|W_BIT)); - printf("vfs:dev_up: after dev_open: result = %d\n", r); - if (r != OK) { - int n; - /* This function will set the fp_filp[]s of processes - * holding that fp to NULL, but _not_ clear - * fp_filp_inuse, so that fd can't be recycled until - * it's close()d. - */ - n = inval_filp(fp); - if(n != fp->filp_count) - printf("VFS: warning: invalidate/count " - "discrepancy (%d, %d)\n", n, fp->filp_count); - fp->filp_count = 0; - printf("VFS: file on dev %d/%d re-open failed: %d; " - "invalidated %d fd's.\n", maj, minor, r, n); - } - } + /* Look for processes that are suspened in an OPEN call. Set SUSP_REOPEN + * to indicate that this process was suspended before the call to dev_up. + */ + for (rfp = &fproc[0]; rfp < &fproc[NR_PROCS]; rfp++) { + if(rfp->fp_pid == PID_FREE) + continue; + if(rfp->fp_suspended != SUSPENDED || rfp->fp_task != -XDOPEN) + continue; + + printf("dev_up: found process in XDOPEN, fd %d\n", + rfp->fp_fd >> 8); + fd_nr= (rfp->fp_fd >> 8); + fp= rfp->fp_filp[fd_nr]; + vp= fp->filp_vno; + if (!vp) panic(__FILE__, "restart_reopen: no vp", NO_NUM); + if ((vp->v_mode & I_TYPE) != I_CHAR_SPECIAL) continue; + if (((vp->v_sdev >> MAJOR) & BYTE) != maj) continue; + + rfp->fp_flags |= SUSP_REOPEN; + } + + needs_reopen= FALSE; + for (fp = filp; fp < &filp[NR_FILPS]; fp++) { + struct vnode *vp; + int minor; + + if(fp->filp_count < 1 || !(vp = fp->filp_vno)) continue; + if(((vp->v_sdev >> MAJOR) & BYTE) != maj) continue; + if(!(vp->v_mode & (I_BLOCK_SPECIAL|I_CHAR_SPECIAL))) continue; + + fp->filp_state= FS_NEEDS_REOPEN; + needs_reopen= TRUE; + } + + if (needs_reopen) + restart_reopen(maj); + +} + +/*===========================================================================* + * restart_reopen * + *===========================================================================*/ +PRIVATE void restart_reopen(maj) +int maj; +{ + int n, r, minor, fd_nr; + endpoint_t driver_e; + struct vnode *vp; + struct filp *fp; + struct fproc *rfp; + + for (fp = filp; fp < &filp[NR_FILPS]; fp++) { + if (fp->filp_count < 1 || !(vp = fp->filp_vno)) continue; + if (fp->filp_state != FS_NEEDS_REOPEN) continue; + if (((vp->v_sdev >> MAJOR) & BYTE) != maj) continue; + if ((vp->v_mode & I_TYPE) != I_CHAR_SPECIAL) continue; + + if (!(fp->filp_flags & O_REOPEN)) + { + /* File descriptor is to be closed when driver restarts. */ + + /* The function inval_filp will set the fp_filp[]s of + * processes holding that fp to NULL, but _not_ clear + * fp_filp_inuse, so that fd can't be recycled until + * it's close()d. + */ + n = inval_filp(fp); + if (n != fp->filp_count) + { + printf("VFS: warning: invalidate/count " + "discrepancy (%d, %d)\n", n, fp->filp_count); + } + fp->filp_count = 0; + continue; + } + + minor = ((vp->v_sdev >> MINOR) & BYTE); + + r = dev_reopen(vp->v_sdev, fp-filp, vp->v_mode & (R_BIT|W_BIT)); + if (r == OK) + return; + + /* This function will set the fp_filp[]s of processes + * holding that fp to NULL, but _not_ clear + * fp_filp_inuse, so that fd can't be recycled until + * it's close()d. + */ + n = inval_filp(fp); + if (n != fp->filp_count) + { + printf("VFS: warning: invalidate/count " + "discrepancy (%d, %d)\n", n, fp->filp_count); + } + fp->filp_count = 0; + printf("VFS: file on dev %d/%d re-open failed: %d; " + "invalidated %d fd's.\n", maj, minor, r, n); + } + + /* Nothing more to re-open. Restart suspended processes */ + driver_e= dmap[maj].dmap_driver; + + for (rfp = &fproc[0]; rfp < &fproc[NR_PROCS]; rfp++) { + if(rfp->fp_pid == PID_FREE) + continue; + if(rfp->fp_suspended == SUSPENDED && + rfp->fp_task == -driver_e && + (rfp->fp_flags & SUSP_REOPEN)) + { + rfp->fp_flags &= ~SUSP_REOPEN; + rfp->fp_suspended = NOT_SUSPENDED; + reply(rfp->fp_endpoint, ERESTART); + } + } + + /* Look for processes that are suspened in an OPEN call */ + for (rfp = &fproc[0]; rfp < &fproc[NR_PROCS]; rfp++) { + if (rfp->fp_pid == PID_FREE) + continue; + if (rfp->fp_suspended != SUSPENDED || + rfp->fp_task != -XDOPEN || + !(rfp->fp_flags & SUSP_REOPEN)) + { + continue; + } + + printf("restart_reopen: found process in XDOPEN, fd %d\n", + rfp->fp_fd >> 8); + fd_nr= (rfp->fp_fd >> 8); + fp= rfp->fp_filp[fd_nr]; + + if (!fp) + { + /* Open failed, and automatic reopen was not requested */ + rfp->fp_suspended = NOT_SUSPENDED; + FD_CLR(fd_nr, &rfp->fp_filp_inuse); + reply(rfp->fp_endpoint, EIO); + continue; + } + + vp= fp->filp_vno; + if (!vp) panic(__FILE__, "restart_reopen: no vp", NO_NUM); + if ((vp->v_mode & I_TYPE) != I_CHAR_SPECIAL) continue; + if (((vp->v_sdev >> MAJOR) & BYTE) != maj) continue; + + rfp->fp_suspended = NOT_SUSPENDED; + reply(rfp->fp_endpoint, fd_nr); + } +} + +/*===========================================================================* + * reopen_reply * + *===========================================================================*/ +PUBLIC void reopen_reply() +{ + endpoint_t driver_e; + int filp_no, status, maj; + struct filp *fp; + struct vnode *vp; + struct dmap *dp; + + driver_e= m_in.m_source; + filp_no= m_in.REP_ENDPT; + status= m_in.REP_STATUS; + + if (filp_no < 0 || filp_no >= NR_FILPS) + { + printf("reopen_reply: bad filp number %d from driver %d\n", + filp_no, driver_e); + return; + } + fp= &filp[filp_no]; + if (fp->filp_count < 1) + { + printf( + "reopen_reply: filp number %d not inuse (from driver %d)\n", + filp_no, driver_e); + return; + } + + vp= fp->filp_vno; + if (!vp) + { + printf( + "reopen_reply: no vnode for filp number %d (from driver %d)\n", + filp_no, driver_e); + return; + } + + if (fp->filp_state != FS_NEEDS_REOPEN) + { + printf( + "reopen_reply: bad state %d for filp number %d (from driver %d)\n", + fp->filp_state, filp_no, driver_e); + return; + } + + if ((vp->v_mode & I_TYPE) != I_CHAR_SPECIAL) + { + printf( + "reopen_reply: bad mode 0%o for filp number %d (from driver %d)\n", + vp->v_mode, filp_no, driver_e); + return; + } + + maj= ((vp->v_sdev >> MAJOR) & BYTE); + dp = &dmap[maj]; + if (dp->dmap_driver != driver_e) + { + printf("reopen_reply: bad major %d for filp number %d " + "(from driver %d, current driver is %d)\n", + maj, filp_no, driver_e, dp->dmap_driver); + return; + } + + if (status == OK) + { + fp->filp_state= FS_NORMAL; + } + else + { + printf("reopen_reply: should handle error status\n"); + return; + } + restart_reopen(maj); +} + +#define ASYN_NR 100 +PRIVATE asynmsg_t msgtable[ASYN_NR]; +PRIVATE int first_slot= 0, next_slot= 0; + +PUBLIC int asynsend(dst, mp) +endpoint_t dst; +message *mp; +{ + int r, src_ind, dst_ind; + unsigned flags; + + /* Update first_slot */ + for (; first_slot < next_slot; first_slot++) + { + flags= msgtable[first_slot].flags; + if ((flags & (AMF_VALID|AMF_DONE)) == (AMF_VALID|AMF_DONE)) + { + if (msgtable[first_slot].result != OK) + { + printf( + "asynsend: found completed entry %d with error %d\n", + first_slot, + msgtable[first_slot].result); + } + continue; + } + if (flags != AMF_EMPTY) + break; + } + + if (first_slot >= next_slot) + { + /* Reset first_slot and next_slot */ + next_slot= first_slot= 0; + } + + if (next_slot >= ASYN_NR) + { + /* Tell the kernel to stop processing */ + r= senda(NULL, 0); + if (r != OK) + panic(__FILE__, "asynsend: senda failed", r); + + dst_ind= 0; + for (src_ind= first_slot; src_ind= ASYN_NR) + panic(__FILE__, "asynsend: msgtable full", NO_NUM); + } + + msgtable[next_slot].dst= dst; + msgtable[next_slot].msg= *mp; + msgtable[next_slot].flags= AMF_VALID; /* Has to be last. The kernel + * scans this table while we + * are sleeping. + */ + next_slot++; - return; + /* Tell the kernel to rescan the table */ + return senda(msgtable+first_slot, next_slot-first_slot); } -- 2.44.0