struct task_s * eval (struct expression_s *expr, struct continuation_s *cont) { switch ( expr->t ) { case EXPRESSION_FUNCTION: return invoke (cont, expr->d.expression_function_v); case EXPRESSION_APPLICATION: { struct continuation_s *ncont = new_continuation (); struct task_s *task = new_task (); ncont->t = CONTINUATION_APP1; init_ptr (&ncont->d.continuation_app1_v.rand, expr->d.expression_application_v.rand); init_ptr (&ncont->d.continuation_app1_v.cont, cont); task->t = TASK_EVAL; init_ptr (&task->d.task_eval_v.expr, expr->d.expression_application_v.rator); init_ptr (&task->d.task_eval_v.cont, ncont); #if 0 /* Harmless but not necessary */ free_continuation (ncont); #endif return task; } } fprintf (stderr, "INTERNAL ERROR: eval() surprised!\n"); return NULL; }
struct task_s * run (struct task_s *task) { switch ( task->t ) { case TASK_EVAL: return eval (task->d.task_eval_v.expr, task->d.task_eval_v.cont); case TASK_APP1: { if ( task->d.task_app1_v.erator->t == FUNCTION_D ) { struct function_s *val = new_function (); struct task_s *ntask; val->t = FUNCTION_D1; init_ptr (&val->d.function_d1_v, task->d.task_app1_v.rand); ntask = invoke (task->d.task_app1_v.cont, val); free_function (val); return ntask; } else { struct continuation_s *ncont = new_continuation (); struct task_s *ntask; ncont->t = CONTINUATION_APP; init_ptr (&ncont->d.continuation_app_v.erator, task->d.task_app1_v.erator); init_ptr (&ncont->d.continuation_app_v.cont, task->d.task_app1_v.cont); ntask = eval (task->d.task_app1_v.rand, ncont); free_continuation (ncont); return ntask; } } case TASK_APP: return apply (task->d.task_app_v.erator, task->d.task_app_v.erand, task->d.task_app_v.cont); case TASK_INVOKE: return invoke (task->d.task_invoke_v.cont, task->d.task_invoke_v.val); case TASK_FINAL: /* Should not happen */; } fprintf (stderr, "INTERNAL ERROR: run() surprised!\n"); return NULL; }
/* * Retry a mount */ static void amfs_retry(int rc, int term, opaque_t arg) { struct continuation *cp = (struct continuation *) arg; am_node *mp = cp->mp; int error = 0; dlog("Commencing retry for mount of %s", mp->am_path); new_ttl(mp); if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime(NULL)) { /* * The entire mount has timed out. Set the error code and skip past all * the mntfs's so that amfs_bgmount will not have any more * ways to try the mount, thus causing an error. */ plog(XLOG_INFO, "mount of \"%s\" has timed out", mp->am_path); error = ETIMEDOUT; while (*cp->al) cp->al++; /* explicitly forbid further retries after timeout */ cp->retry = FALSE; } if (error || !IN_PROGRESS(cp)) error = amfs_bgmount(cp); else /* Normally it's amfs_bgmount() which frees the continuation. However, if * the mount is already in progress and we're in amfs_retry() for another * node we don't try mounting the filesystem once again. Still, we have * to free the continuation as we won't get called again and thus would * leak the continuation structure and our am_loc references. */ free_continuation(cp); reschedule_timeout_mp(); }
/* * Pick a file system to try mounting and * do that in the background if necessary * For each location: discard previous mount location if required fetch next mount location if the filesystem failed to be mounted then this_error = error from filesystem goto failed if the filesystem is mounting or unmounting then goto retry; if the fileserver is down then this_error = EIO continue; if the filesystem is already mounted break fi this_error = initialize mount point if no error on this mount and mount is delayed then this_error = -1 fi if this_error < 0 then retry = true fi if no error on this mount then if mount in background then run mount in background return -1 else this_error = mount in foreground fi fi if an error occurred on this mount then update stats save error in mount point fi endfor */ static int amfs_bgmount(struct continuation *cp) { am_node *mp = cp->mp; am_loc *loc; mntfs *mf; int this_error = -1; /* Per-mount error */ int hard_error = -1; /* Cumulative per-node error */ if (mp->am_al) free_loc(mp->am_al); /* * Try to mount each location. * At the end: * hard_error == 0 indicates something was mounted. * hard_error > 0 indicates everything failed with a hard error * hard_error < 0 indicates nothing could be mounted now */ for (mp->am_al = *cp->al; *cp->al; cp->al++, mp->am_al = *cp->al) { am_ops *p; loc = dup_loc(mp->am_al); mf = loc->al_mnt; p = mf->mf_ops; if (hard_error < 0) hard_error = this_error; this_error = 0; if (mf->mf_error > 0) { this_error = mf->mf_error; goto failed; } if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) { /* * Still mounting - retry later */ dlog("mount of \"%s\" already pending", mf->mf_info); goto retry; } if (FSRV_ISDOWN(mf->mf_server)) { /* * Would just mount from the same place * as a hung mount - so give up */ dlog("%s is already hung - giving up", mf->mf_server->fs_host); this_error = EIO; goto failed; } XFREE(mp->am_link); mp->am_link = NULL; if (loc->al_fo && loc->al_fo->opt_sublink && loc->al_fo->opt_sublink[0]) mp->am_link = xstrdup(loc->al_fo->opt_sublink); /* * Will usually need to play around with the mount nodes * file attribute structure. This must be done here. * Try and get things initialized, even if the fileserver * is not known to be up. In the common case this will * progress things faster. */ /* * Fill in attribute fields. */ if (mf->mf_fsflags & FS_DIRECTORY) mk_fattr(&mp->am_fattr, NFDIR); else mk_fattr(&mp->am_fattr, NFLNK); if (mf->mf_flags & MFF_MOUNTED) { dlog("duplicate mount of \"%s\" ...", mf->mf_info); /* * Skip initial processing of the mountpoint if already mounted. * This could happen if we have multiple sublinks into the same f/s, * or if we are restarting an already-mounted filesystem. */ goto already_mounted; } if (mf->mf_fo && mf->mf_fo->fs_mtab) { plog(XLOG_MAP, "Trying mount of %s on %s fstype %s mount_type %s", mf->mf_fo->fs_mtab, mf->mf_mount, p->fs_type, mp->am_flags & AMF_AUTOFS ? "autofs" : "non-autofs"); } if (p->fs_init && !(mf->mf_flags & MFF_RESTART)) this_error = p->fs_init(mf); if (this_error > 0) goto failed; if (this_error < 0) goto retry; if (loc->al_fo && loc->al_fo->opt_delay) { /* * If there is a delay timer on the location * then don't try to mount if the timer * has not expired. */ int i = atoi(loc->al_fo->opt_delay); time_t now = clocktime(NULL); if (i > 0 && now < (cp->start + i)) { dlog("Mount of %s delayed by %lds", mf->mf_mount, (long) (i - now + cp->start)); goto retry; } } /* * If the directory is not yet made and it needs to be made, then make it! */ if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_fsflags & FS_MKMNT) { plog(XLOG_INFO, "creating mountpoint directory '%s'", mf->mf_mount); this_error = mkdirs(mf->mf_mount, 0555); if (this_error) { plog(XLOG_ERROR, "mkdirs failed: %s", strerror(this_error)); goto failed; } mf->mf_flags |= MFF_MKMNT; } #ifdef HAVE_FS_AUTOFS if (mf->mf_flags & MFF_IS_AUTOFS) if ((this_error = autofs_get_fh(mp))) goto failed; #endif /* HAVE_FS_AUTOFS */ already_mounted: mf->mf_flags |= MFF_MOUNTING; if (mf->mf_fsflags & FS_MBACKGROUND) { dlog("backgrounding mount of \"%s\"", mf->mf_mount); if (cp->callout) { untimeout(cp->callout); cp->callout = 0; } /* actually run the task, backgrounding as necessary */ run_task(mount_node, (opaque_t) mp, amfs_cont, (opaque_t) cp); return -1; } else { dlog("foreground mount of \"%s\" ...", mf->mf_mount); this_error = mount_node((opaque_t) mp); } mf->mf_flags &= ~MFF_MOUNTING; if (this_error > 0) goto failed; if (this_error == 0) { am_mounted(mp); break; /* Success */ } retry: if (!cp->retry) continue; dlog("will retry ...\n"); /* * Arrange that amfs_bgmount is called * after anything else happens. */ dlog("Arranging to retry mount of %s", mp->am_path); sched_task(amfs_retry, (opaque_t) cp, get_mntfs_wchan(mf)); if (cp->callout) untimeout(cp->callout); cp->callout = timeout(RETRY_INTERVAL, wakeup, (opaque_t) get_mntfs_wchan(mf)); mp->am_ttl = clocktime(NULL) + RETRY_INTERVAL; /* * Not done yet - so don't return anything */ return -1; failed: if (!FSRV_ISDOWN(mf->mf_server)) { /* mark the mount as failed unless the server is down */ amd_stats.d_merr++; mf->mf_error = this_error; mf->mf_flags |= MFF_ERROR; #ifdef HAVE_FS_AUTOFS if (mp->am_autofs_fh) autofs_release_fh(mp); #endif /* HAVE_FS_AUTOFS */ if (mf->mf_flags & MFF_MKMNT) { rmdirs(mf->mf_mount); mf->mf_flags &= ~MFF_MKMNT; } } /* * Wakeup anything waiting for this mount */ wakeup(get_mntfs_wchan(mf)); free_loc(loc); /* continue */ } /* * If we get here, then either the mount succeeded or * there is no more mount information available. */ if (this_error) { if (mp->am_al) free_loc(mp->am_al); mp->am_al = loc = new_loc(); mf = loc->al_mnt; #ifdef HAVE_FS_AUTOFS if (mp->am_flags & AMF_AUTOFS) autofs_mount_failed(mp); else #endif /* HAVE_FS_AUTOFS */ nfs_quick_reply(mp, this_error); if (hard_error <= 0) hard_error = this_error; if (hard_error < 0) hard_error = ETIMEDOUT; /* * Set a small(ish) timeout on an error node if * the error was not a time out. */ switch (hard_error) { case ETIMEDOUT: case EWOULDBLOCK: case EIO: mp->am_timeo = 17; break; default: mp->am_timeo = 5; break; } new_ttl(mp); } else { mf = loc->al_mnt; /* * Wakeup anything waiting for this mount */ wakeup(get_mntfs_wchan(mf)); hard_error = 0; } /* * Make sure that the error value in the mntfs has a * reasonable value. */ if (mf->mf_error < 0) { mf->mf_error = hard_error; if (hard_error) mf->mf_flags |= MFF_ERROR; } /* * In any case we don't need the continuation any more */ free_continuation(cp); return hard_error; }
/* * The continuation function. This is called by * the task notifier when a background mount attempt * completes. */ static void amfs_cont(int rc, int term, opaque_t arg) { struct continuation *cp = (struct continuation *) arg; am_node *mp = cp->mp; mntfs *mf = mp->am_al->al_mnt; dlog("amfs_cont: '%s'", mp->am_path); /* * Definitely not trying to mount at the moment */ mf->mf_flags &= ~MFF_MOUNTING; /* * While we are mounting - try to avoid race conditions */ new_ttl(mp); /* * Wakeup anything waiting for this mount */ wakeup(get_mntfs_wchan(mf)); /* * Check for termination signal or exit status... */ if (rc || term) { #ifdef HAVE_FS_AUTOFS if (mf->mf_flags & MFF_IS_AUTOFS && !(mf->mf_flags & MFF_MOUNTED)) autofs_release_fh(mp); #endif /* HAVE_FS_AUTOFS */ if (term) { /* * Not sure what to do for an error code. */ mf->mf_error = EIO; /* XXX ? */ mf->mf_flags |= MFF_ERROR; plog(XLOG_ERROR, "mount for %s got signal %d", mp->am_path, term); } else { /* * Check for exit status... */ #ifdef __linux__ /* * HACK ALERT! * * On Linux (and maybe not only) it's possible to run * an amd which "knows" how to mount certain combinations * of nfs_proto/nfs_version which the kernel doesn't grok. * So if we got an EINVAL and we have a server that's not * using NFSv2/UDP, try again with NFSv2/UDP. * * Too bad that there is no way to dynamically determine * what combinations the _client_ supports, as opposed to * what the _server_ supports... */ if (rc == EINVAL && mf->mf_server && (mf->mf_server->fs_version != 2 || !STREQ(mf->mf_server->fs_proto, "udp"))) mf->mf_flags |= MFF_NFS_SCALEDOWN; else #endif /* __linux__ */ { mf->mf_error = rc; mf->mf_flags |= MFF_ERROR; errno = rc; /* XXX */ if (!STREQ(mp->am_al->al_mnt->mf_ops->fs_type, "linkx")) plog(XLOG_ERROR, "%s: mount (amfs_cont): %m", mp->am_path); } } if (!(mf->mf_flags & MFF_NFS_SCALEDOWN)) { /* * If we get here then that attempt didn't work, so * move the info vector pointer along by one and * call the background mount routine again */ amd_stats.d_merr++; cp->al++; } amfs_bgmount(cp); if (mp->am_error > 0) assign_error_mntfs(mp); } else { /* * The mount worked. */ dlog("Mounting %s returned success", cp->mp->am_path); am_mounted(cp->mp); free_continuation(cp); } reschedule_timeout_mp(); }
struct task_s * apply (struct function_s *rator, struct function_s *rand, struct continuation_s *cont) { switch ( rator->t ) { case FUNCTION_I: return invoke (cont, rand); case FUNCTION_DOT: putchar (rator->d.function_dot_v); return invoke (cont, rand); case FUNCTION_K1: return invoke (cont, rator->d.function_k1_v); case FUNCTION_K: { struct function_s *val = new_function (); struct task_s *task; val->t = FUNCTION_K1; init_ptr (&val->d.function_k1_v, rand); task = invoke (cont, val); free_function (val); return task; } case FUNCTION_S2: { struct expression_s *e_x = new_expression (); struct expression_s *e_y = new_expression (); struct expression_s *e_z = new_expression (); struct expression_s *e1 = new_expression (); struct expression_s *e2 = new_expression (); struct expression_s *e = new_expression (); struct task_s *task = new_task (); e_x->t = EXPRESSION_FUNCTION; init_ptr (&e_x->d.expression_function_v, rator->d.function_s2_v.x); e_y->t = EXPRESSION_FUNCTION; init_ptr (&e_y->d.expression_function_v, rator->d.function_s2_v.y); e_z->t = EXPRESSION_FUNCTION; init_ptr (&e_z->d.expression_function_v, rand); e1->t = EXPRESSION_APPLICATION; init_ptr (&e1->d.expression_application_v.rator, e_x); init_ptr (&e1->d.expression_application_v.rand, e_z); e2->t = EXPRESSION_APPLICATION; init_ptr (&e2->d.expression_application_v.rator, e_y); init_ptr (&e2->d.expression_application_v.rand, e_z); e->t = EXPRESSION_APPLICATION; init_ptr (&e->d.expression_application_v.rator, e1); init_ptr (&e->d.expression_application_v.rand, e2); task->t = TASK_EVAL; init_ptr (&task->d.task_eval_v.expr, e); init_ptr (&task->d.task_eval_v.cont, cont); #if 0 /* Harmless but not necessary */ free_expression (e_x); free_expression (e_y); free_expression (e_z); free_expression (e1); free_expression (e2); free_expression (e); #endif return task; } case FUNCTION_S1: { struct function_s *val = new_function (); struct task_s *task; val->t = FUNCTION_S2; init_ptr (&val->d.function_s2_v.x, rator->d.function_s1_v); init_ptr (&val->d.function_s2_v.y, rand); task = invoke (cont, val); free_function (val); return task; } case FUNCTION_S: { struct function_s *val = new_function (); struct task_s *task; val->t = FUNCTION_S1; init_ptr (&val->d.function_s1_v, rand); task = invoke (cont, val); free_function (val); return task; } case FUNCTION_V: return invoke (cont, rator); case FUNCTION_D1: { struct continuation_s *ncont = new_continuation (); struct task_s *task = new_task (); ncont->t = CONTINUATION_DEL; init_ptr (&ncont->d.continuation_del_v.erand, rand); init_ptr (&ncont->d.continuation_del_v.cont, cont); task->t = TASK_EVAL; init_ptr (&task->d.task_eval_v.expr, rator->d.function_d1_v); init_ptr (&task->d.task_eval_v.cont, ncont); #if 0 /* Harmless but not necessary */ free_continuation (ncont); #endif return task; } case FUNCTION_D: { struct expression_s *promise = new_expression (); struct function_s *val = new_function (); struct task_s *task; promise->t = EXPRESSION_FUNCTION; init_ptr (&promise->d.expression_function_v, rand); val->t = FUNCTION_D1; init_ptr (&val->d.function_d1_v, promise); task = invoke (cont, val); free_continuation (cont); free_function (val); return task; } case FUNCTION_CONT: return invoke (rator->d.function_cont_v, rand); case FUNCTION_DCONT: { struct function_s *val = new_function (); struct task_s *task = new_task (); struct continuation_s *ncont = new_continuation (); push_cont (cont); val->t = FUNCTION_CONT; init_ptr (&val->d.function_cont_v, rator->d.function_cont_v); task->t = TASK_APP; ncont->t = CONTINUATION_ABORT; init_ptr (&task->d.task_app_v.erator, val); init_ptr (&task->d.task_app_v.erand, rand); init_ptr (&task->d.task_app_v.cont, ncont); return task; } case FUNCTION_P: { struct function_s *val = new_function (); struct task_s *task = new_task (); struct continuation_s *ncont = new_continuation (); push_cont (cont); val->t = FUNCTION_I; task->t = TASK_APP; ncont->t = CONTINUATION_ABORT; init_ptr (&task->d.task_app_v.erator, rand); init_ptr (&task->d.task_app_v.erand, val); init_ptr (&task->d.task_app_v.cont, ncont); return task; } case FUNCTION_F: { struct function_s *val = new_function (); struct task_s *task = new_task (); struct continuation_s *ncont = new_continuation (); val->t = FUNCTION_DCONT; init_ptr (&val->d.function_cont_v, cont); task->t = TASK_APP; ncont->t = CONTINUATION_ABORT; init_ptr (&task->d.task_app_v.erator, rand); init_ptr (&task->d.task_app_v.erand, val); init_ptr (&task->d.task_app_v.cont, ncont); return task; } case FUNCTION_E: { struct task_s *task = new_task (); task->t = TASK_FINAL; return task; } case FUNCTION_AT: { struct function_s *val = new_function (); struct task_s *task = new_task (); current_ch = getchar (); val->t = (current_ch != EOF ? FUNCTION_I : FUNCTION_V); task->t = TASK_APP; init_ptr (&task->d.task_app_v.erator, rand); init_ptr (&task->d.task_app_v.erand, val); init_ptr (&task->d.task_app_v.cont, cont); #if 0 /* Harmless but not necessary */ free_function (val); #endif return task; } case FUNCTION_QUES: { struct function_s *val = new_function (); struct task_s *task = new_task (); val->t = (current_ch == rator->d.function_ques_v ? FUNCTION_I : FUNCTION_V); task->t = TASK_APP; init_ptr (&task->d.task_app_v.erator, rand); init_ptr (&task->d.task_app_v.erand, val); init_ptr (&task->d.task_app_v.cont, cont); #if 0 /* Harmless but not necessary */ free_function (val); #endif return task; } case FUNCTION_PIPE: { struct function_s *val = new_function (); struct task_s *task = new_task (); if ( current_ch != EOF ) { val->t = FUNCTION_DOT; val->d.function_dot_v = current_ch; } else val->t = FUNCTION_V; task->t = TASK_APP; init_ptr (&task->d.task_app_v.erator, rand); init_ptr (&task->d.task_app_v.erand, val); init_ptr (&task->d.task_app_v.cont, cont); #if 0 /* Harmless but not necessary */ free_function (val); #endif return task; } } fprintf (stderr, "INTERNAL ERROR: apply() surprised!\n"); return NULL; }
void release_continuation (struct continuation_s *cont) { cont->refcnt --; free_continuation (cont); }