minix/minix/servers/vfs/main.c
David van Moolenbroek 129adfeb53 Annotations and tweaks for live update
This change is necessary for instrumentation-aided state transfer.

Change-Id: I24be938009f02e302a15083f9a7a11824975e42b
2015-09-17 17:13:38 +00:00

882 lines
25 KiB
C

/*
* a loop that gets messages requesting work, carries out the work, and sends
* replies.
*
* The entry points into this file are:
* main: main program of the Virtual File System
* reply: send a reply to a process after the requested work is done
*
*/
#include "fs.h"
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <signal.h>
#include <assert.h>
#include <stdlib.h>
#include <sys/ioc_memory.h>
#include <sys/svrctl.h>
#include <sys/select.h>
#include <minix/callnr.h>
#include <minix/com.h>
#include <minix/const.h>
#include <minix/endpoint.h>
#include <minix/safecopies.h>
#include <minix/debug.h>
#include <minix/vfsif.h>
#include "file.h"
#include "vmnt.h"
#include "vnode.h"
#if ENABLE_SYSCALL_STATS
EXTERN unsigned long calls_stats[NR_VFS_CALLS];
#endif
/* Thread related prototypes */
static void do_reply(struct worker_thread *wp);
static void do_work(void);
static void do_init_root(void);
static void handle_work(void (*func)(void));
static void reply(message *m_out, endpoint_t whom, int result);
static int get_work(void);
static void service_pm(void);
static int unblock(struct fproc *rfp);
/* SEF functions and variables. */
static void sef_local_startup(void);
static int sef_cb_init_fresh(int type, sef_init_info_t *info);
/*===========================================================================*
* main *
*===========================================================================*/
int main(void)
{
/* This is the main program of the file system. The main loop consists of
* three major activities: getting new work, processing the work, and sending
* the reply. This loop never terminates as long as the file system runs.
*/
int transid;
struct worker_thread *wp;
/* SEF local startup. */
sef_local_startup();
printf("Started VFS: %d worker thread(s)\n", NR_WTHREADS);
/* This is the main loop that gets work, processes it, and sends replies. */
while (TRUE) {
yield_all(); /* let other threads run */
self = NULL;
send_work();
/* The get_work() function returns TRUE if we have a new message to
* process. It returns FALSE if it spawned other thread activities.
*/
if (!get_work())
continue;
transid = TRNS_GET_ID(m_in.m_type);
if (IS_VFS_FS_TRANSID(transid)) {
wp = worker_get((thread_t) transid - VFS_TRANSID);
if (wp == NULL || wp->w_fp == NULL) {
printf("VFS: spurious message %d from endpoint %d\n",
m_in.m_type, m_in.m_source);
continue;
}
m_in.m_type = TRNS_DEL_ID(m_in.m_type);
do_reply(wp);
continue;
} else if (who_e == PM_PROC_NR) { /* Calls from PM */
/* Special control messages from PM */
service_pm();
continue;
} else if (is_notify(call_nr)) {
/* A task ipc_notify()ed us */
switch (who_e) {
case DS_PROC_NR:
/* Start a thread to handle DS events, if no thread
* is pending or active for it already. DS is not
* supposed to issue calls to VFS or be the subject of
* postponed PM requests, so this should be no problem.
*/
if (worker_can_start(fp))
handle_work(ds_event);
break;
case KERNEL:
mthread_stacktraces();
break;
case CLOCK:
/* Timer expired. Used only for select(). Check it. */
expire_timers(m_in.m_notify.timestamp);
break;
default:
printf("VFS: ignoring notification from %d\n", who_e);
}
continue;
} else if (who_p < 0) { /* i.e., message comes from a task */
/* We're going to ignore this message. Tasks should
* send ipc_notify()s only.
*/
printf("VFS: ignoring message from %d (%d)\n", who_e, call_nr);
continue;
}
if (IS_BDEV_RS(call_nr)) {
/* We've got results for a block device request. */
bdev_reply();
} else if (IS_CDEV_RS(call_nr)) {
/* We've got results for a character device request. */
cdev_reply();
} else {
/* Normal syscall. This spawns a new thread. */
handle_work(do_work);
}
}
return(OK); /* shouldn't come here */
}
/*===========================================================================*
* handle_work *
*===========================================================================*/
static void handle_work(void (*func)(void))
{
/* Handle asynchronous device replies and new system calls. If the originating
* endpoint is an FS endpoint, take extra care not to get in deadlock. */
struct vmnt *vmp = NULL;
endpoint_t proc_e;
int use_spare = FALSE;
proc_e = m_in.m_source;
if (fp->fp_flags & FP_SRV_PROC) {
vmp = find_vmnt(proc_e);
if (vmp != NULL) {
/* A callback from an FS endpoint. Can do only one at once. */
if (vmp->m_flags & VMNT_CALLBACK) {
replycode(proc_e, EAGAIN);
return;
}
/* Already trying to resolve a deadlock? Can't handle more. */
if (worker_available() == 0) {
replycode(proc_e, EAGAIN);
return;
}
/* A thread is available. Set callback flag. */
vmp->m_flags |= VMNT_CALLBACK;
if (vmp->m_flags & VMNT_MOUNTING) {
vmp->m_flags |= VMNT_FORCEROOTBSF;
}
}
/* Use the spare thread to handle this request if needed. */
use_spare = TRUE;
}
worker_start(fp, func, &m_in, use_spare);
}
/*===========================================================================*
* do_reply *
*===========================================================================*/
static void do_reply(struct worker_thread *wp)
{
struct vmnt *vmp = NULL;
if(who_e != VM_PROC_NR && (vmp = find_vmnt(who_e)) == NULL)
panic("Couldn't find vmnt for endpoint %d", who_e);
if (wp->w_task != who_e) {
printf("VFS: tid %d: expected %d to reply, not %d\n",
wp->w_tid, wp->w_task, who_e);
return;
}
/* It should be impossible to trigger the following case, but it is here for
* consistency reasons: worker_stop() resets w_sendrec but not w_task.
*/
if (wp->w_sendrec == NULL) {
printf("VFS: tid %d: late reply from %d ignored\n", wp->w_tid, who_e);
return;
}
*wp->w_sendrec = m_in;
wp->w_sendrec = NULL;
wp->w_task = NONE;
if(vmp) vmp->m_comm.c_cur_reqs--; /* We've got our reply, make room for others */
worker_signal(wp); /* Continue this thread */
}
/*===========================================================================*
* do_pending_pipe *
*===========================================================================*/
static void do_pending_pipe(void)
{
int r, op;
struct filp *f;
tll_access_t locktype;
f = fp->fp_filp[fp->fp_fd];
assert(f != NULL);
locktype = (job_call_nr == VFS_READ) ? VNODE_READ : VNODE_WRITE;
op = (job_call_nr == VFS_READ) ? READING : WRITING;
lock_filp(f, locktype);
r = rw_pipe(op, who_e, f, fp->fp_io_buffer, fp->fp_io_nbytes);
if (r != SUSPEND) { /* Do we have results to report? */
/* Process is writing, but there is no reader. Send a SIGPIPE signal.
* This should match the corresponding code in read_write().
*/
if (r == EPIPE && op == WRITING) {
if (!(f->filp_flags & O_NOSIGPIPE))
sys_kill(fp->fp_endpoint, SIGPIPE);
}
replycode(fp->fp_endpoint, r);
}
unlock_filp(f);
}
/*===========================================================================*
* do_work *
*===========================================================================*/
static void do_work(void)
{
unsigned int call_index;
int error;
if (fp->fp_pid == PID_FREE) {
/* Process vanished before we were able to handle request.
* Replying has no use. Just drop it.
*/
return;
}
memset(&job_m_out, 0, sizeof(job_m_out));
/* At this point we assume that we're dealing with a call that has been
* made specifically to VFS. Typically it will be a POSIX call from a
* normal process, but we also handle a few calls made by drivers such
* such as UDS and VND through here. Call the internal function that
* does the work.
*/
if (IS_VFS_CALL(job_call_nr)) {
call_index = (unsigned int) (job_call_nr - VFS_BASE);
if (call_index < NR_VFS_CALLS && call_vec[call_index] != NULL) {
#if ENABLE_SYSCALL_STATS
calls_stats[call_index]++;
#endif
error = (*call_vec[call_index])();
} else
error = ENOSYS;
} else
error = ENOSYS;
/* Copy the results back to the user and send reply. */
if (error != SUSPEND) reply(&job_m_out, fp->fp_endpoint, error);
}
/*===========================================================================*
* sef_local_startup *
*===========================================================================*/
static void sef_local_startup()
{
/* Register init callbacks. */
sef_setcb_init_fresh(sef_cb_init_fresh);
sef_setcb_init_restart(SEF_CB_INIT_RESTART_STATEFUL);
/* Let SEF perform startup. */
sef_startup();
}
/*===========================================================================*
* sef_cb_init_fresh *
*===========================================================================*/
static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *info)
{
/* Initialize the virtual file server. */
int s, i;
struct fproc *rfp;
message mess;
struct rprocpub rprocpub[NR_BOOT_PROCS];
self = NULL;
verbose = 0;
/* Initialize proc endpoints to NONE */
for (rfp = &fproc[0]; rfp < &fproc[NR_PROCS]; rfp++) {
rfp->fp_endpoint = NONE;
rfp->fp_pid = PID_FREE;
}
/* Initialize the process table with help of the process manager messages.
* Expect one message for each system process with its slot number and pid.
* When no more processes follow, the magic process number NONE is sent.
* Then, stop and synchronize with the PM.
*/
do {
if ((s = sef_receive(PM_PROC_NR, &mess)) != OK)
panic("VFS: couldn't receive from PM: %d", s);
if (mess.m_type != VFS_PM_INIT)
panic("unexpected message from PM: %d", mess.m_type);
if (NONE == mess.VFS_PM_ENDPT) break;
rfp = &fproc[mess.VFS_PM_SLOT];
rfp->fp_flags = FP_NOFLAGS;
rfp->fp_pid = mess.VFS_PM_PID;
rfp->fp_endpoint = mess.VFS_PM_ENDPT;
rfp->fp_grant = GRANT_INVALID;
rfp->fp_blocked_on = FP_BLOCKED_ON_NONE;
rfp->fp_realuid = (uid_t) SYS_UID;
rfp->fp_effuid = (uid_t) SYS_UID;
rfp->fp_realgid = (gid_t) SYS_GID;
rfp->fp_effgid = (gid_t) SYS_GID;
rfp->fp_umask = ~0;
} while (TRUE); /* continue until process NONE */
mess.m_type = OK; /* tell PM that we succeeded */
s = ipc_send(PM_PROC_NR, &mess); /* send synchronization message */
system_hz = sys_hz();
/* Subscribe to block and character driver events. */
s = ds_subscribe("drv\\.[bc]..\\..*", DSF_INITIAL | DSF_OVERWRITE);
if (s != OK) panic("VFS: can't subscribe to driver events (%d)", s);
/* Initialize worker threads */
worker_init();
/* Initialize global locks */
if (mthread_mutex_init(&bsf_lock, NULL) != 0)
panic("VFS: couldn't initialize block special file lock");
init_dmap(); /* Initialize device table. */
/* Map all the services in the boot image. */
if ((s = sys_safecopyfrom(RS_PROC_NR, info->rproctab_gid, 0,
(vir_bytes) rprocpub, sizeof(rprocpub))) != OK){
panic("sys_safecopyfrom failed: %d", s);
}
for (i = 0; i < NR_BOOT_PROCS; i++) {
if (rprocpub[i].in_use) {
if ((s = map_service(&rprocpub[i])) != OK) {
panic("VFS: unable to map service: %d", s);
}
}
}
/* Initialize locks and initial values for all processes. */
for (rfp = &fproc[0]; rfp < &fproc[NR_PROCS]; rfp++) {
if (mutex_init(&rfp->fp_lock, NULL) != 0)
panic("unable to initialize fproc lock");
rfp->fp_worker = NULL;
#if LOCK_DEBUG
rfp->fp_vp_rdlocks = 0;
rfp->fp_vmnt_rdlocks = 0;
#endif
/* Initialize process directories. mount_fs will set them to the
* correct values.
*/
for (i = 0; i < OPEN_MAX; i++)
rfp->fp_filp[i] = NULL;
rfp->fp_rd = NULL;
rfp->fp_wd = NULL;
}
init_vnodes(); /* init vnodes */
init_vmnts(); /* init vmnt structures */
init_select(); /* init select() structures */
init_filps(); /* Init filp structures */
/* Mount PFS and initial file system root. */
worker_start(fproc_addr(VFS_PROC_NR), do_init_root, &mess /*unused*/,
FALSE /*use_spare*/);
return(OK);
}
/*===========================================================================*
* do_init_root *
*===========================================================================*/
static void do_init_root(void)
{
char *mount_type, *mount_label;
int r;
/* Disallow requests from e.g. init(8) while doing the initial mounting. */
worker_allow(FALSE);
/* Mount the pipe file server. */
mount_pfs();
/* Mount the root file system. */
mount_type = "mfs"; /* FIXME: use boot image process name instead */
mount_label = "fs_imgrd"; /* FIXME: obtain this from RS */
r = mount_fs(DEV_IMGRD, "bootramdisk", "/", MFS_PROC_NR, 0, mount_type,
mount_label);
if (r != OK)
panic("Failed to initialize root");
/* All done with mounting, allow requests now. */
worker_allow(TRUE);
}
/*===========================================================================*
* lock_proc *
*===========================================================================*/
void lock_proc(struct fproc *rfp)
{
int r;
struct worker_thread *org_self;
r = mutex_trylock(&rfp->fp_lock);
if (r == 0) return;
org_self = worker_suspend();
if ((r = mutex_lock(&rfp->fp_lock)) != 0)
panic("unable to lock fproc lock: %d", r);
worker_resume(org_self);
}
/*===========================================================================*
* unlock_proc *
*===========================================================================*/
void unlock_proc(struct fproc *rfp)
{
int r;
if ((r = mutex_unlock(&rfp->fp_lock)) != 0)
panic("Failed to unlock: %d", r);
}
/*===========================================================================*
* thread_cleanup *
*===========================================================================*/
void thread_cleanup(void)
{
/* Perform cleanup actions for a worker thread. */
#if LOCK_DEBUG
check_filp_locks_by_me();
check_vnode_locks_by_me(fp);
check_vmnt_locks_by_me(fp);
#endif
if (fp->fp_flags & FP_SRV_PROC) {
struct vmnt *vmp;
if ((vmp = find_vmnt(fp->fp_endpoint)) != NULL) {
vmp->m_flags &= ~VMNT_CALLBACK;
}
}
}
/*===========================================================================*
* get_work *
*===========================================================================*/
static int get_work(void)
{
/* Normally wait for new input. However, if 'reviving' is nonzero, a
* suspended process must be awakened. Return TRUE if there is a message to
* process (usually newly received, but possibly a resumed request), or FALSE
* if a thread for other activities has been spawned instead.
*/
int r, proc_p;
register struct fproc *rp;
if (reviving != 0) {
/* Find a suspended process. */
for (rp = &fproc[0]; rp < &fproc[NR_PROCS]; rp++)
if (rp->fp_pid != PID_FREE && (rp->fp_flags & FP_REVIVED))
return unblock(rp); /* So main loop can process job */
panic("VFS: get_work couldn't revive anyone");
}
for(;;) {
/* Normal case. No one to revive. Get a useful request. */
if ((r = sef_receive(ANY, &m_in)) != OK) {
panic("VFS: sef_receive error: %d", r);
}
proc_p = _ENDPOINT_P(m_in.m_source);
if (proc_p < 0 || proc_p >= NR_PROCS) fp = NULL;
else fp = &fproc[proc_p];
/* Negative who_p is never used to access the fproc array. Negative
* numbers (kernel tasks) are treated in a special way.
*/
if (fp && fp->fp_endpoint == NONE) {
printf("VFS: ignoring request from %d: NONE endpoint %d (%d)\n",
m_in.m_source, who_p, m_in.m_type);
continue;
}
/* Internal consistency check; our mental image of process numbers and
* endpoints must match with how the rest of the system thinks of them.
*/
if (fp && fp->fp_endpoint != who_e) {
if (fproc[who_p].fp_endpoint == NONE)
printf("slot unknown even\n");
panic("VFS: receive endpoint inconsistent (source %d, who_p "
"%d, stored ep %d, who_e %d).\n", m_in.m_source, who_p,
fproc[who_p].fp_endpoint, who_e);
}
return TRUE;
}
/* NOTREACHED */
}
/*===========================================================================*
* reply *
*===========================================================================*/
static void reply(message *m_out, endpoint_t whom, int result)
{
/* Send a reply to a user process. If the send fails, just ignore it. */
int r;
m_out->m_type = result;
r = ipc_sendnb(whom, m_out);
if (r != OK) {
printf("VFS: %d couldn't send reply %d to %d: %d\n", mthread_self(),
result, whom, r);
util_stacktrace();
}
}
/*===========================================================================*
* replycode *
*===========================================================================*/
void replycode(endpoint_t whom, int result)
{
/* Send a reply to a user process. If the send fails, just ignore it. */
message m_out;
memset(&m_out, 0, sizeof(m_out));
reply(&m_out, whom, result);
}
/*===========================================================================*
* service_pm_postponed *
*===========================================================================*/
void service_pm_postponed(void)
{
int r, term_signal;
vir_bytes core_path;
vir_bytes exec_path, stack_frame, pc, newsp, ps_str;
size_t exec_path_len, stack_frame_len;
endpoint_t proc_e;
message m_out;
memset(&m_out, 0, sizeof(m_out));
switch(job_call_nr) {
case VFS_PM_EXEC:
proc_e = job_m_in.VFS_PM_ENDPT;
exec_path = (vir_bytes) job_m_in.VFS_PM_PATH;
exec_path_len = (size_t) job_m_in.VFS_PM_PATH_LEN;
stack_frame = (vir_bytes) job_m_in.VFS_PM_FRAME;
stack_frame_len = (size_t) job_m_in.VFS_PM_FRAME_LEN;
ps_str = (vir_bytes) job_m_in.VFS_PM_PS_STR;
assert(proc_e == fp->fp_endpoint);
r = pm_exec(exec_path, exec_path_len, stack_frame, stack_frame_len,
&pc, &newsp, &ps_str);
/* Reply status to PM */
m_out.m_type = VFS_PM_EXEC_REPLY;
m_out.VFS_PM_ENDPT = proc_e;
m_out.VFS_PM_PC = (void *) pc;
m_out.VFS_PM_STATUS = r;
m_out.VFS_PM_NEWSP = (void *) newsp;
m_out.VFS_PM_NEWPS_STR = ps_str;
break;
case VFS_PM_EXIT:
proc_e = job_m_in.VFS_PM_ENDPT;
assert(proc_e == fp->fp_endpoint);
pm_exit();
/* Reply dummy status to PM for synchronization */
m_out.m_type = VFS_PM_EXIT_REPLY;
m_out.VFS_PM_ENDPT = proc_e;
break;
case VFS_PM_DUMPCORE:
proc_e = job_m_in.VFS_PM_ENDPT;
term_signal = job_m_in.VFS_PM_TERM_SIG;
core_path = (vir_bytes) job_m_in.VFS_PM_PATH;
/* A zero signal used to indicate that a coredump should be generated
* without terminating the target process, but this was broken in so
* many ways that we no longer support this. Userland should implement
* this functionality itself, for example through ptrace(2).
*/
if (term_signal == 0)
panic("no termination signal given for coredump!");
assert(proc_e == fp->fp_endpoint);
r = pm_dumpcore(term_signal, core_path);
/* Reply status to PM */
m_out.m_type = VFS_PM_CORE_REPLY;
m_out.VFS_PM_ENDPT = proc_e;
m_out.VFS_PM_STATUS = r;
break;
case VFS_PM_UNPAUSE:
proc_e = job_m_in.VFS_PM_ENDPT;
assert(proc_e == fp->fp_endpoint);
unpause();
m_out.m_type = VFS_PM_UNPAUSE_REPLY;
m_out.VFS_PM_ENDPT = proc_e;
break;
default:
panic("Unhandled postponed PM call %d", job_m_in.m_type);
}
r = ipc_send(PM_PROC_NR, &m_out);
if (r != OK)
panic("service_pm_postponed: ipc_send failed: %d", r);
}
/*===========================================================================*
* service_pm *
*===========================================================================*/
static void service_pm(void)
{
/* Process a request from PM. This function is called from the main thread, and
* may therefore not block. Any requests that may require blocking the calling
* thread must be executed in a separate thread. Aside from VFS_PM_REBOOT, all
* requests from PM involve another, target process: for example, PM tells VFS
* that a process is performing a setuid() call. For some requests however,
* that other process may not be idle, and in that case VFS must serialize the
* PM request handling with any operation is it handling for that target
* process. As it happens, the requests that may require blocking are also the
* ones where the target process may not be idle. For both these reasons, such
* requests are run in worker threads associated to the target process.
*/
struct fproc *rfp;
int r, slot;
message m_out;
memset(&m_out, 0, sizeof(m_out));
switch (call_nr) {
case VFS_PM_SETUID:
{
endpoint_t proc_e;
uid_t euid, ruid;
proc_e = m_in.VFS_PM_ENDPT;
euid = m_in.VFS_PM_EID;
ruid = m_in.VFS_PM_RID;
pm_setuid(proc_e, euid, ruid);
m_out.m_type = VFS_PM_SETUID_REPLY;
m_out.VFS_PM_ENDPT = proc_e;
}
break;
case VFS_PM_SETGID:
{
endpoint_t proc_e;
gid_t egid, rgid;
proc_e = m_in.VFS_PM_ENDPT;
egid = m_in.VFS_PM_EID;
rgid = m_in.VFS_PM_RID;
pm_setgid(proc_e, egid, rgid);
m_out.m_type = VFS_PM_SETGID_REPLY;
m_out.VFS_PM_ENDPT = proc_e;
}
break;
case VFS_PM_SETSID:
{
endpoint_t proc_e;
proc_e = m_in.VFS_PM_ENDPT;
pm_setsid(proc_e);
m_out.m_type = VFS_PM_SETSID_REPLY;
m_out.VFS_PM_ENDPT = proc_e;
}
break;
case VFS_PM_EXEC:
case VFS_PM_EXIT:
case VFS_PM_DUMPCORE:
case VFS_PM_UNPAUSE:
{
endpoint_t proc_e = m_in.VFS_PM_ENDPT;
if(isokendpt(proc_e, &slot) != OK) {
printf("VFS: proc ep %d not ok\n", proc_e);
return;
}
rfp = &fproc[slot];
/* PM requests on behalf of a proc are handled after the
* system call that might be in progress for that proc has
* finished. If the proc is not busy, we start a new thread.
*/
worker_start(rfp, NULL, &m_in, FALSE /*use_spare*/);
return;
}
case VFS_PM_FORK:
case VFS_PM_SRV_FORK:
{
endpoint_t pproc_e, proc_e;
pid_t child_pid;
uid_t reuid;
gid_t regid;
pproc_e = m_in.VFS_PM_PENDPT;
proc_e = m_in.VFS_PM_ENDPT;
child_pid = m_in.VFS_PM_CPID;
reuid = m_in.VFS_PM_REUID;
regid = m_in.VFS_PM_REGID;
pm_fork(pproc_e, proc_e, child_pid);
m_out.m_type = VFS_PM_FORK_REPLY;
if (call_nr == VFS_PM_SRV_FORK) {
m_out.m_type = VFS_PM_SRV_FORK_REPLY;
pm_setuid(proc_e, reuid, reuid);
pm_setgid(proc_e, regid, regid);
}
m_out.VFS_PM_ENDPT = proc_e;
}
break;
case VFS_PM_SETGROUPS:
{
endpoint_t proc_e;
int group_no;
gid_t *group_addr;
proc_e = m_in.VFS_PM_ENDPT;
group_no = m_in.VFS_PM_GROUP_NO;
group_addr = (gid_t *) m_in.VFS_PM_GROUP_ADDR;
pm_setgroups(proc_e, group_no, group_addr);
m_out.m_type = VFS_PM_SETGROUPS_REPLY;
m_out.VFS_PM_ENDPT = proc_e;
}
break;
case VFS_PM_REBOOT:
/* Reboot requests are not considered postponed PM work and are instead
* handled from a separate worker thread that is associated with PM's
* process. PM makes no regular VFS calls, and thus, from VFS's
* perspective, PM is always idle. Therefore, we can safely do this.
* We do assume that PM sends us only one VFS_PM_REBOOT message at
* once, or ever for that matter. :)
*/
worker_start(fproc_addr(PM_PROC_NR), pm_reboot, &m_in,
FALSE /*use_spare*/);
return;
default:
printf("VFS: don't know how to handle PM request %d\n", call_nr);
return;
}
r = ipc_send(PM_PROC_NR, &m_out);
if (r != OK)
panic("service_pm: ipc_send failed: %d", r);
}
/*===========================================================================*
* unblock *
*===========================================================================*/
static int unblock(rfp)
struct fproc *rfp;
{
/* Unblock a process that was previously blocked on a pipe or a lock. This is
* done by reconstructing the original request and continuing/repeating it.
* This function returns TRUE when it has restored a request for execution, and
* FALSE if the caller should continue looking for work to do.
*/
int blocked_on;
blocked_on = rfp->fp_blocked_on;
/* Reconstruct the original request from the saved data. */
memset(&m_in, 0, sizeof(m_in));
m_in.m_source = rfp->fp_endpoint;
m_in.m_type = rfp->fp_block_callnr;
switch (m_in.m_type) {
case VFS_READ:
case VFS_WRITE:
assert(blocked_on == FP_BLOCKED_ON_PIPE);
m_in.m_lc_vfs_readwrite.fd = rfp->fp_fd;
m_in.m_lc_vfs_readwrite.buf = rfp->fp_io_buffer;
m_in.m_lc_vfs_readwrite.len = rfp->fp_io_nbytes;
break;
case VFS_FCNTL:
assert(blocked_on == FP_BLOCKED_ON_LOCK);
m_in.m_lc_vfs_fcntl.fd = rfp->fp_fd;
m_in.m_lc_vfs_fcntl.cmd = rfp->fp_io_nbytes;
m_in.m_lc_vfs_fcntl.arg_ptr = rfp->fp_io_buffer;
assert(m_in.m_lc_vfs_fcntl.cmd == F_SETLKW);
break;
default:
panic("unblocking call %d blocked on %d ??", m_in.m_type, blocked_on);
}
rfp->fp_blocked_on = FP_BLOCKED_ON_NONE; /* no longer blocked */
rfp->fp_flags &= ~FP_REVIVED;
reviving--;
assert(reviving >= 0);
/* This should not be device I/O. If it is, it'll 'leak' grants. */
assert(!GRANT_VALID(rfp->fp_grant));
/* Pending pipe reads/writes cannot be repeated as is, and thus require a
* special resumption procedure.
*/
if (blocked_on == FP_BLOCKED_ON_PIPE) {
worker_start(rfp, do_pending_pipe, &m_in, FALSE /*use_spare*/);
return(FALSE); /* Retrieve more work */
}
/* A lock request. Repeat the original request as though it just came in. */
fp = rfp;
return(TRUE); /* We've unblocked a process */
}