Add support for survival of crashed FSs

When an FS crashes, VFS will clean up resources tied to that FS:
 - Pending requests to the FS are canceled (i.e., fail with EIO)
 - Threads waiting for a reply are stopped (i.e., fail with EIO)
 - Open files are marked invalid. Future operations on a file descriptor
   will cause EBADF errors.
 - vmnt entry is cleared, so in-flight system calls that got past the
   file descriptor check but not yet talking to the crashed FS, will
   fail with EIO.
 - The reference counter of the mount point is decreased, effectively
   removing the crashed FS from the file system tree. Descendants of
   this part of the tree are unreachable by means of a path, but can
   still be unmounted by feeding the block special file to unmount(2).

This patch also gets rid of the "not a known driver endpoint" messages
during shutdown.
This commit is contained in:
Thomas Veerman 2012-01-19 14:21:46 +00:00
parent dd59d50944
commit ddbdca6cdb
7 changed files with 81 additions and 19 deletions

View file

@ -49,6 +49,22 @@ PUBLIC void send_work(void)
fs_sendmore(vmp);
}
/*===========================================================================*
* fs_cancel *
*===========================================================================*/
PUBLIC void fs_cancel(struct vmnt *vmp)
{
/* Cancel all pending requests for this vmp */
struct worker_thread *worker;
while ((worker = vmp->m_comm.c_req_queue) != NULL) {
vmp->m_comm.c_req_queue = worker->w_next;
worker->w_next = NULL;
sending--;
worker_stop(worker);
}
}
/*===========================================================================*
* fs_sendmore *
*===========================================================================*/
@ -80,8 +96,10 @@ PUBLIC int fs_sendrec(endpoint_t fs_e, message *reqmp)
struct vmnt *vmp;
int r;
if ((vmp = find_vmnt(fs_e)) == NULL)
panic("Trying to talk to non-existent FS");
if ((vmp = find_vmnt(fs_e)) == NULL) {
printf("Trying to talk to non-existent FS endpoint %d\n", fs_e);
return(EIO);
}
if (fs_e == fp->fp_endpoint) return(EDEADLK);
if (!force_sync) {

View file

@ -990,7 +990,7 @@ PUBLIC void cdev_up(int maj)
fd_nr);
rfilp = rfp->fp_filp[fd_nr];
vp = rfilp->filp_vno;
if (!vp) panic("VFS: restart_reopen: no vp");
if (!vp) panic("VFS: cdev_up: no vp");
if ((vp->v_mode & I_TYPE) != I_CHAR_SPECIAL) continue;
if (major(vp->v_sdev) != maj) continue;
@ -1051,7 +1051,7 @@ int maj;
if (!(rfilp->filp_flags & O_REOPEN)) {
/* File descriptor is to be closed when driver restarts. */
n = invalidate(rfilp);
n = invalidate_filp(rfilp);
if (n != rfilp->filp_count) {
printf("VFS: warning: invalidate/count "
"discrepancy (%d, %d)\n", n, rfilp->filp_count);
@ -1064,7 +1064,7 @@ int maj;
if (r == OK) return;
/* Device could not be reopened. Invalidate all filps on that device.*/
n = invalidate(rfilp);
n = invalidate_filp(rfilp);
if (n != rfilp->filp_count) {
printf("VFS: warning: invalidate/count "
"discrepancy (%d, %d)\n", n, rfilp->filp_count);

View file

@ -201,18 +201,18 @@ PUBLIC struct filp *find_filp(struct vnode *vp, mode_t bits)
}
/*===========================================================================*
* invalidate *
* invalidate_filp *
*===========================================================================*/
PUBLIC int invalidate(struct filp *fp)
PUBLIC int invalidate_filp(struct filp *rfilp)
{
/* Invalidate filp. fp_filp_inuse is not cleared, so filp can't be reused
until it is closed first. */
int f, fd, n = 0;
for(f = 0; f < NR_PROCS; f++) {
if(fproc[f].fp_pid == PID_FREE) continue;
for(fd = 0; fd < OPEN_MAX; fd++) {
if(fproc[f].fp_filp[fd] && fproc[f].fp_filp[fd] == fp) {
for (f = 0; f < NR_PROCS; f++) {
if (fproc[f].fp_pid == PID_FREE) continue;
for (fd = 0; fd < OPEN_MAX; fd++) {
if(fproc[f].fp_filp[fd] && fproc[f].fp_filp[fd] == rfilp) {
fproc[f].fp_filp[fd] = NULL;
n++;
}
@ -222,6 +222,21 @@ PUBLIC int invalidate(struct filp *fp)
return(n); /* Report back how often this filp has been invalidated. */
}
/*===========================================================================*
* invalidate_filp_by_endpt *
*===========================================================================*/
PUBLIC void invalidate_filp_by_endpt(endpoint_t proc_e)
{
struct filp *f;
for (f = &filp[0]; f < &filp[NR_FILPS]; f++) {
if (f->filp_count != 0 && f->filp_vno != NULL) {
if (f->filp_vno->v_fs_e == proc_e)
(void) invalidate_filp(f);
}
}
}
/*===========================================================================*
* lock_filp *
*===========================================================================*/

View file

@ -450,6 +450,13 @@ PRIVATE void free_proc(struct fproc *exiter, int flags)
(void) close_fd(exiter, i);
}
/* Release root and working directories. */
if (exiter->fp_rd) { put_vnode(exiter->fp_rd); exiter->fp_rd = NULL; }
if (exiter->fp_wd) { put_vnode(exiter->fp_wd); exiter->fp_wd = NULL; }
/* The rest of these actions is only done when processes actually exit. */
if (!(flags & FP_EXITING)) return;
/* Check if any process is SUSPENDed on this driver.
* If a driver exits, unmap its entries in the dmap table.
* (unmapping has to be done after the first step, because the
@ -457,14 +464,10 @@ PRIVATE void free_proc(struct fproc *exiter, int flags)
*/
unsuspend_by_endpt(exiter->fp_endpoint);
dmap_unmap_by_endpt(exiter->fp_endpoint);
worker_stop_by_endpt(exiter->fp_endpoint);
/* Release root and working directories. */
if (exiter->fp_rd) { put_vnode(exiter->fp_rd); exiter->fp_rd = NULL; }
if (exiter->fp_wd) { put_vnode(exiter->fp_wd); exiter->fp_wd = NULL; }
/* The rest of these actions is only done when processes actually exit. */
if (!(flags & FP_EXITING)) return;
worker_stop_by_endpt(exiter->fp_endpoint); /* Unblock waiting threads */
vmnt_unmap_by_endpt(exiter->fp_endpoint); /* Invalidate open files if this
* was an active FS */
/* Invalidate endpoint number for error and sanity checks. */
exiter->fp_endpoint = NONE;

View file

@ -210,6 +210,11 @@ struct fproc *rfp;
struct vmnt *vmp;
vmp = find_vmnt(start_dir->v_fs_e);
if (vmp == NULL) {
r = EIO;
res_vp = NULL;
break;
}
r = lock_vmnt(vmp, resolve->l_vmnt_lock);
if (r == EDEADLK) {
res_vp = NULL;
@ -390,6 +395,8 @@ struct fproc *rfp;
dir_ino = start_node->v_inode_nr;
vmpres = find_vmnt(fs_e);
if (vmpres == NULL) return(EIO); /* mountpoint vanished? */
/* Is the process' root directory on the same partition?,
* if so, set the chroot directory too. */
if (rfp->fp_rd->v_dev == rfp->fp_wd->v_dev)
@ -500,6 +507,7 @@ struct fproc *rfp;
/* Unlock a previously locked vmnt if locked and lock new vmnt */
if (vmpres) unlock_vmnt(vmpres);
vmpres = find_vmnt(fs_e);
if (vmpres == NULL) return(EIO); /* mount point vanished? */
if ((r = lock_vmnt(vmpres, resolve->l_vmnt_lock)) != OK) {
if (r == EBUSY)
vmpres = NULL; /* Already locked */

View file

@ -21,6 +21,7 @@ struct job;
typedef struct filp * filp_id_t;
/* comm.c */
_PROTOTYPE(void fs_cancel, (struct vmnt *vmp) );
_PROTOTYPE(int fs_sendrec, (endpoint_t fs_e, message *reqm) );
_PROTOTYPE(void fs_sendmore, (struct vmnt *vmp) );
_PROTOTYPE(void send_work, (void) );
@ -92,7 +93,8 @@ _PROTOTYPE( struct filp *get_filp2, (struct fproc *rfp, int fild,
_PROTOTYPE( void lock_filp, (struct filp *filp, tll_access_t locktype) );
_PROTOTYPE( void unlock_filp, (struct filp *filp) );
_PROTOTYPE( void unlock_filps, (struct filp *filp1, struct filp *filp2) );
_PROTOTYPE( int invalidate, (struct filp *) );
_PROTOTYPE( int invalidate_filp, (struct filp *) );
_PROTOTYPE( void invalidate_filp_by_endpt, (endpoint_t proc_e) );
_PROTOTYPE( int do_verify_fd, (void) );
_PROTOTYPE( int set_filp, (filp_id_t sfilp) );
_PROTOTYPE( int do_set_filp, (void) );
@ -334,6 +336,7 @@ _PROTOTYPE( struct vmnt *get_locked_vmnt, (struct fproc *rfp) );
_PROTOTYPE( void init_vmnts, (void) );
_PROTOTYPE( int lock_vmnt, (struct vmnt *vp, tll_access_t locktype) );
_PROTOTYPE( void unlock_vmnt, (struct vmnt *vp) );
_PROTOTYPE( void vmnt_unmap_by_endpt, (endpoint_t proc_e) );
/* vnode.c */
_PROTOTYPE( void check_vnode_locks, (void) );

View file

@ -159,6 +159,21 @@ PUBLIC int lock_vmnt(struct vmnt *vmp, tll_access_t locktype)
return(OK);
}
/*===========================================================================*
* vmnt_unmap_by_endpoint *
*===========================================================================*/
PUBLIC void vmnt_unmap_by_endpt(endpoint_t proc_e)
{
struct vmnt *vmp;
if ((vmp = find_vmnt(proc_e)) != NULL) {
fs_cancel(vmp);
invalidate_filp_by_endpt(proc_e);
put_vnode(vmp->m_mounted_on);
clear_vmnt(vmp);
}
}
/*===========================================================================*
* unlock_vmnt *
*===========================================================================*/