minix/servers/avfs/comm.c
Thomas Veerman ddbdca6cdb 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.
2012-01-19 14:21:46 +00:00

180 lines
5 KiB
C

#include "fs.h"
#include "glo.h"
#include "vmnt.h"
#include "fproc.h"
#include <minix/vfsif.h>
#include <assert.h>
FORWARD _PROTOTYPE( int sendmsg, (struct vmnt *vmp, struct fproc *rfp) );
FORWARD _PROTOTYPE( int queuemsg, (struct vmnt *vmp) );
/*===========================================================================*
* sendmsg *
*===========================================================================*/
PRIVATE int sendmsg(vmp, rfp)
struct vmnt *vmp;
struct fproc *rfp;
{
/* This is the low level function that sends requests to FS processes.
*/
int r, transid;
if (vmp->m_fs_e == rfp->fp_endpoint) return(EDEADLK);
vmp->m_comm.c_cur_reqs++; /* One more request awaiting a reply */
transid = rfp->fp_wtid + VFS_TRANSID;
rfp->fp_sendrec->m_type = TRNS_ADD_ID(rfp->fp_sendrec->m_type, transid);
rfp->fp_task = vmp->m_fs_e;
if ((r = asynsend3(vmp->m_fs_e, rfp->fp_sendrec, AMF_NOREPLY)) != OK) {
printf("VFS: sendmsg: error sending message. "
"FS_e: %d req_nr: %d err: %d\n", vmp->m_fs_e,
rfp->fp_sendrec->m_type, r);
util_stacktrace();
return(r);
}
return(r);
}
/*===========================================================================*
* send_work *
*===========================================================================*/
PUBLIC void send_work(void)
{
/* Try to send out as many requests as possible */
struct vmnt *vmp;
if (sending == 0) return;
for (vmp = &vmnt[0]; vmp < &vmnt[NR_MNTS]; vmp++)
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 *
*===========================================================================*/
PUBLIC void fs_sendmore(struct vmnt *vmp)
{
struct worker_thread *worker;
/* Can we send more requests? */
if (vmp->m_fs_e == NONE) return;
if ((worker = vmp->m_comm.c_req_queue) == NULL) /* No process is queued */
return;
if (vmp->m_comm.c_cur_reqs >= vmp->m_comm.c_max_reqs)/*No room to send more*/
return;
if (vmp->m_flags & VMNT_CALLBACK) /* Hold off for now */
return;
vmp->m_comm.c_req_queue = worker->w_next; /* Remove head */
worker->w_next = NULL;
sending--;
assert(sending >= 0);
sendmsg(vmp, worker->w_job.j_fp);
}
/*===========================================================================*
* fs_sendrec *
*===========================================================================*/
PUBLIC int fs_sendrec(endpoint_t fs_e, message *reqmp)
{
struct vmnt *vmp;
int r;
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) {
fp->fp_sendrec = reqmp; /* Where to store request and reply */
/* Find out whether we can send right away or have to enqueue */
if ( !(vmp->m_flags & VMNT_CALLBACK) &&
vmp->m_comm.c_cur_reqs < vmp->m_comm.c_max_reqs) {
/* There's still room to send more and no proc is queued */
r = sendmsg(vmp, fp);
} else {
r = queuemsg(vmp);
}
self->w_next = NULL; /* End of list */
if (r != OK) return(r);
worker_wait(); /* Yield execution until we've received the reply. */
} else if (force_sync == 1) {
int r;
if (OK != (r = sendrec(fs_e, reqmp))) {
printf("VFS: sendrec failed: %d\n", r);
util_stacktrace();
return(r);
}
} else if (force_sync == 2) {
int r, status;
if (OK != (r = asynsend(fs_e, reqmp)) ||
OK != (r = receive(fs_e, reqmp, &status))) {
printf("VFS: asynrec failed: %d\n", r);
util_stacktrace();
return(r);
}
} else if (force_sync == 3) {
int r, status;
if (OK != (r = send(fs_e, reqmp)) ||
OK != (r = receive(fs_e, reqmp, &status))) {
printf("VFS: sendreceive failed: %d\n", r);
util_stacktrace();
return(r);
}
}
if (force_sync != 0 && reqmp->m_type > 0) {
/* XXX: Keep this as long as we're interested in having support
* for synchronous communication. */
nested_fs_call(reqmp);
return fs_sendrec(fs_e, reqmp);
}
return(reqmp->m_type);
}
/*===========================================================================*
* queuemsg *
*===========================================================================*/
PRIVATE int queuemsg(struct vmnt *vmp)
{
/* Put request on queue for vmnt */
struct worker_thread *queue;
if (vmp->m_comm.c_req_queue == NULL) {
vmp->m_comm.c_req_queue = self;
} else {
/* Walk the list ... */
queue = vmp->m_comm.c_req_queue;
while (queue->w_next != NULL) queue = queue->w_next;
/* ... and append this worker */
queue->w_next = self;
}
self->w_next = NULL; /* End of list */
sending++;
return(OK);
}