X-Git-Url: http://git.barrelfish.org/?p=barrelfish;a=blobdiff_plain;f=usr%2Fproc_mgmt%2Fservice.c;h=41df760aac19744824cc7aedcee2c97535f85726;hp=66fc77969e4360b88ebb88fd52c1d4b25b0e6b95;hb=1c3e8231d91b036121ee3dd81e3efa50edc96434;hpb=11268e99bb0c6fc4e2bddd3ba507519d0db55bec diff --git a/usr/proc_mgmt/service.c b/usr/proc_mgmt/service.c index 66fc779..41df760 100644 --- a/usr/proc_mgmt/service.c +++ b/usr/proc_mgmt/service.c @@ -58,262 +58,559 @@ static void add_spawnd_handler_non_monitor(struct proc_mgmt_binding *b, } static void spawn_reply_handler(struct spawn_binding *b, - struct capref domain_cap, errval_t spawn_err) + struct capref domain_cap, errval_t spawn_err); +static void spawn_with_caps_reply_handler(struct spawn_binding *b, + struct capref domain_cap, + errval_t spawn_err); +static void span_reply_handler(struct spawn_binding *b, + struct capref domain_cap, errval_t span_err); +static void kill_reply_handler(struct spawn_binding *b, + struct capref domain_cap, errval_t kill_err); +static void exit_reply_handler(struct spawn_binding *b, + struct capref domain_cap, errval_t exit_err); +static void cleanup_reply_handler(struct spawn_binding *b, + struct capref domain_cap, + errval_t cleanup_err); + +static void spawn_request_sender(void *arg) { - struct pending_client *cl; - errval_t err = pending_clients_release(domain_cap, &cl); - if (err_is_fail(err)) { - // This might be a kill request issued after a successful spawn/span - // followed by a local error in the process manager (see below). If that - // is the case, then we won't have a client, as it has already been - // released. - DEBUG_ERR(err, "failed to retrieve pending client based on domain cap " - "returned by spawnd"); - return; + struct pending_spawn *spawn = (struct pending_spawn*) arg; + + errval_t err; + bool with_caps = !(capref_is_null(spawn->inheritcn_cap) && + capref_is_null(spawn->argcn_cap)); + if (with_caps) { + spawn->b->rx_vtbl.spawn_with_caps_reply = spawn_with_caps_reply_handler; + err = spawn->b->tx_vtbl.spawn_with_caps_request(spawn->b, NOP_CONT, + cap_procmng, + spawn->domain_cap, + spawn->path, + spawn->argvbuf, + spawn->argvbytes, + spawn->envbuf, + spawn->envbytes, + spawn->inheritcn_cap, + spawn->argcn_cap, + spawn->flags); + } else { + spawn->b->rx_vtbl.spawn_reply = spawn_reply_handler; + err = spawn->b->tx_vtbl.spawn_request(spawn->b, NOP_CONT, cap_procmng, + spawn->domain_cap, spawn->path, + spawn->argvbuf, spawn->argvbytes, + spawn->envbuf, spawn->envbytes, + spawn->flags); } - - errval_t resp_err = SYS_ERR_OK; - struct domain_entry *entry; - switch (cl->type) { - case ClientType_Spawn: - err = spawn_err; - if (err_is_ok(spawn_err)) { - err = domain_spawn(domain_cap, cl->core_id); - } - resp_err = cl->b->tx_vtbl.spawn_response(cl->b, NOP_CONT, err, - domain_cap); - break; - - case ClientType_SpawnWithCaps: - err = spawn_err; - if (err_is_ok(spawn_err)) { - err = domain_spawn(domain_cap, cl->core_id); - } - resp_err = cl->b->tx_vtbl.spawn_with_caps_response(cl->b, NOP_CONT, - err, domain_cap); - break; - - case ClientType_Span: - err = spawn_err; - if (err_is_ok(spawn_err)) { - err = domain_span(domain_cap, cl->core_id); - } - resp_err = cl->b->tx_vtbl.span_response(cl->b, NOP_CONT, err); - break; - - case ClientType_Kill: - if (err_is_fail(spawn_err)) { - // Looks like some spawnd was unable to successfully kill - // its dispatcher for this domain. Not much the process - // manager can do about it; return the error to the client. - resp_err = cl->b->tx_vtbl.kill_response(cl->b, NOP_CONT, - err); - break; + if (err_is_ok(err)) { + free(spawn); + } else { + if (err_no(err) == FLOUNDER_ERR_TX_BUSY) { + err = spawn->b->register_send(spawn->b, spawn->b->waitset, + MKCONT(spawn_request_sender, arg)); + if (err_is_fail(err)) { + DEBUG_ERR(err, "registering for spawn request"); + pending_clients_release(spawn->domain_cap, + with_caps ? ClientType_SpawnWithCaps + : ClientType_Spawn, + NULL); + event_mutex_unlock(&spawn->b->mutex); + free(spawn); } + } else { + DEBUG_ERR(err, "sending spawn request"); + pending_clients_release(spawn->domain_cap, + with_caps ? ClientType_SpawnWithCaps + : ClientType_Spawn, + NULL); + event_mutex_unlock(&spawn->b->mutex); + free(spawn); + } + } +} - err = domain_get_by_cap(domain_cap, &entry); +static void span_request_sender(void *arg) +{ + struct pending_span *span = (struct pending_span*) arg; + + errval_t err; + span->b->rx_vtbl.span_reply = span_reply_handler; + err = span->b->tx_vtbl.span_request(span->b, NOP_CONT, cap_procmng, + span->domain_cap, span->vroot, + span->dispframe); + if (err_is_ok(err)) { + err = domain_span(span->domain_cap, span->core_id); + if (err_is_fail(err)) { + DEBUG_ERR(err, "failed domain_span to core %u\n", span->core_id); + } + free(span); + } else { + if (err_no(err) == FLOUNDER_ERR_TX_BUSY) { + err = span->b->register_send(span->b, span->b->waitset, + MKCONT(span_request_sender, arg)); if (err_is_fail(err)) { - DEBUG_ERR(err, "failed to retrieve domain by domain_cap " - "returned by spawnd after kill"); - break; + DEBUG_ERR(err, "registering for span request"); + pending_clients_release(span->domain_cap, ClientType_Span, + NULL); + event_mutex_unlock(&span->b->mutex); + free(span); } + } else { + DEBUG_ERR(err, "sending span request"); + pending_clients_release(span->domain_cap, ClientType_Span, NULL); + event_mutex_unlock(&span->b->mutex); + free(span); + } + } +} - assert(entry->num_spawnds_resources > 0 || - entry->num_spawnds_running > 0); - assert(entry->status != DOMAIN_STATUS_CLEANED); - - if (entry->num_spawnds_running > 0) { - --entry->num_spawnds_running; - - err = pending_clients_add(domain_cap, cl->b, - ClientType_Kill, MAX_COREID); - if (err_is_fail(err)) { - DEBUG_ERR(err, "pending_clients_add in reply handler"); - } - - if (entry->num_spawnds_running == 0) { - entry->status = DOMAIN_STATUS_STOPPED; - entry->exit_status = EXIT_STATUS_KILLED; - - // TODO(razvan): Might it be more sane if we respond back - // to the client after the domain has been cleaned up (i.e. - // the cspace root has been revoked for all dispatchers)? - resp_err = cl->b->tx_vtbl.kill_response(cl->b, NOP_CONT, - err); - - // TODO(razvan): Same problem applies to the waiters: would - // it be better if we sent them wait_responses after the - // cspace root has been revoked, too? (here and in the exit - // case). - struct domain_waiter *waiter = entry->waiters; - while (waiter != NULL) { - waiter->b->tx_vtbl.wait_response(waiter->b, NOP_CONT, - SYS_ERR_OK, - entry->exit_status); - struct domain_waiter *aux = waiter; - waiter = waiter->next; - free(aux); - } +static void kill_request_sender(void *arg) +{ + struct pending_kill_exit_cleanup *kill = (struct pending_kill_exit_cleanup*) arg; - for (coreid_t i = 0; i < MAX_COREID; ++i) { - if (entry->spawnds[i] == NULL) { - continue; - } - - struct spawn_binding *spb = entry->spawnds[i]->b; - spb->rx_vtbl.spawn_reply = spawn_reply_handler; - errval_t req_err = spb->tx_vtbl.cleanup_request(spb, - NOP_CONT, cap_procmng, domain_cap); - if (err_is_fail(req_err)) { - DEBUG_ERR(req_err, "failed to send cleanup_request " - "to spawnd %u\n", i); - } + errval_t err; + kill->sb->rx_vtbl.kill_reply = kill_reply_handler; + err = kill->sb->tx_vtbl.kill_request(kill->sb, NOP_CONT, cap_procmng, + kill->domain_cap); + if (err_is_ok(err)) { + free(kill); + } else { + if (err_no(err) == FLOUNDER_ERR_TX_BUSY) { + err = kill->sb->register_send(kill->sb, kill->sb->waitset, + MKCONT(kill_request_sender, arg)); + if (err_is_fail(err)) { + DEBUG_ERR(err, "registering for kill request"); + + struct pending_client *cl; + err = pending_clients_release_one(kill->domain_cap, + ClientType_Kill, + kill->pmb, &cl); + if (err_is_ok(err)) { + while (cl != NULL) { + struct pending_client *tmp = cl; + cl = cl->next; + free(tmp); } } - } else { - --entry->num_spawnds_resources; - - if (entry->num_spawnds_resources == 0) { - entry->status = DOMAIN_STATUS_CLEANED; - - // At this point, the domain exists in state CLEANED for - // history reasons. For instance, if some other domain - // issues a wait call for this one, the process manager can - // return the exit status directly. - // At some point, however, we might want to just clean up - // the domain entry and recycle the domain cap. - } else { - // Expecting to receive further cleanup replies from other - // spawnds for the same domain cap, hence re-add the - // pending client. - err = pending_clients_add(domain_cap, cl->b, - ClientType_Exit, MAX_COREID); - if (err_is_fail(err)) { - DEBUG_ERR(err, "pending_clients_add in reply handler"); - } + + event_mutex_unlock(&kill->sb->mutex); + free(kill); + } + } else { + DEBUG_ERR(err, "sending kill request"); + + struct pending_client *cl; + err = pending_clients_release_one(kill->domain_cap, + ClientType_Kill, + kill->pmb, &cl); + if (err_is_ok(err)) { + while (cl != NULL) { + struct pending_client *tmp = cl; + cl = cl->next; + free(tmp); } } - break; - - case ClientType_Exit: - if (err_is_fail(spawn_err)) { - // Looks like some spawnd was unable to successfully kill - // its dispatcher for this domain. Not much the process - // manager can do about it. Furthermore, this was an exit call, - // so there's no client to reply back to. - break; + + event_mutex_unlock(&kill->sb->mutex); + free(kill); + } + } +} + +static void exit_request_sender(void *arg) +{ + struct pending_kill_exit_cleanup *exit = (struct pending_kill_exit_cleanup*) arg; + + errval_t err; + exit->sb->rx_vtbl.exit_reply = exit_reply_handler; + err = exit->sb->tx_vtbl.exit_request(exit->sb, NOP_CONT, cap_procmng, + exit->domain_cap); + if (err_is_ok(err)) { + free(exit); + } else { + if (err_no(err) == FLOUNDER_ERR_TX_BUSY) { + err = exit->sb->register_send(exit->sb, exit->sb->waitset, + MKCONT(exit_request_sender, arg)); + if (err_is_fail(err)) { + DEBUG_ERR(err, "registering for exit request"); + err = pending_clients_release(exit->domain_cap, ClientType_Exit, + NULL); + event_mutex_unlock(&exit->sb->mutex); + free(exit); } + } else { + DEBUG_ERR(err, "sending exit request"); + err = pending_clients_release(exit->domain_cap, ClientType_Exit, + NULL); + event_mutex_unlock(&exit->sb->mutex); + free(exit); + } + } +} - err = domain_get_by_cap(domain_cap, &entry); +static void cleanup_request_sender(void *arg) +{ + struct pending_kill_exit_cleanup *cleanup = (struct pending_kill_exit_cleanup*) arg; + + errval_t err; + cleanup->sb->rx_vtbl.cleanup_reply = cleanup_reply_handler; + err = cleanup->sb->tx_vtbl.cleanup_request(cleanup->sb, NOP_CONT, cap_procmng, + cleanup->domain_cap); + if (err_is_ok(err)) { + free(cleanup); + } else { + if (err_no(err) == FLOUNDER_ERR_TX_BUSY) { + err = cleanup->sb->register_send(cleanup->sb, cleanup->sb->waitset, + MKCONT(cleanup_request_sender, arg)); if (err_is_fail(err)) { - DEBUG_ERR(err, "failed to retrieve domain by domain_cap " - "returned by spawnd after kill"); - break; + DEBUG_ERR(err, "registering for cleanup request"); + pending_clients_release(cleanup->domain_cap, ClientType_Cleanup, + NULL); + event_mutex_unlock(&cleanup->sb->mutex); + free(cleanup); } + } else { + DEBUG_ERR(err, "sending cleanup request"); + pending_clients_release(cleanup->domain_cap, ClientType_Cleanup, + NULL); + event_mutex_unlock(&cleanup->sb->mutex); + free(cleanup); + } + } +} - assert(entry->num_spawnds_resources > 0 || - entry->num_spawnds_running > 0); - assert(entry->status != DOMAIN_STATUS_CLEANED); +static void spawn_reply_handler(struct spawn_binding *b, + struct capref domain_cap, errval_t spawn_err) +{ + event_mutex_unlock(&b->mutex); - if (entry->num_spawnds_running > 0) { - --entry->num_spawnds_running; + struct pending_client *cl; + errval_t err = pending_clients_release(domain_cap, ClientType_Spawn, &cl); + if (err_is_fail(err)) { + DEBUG_ERR(err, "failed to retrieve pending spawn client based on domain" + " cap"); + return; + } - err = pending_clients_add(domain_cap, cl->b, - ClientType_Exit, MAX_COREID); - if (err_is_fail(err)) { - DEBUG_ERR(err, "pending_clients_add in reply handler"); - } + err = spawn_err; + if (err_is_ok(spawn_err)) { + err = domain_spawn(domain_cap, cl->core_id); + } - if (entry->num_spawnds_running == 0) { - entry->status = DOMAIN_STATUS_STOPPED; - - struct domain_waiter *waiter = entry->waiters; - while (waiter != NULL) { - waiter->b->tx_vtbl.wait_response(waiter->b, NOP_CONT, - SYS_ERR_OK, - entry->exit_status); - struct domain_waiter *aux = waiter; - waiter = waiter->next; - free(aux); - } + errval_t resp_err = cl->b->tx_vtbl.spawn_response(cl->b, NOP_CONT, err, + domain_cap); + if (err_is_fail(resp_err)) { + DEBUG_ERR(resp_err, "failed to send spawn_response to client"); + } + + free(cl); +} - for (coreid_t i = 0; i < MAX_COREID; ++i) { - if (entry->spawnds[i] == NULL) { - continue; - } - - struct spawn_binding *spb = entry->spawnds[i]->b; - spb->rx_vtbl.spawn_reply = spawn_reply_handler; - errval_t req_err = spb->tx_vtbl.cleanup_request(spb, - NOP_CONT, cap_procmng, domain_cap); - if (err_is_fail(req_err)) { - DEBUG_ERR(req_err, "failed to send cleanup_request " - "to spawnd %u\n", i); - } - } - } - } else { - --entry->num_spawnds_resources; - - if (entry->num_spawnds_resources == 0) { - entry->status = DOMAIN_STATUS_CLEANED; - - // At this point, the domain exists in state CLEANED for - // history reasons. For instance, if some other domain - // issues a wait call for this one, the process manager can - // return the exit status directly. - // At some point, however, we might want to just clean up - // the domain entry and recycle the domain cap. - } else { - // Expecting to receive further cleanup replies from other - // spawnds for the same domain cap, hence re-add the - // pending client. - err = pending_clients_add(domain_cap, cl->b, - ClientType_Exit, MAX_COREID); - if (err_is_fail(err)) { - DEBUG_ERR(err, "pending_clients_add in reply handler"); - } - } +static void spawn_with_caps_reply_handler(struct spawn_binding *b, + struct capref domain_cap, + errval_t spawn_err) +{ + event_mutex_unlock(&b->mutex); + + struct pending_client *cl; + errval_t err = pending_clients_release(domain_cap, ClientType_SpawnWithCaps, + &cl); + if (err_is_fail(err)) { + DEBUG_ERR(err, "failed to retrieve pending spawn_with_caps client based" + " on domain cap"); + return; + } + + err = spawn_err; + if (err_is_ok(spawn_err)) { + err = domain_spawn(domain_cap, cl->core_id); + } + + errval_t resp_err = cl->b->tx_vtbl.spawn_with_caps_response(cl->b, NOP_CONT, + err, + domain_cap); + if (err_is_fail(resp_err)) { + DEBUG_ERR(resp_err, "failed to send spawn_with_caps_response to " + "client"); + } + + free(cl); +} + +static void span_reply_handler(struct spawn_binding *b, + struct capref domain_cap, errval_t span_err) +{ + event_mutex_unlock(&b->mutex); + + struct pending_client *cl; + errval_t err = pending_clients_release(domain_cap, ClientType_Span, &cl); + if (err_is_fail(err)) { + DEBUG_ERR(err, "failed to retrieve pending span client based on domain" + " cap"); + return; + } + + struct domain_entry *entry; + err = domain_get_by_cap(cl->domain_cap, &entry); + if (err_is_fail(err)) { + DEBUG_ERR(err, "failed to retrieve span client by domain cap"); + return; + } + + if (entry->status != DOMAIN_STATUS_RUNNING) { + // Domain has been stopped while we were serving the request; there's + // no one to respond to. + free(cl); + return; + } + + err = cl->b->tx_vtbl.span_response(cl->b, NOP_CONT, span_err); + if (err_is_fail(err)) { + DEBUG_ERR(err, "failed to send span_response to client"); + } + + free(cl); +} + +static void cleanup_reply_handler(struct spawn_binding *b, + struct capref domain_cap, + errval_t cleanup_err) +{ + event_mutex_unlock(&b->mutex); + + struct pending_client *cl; + errval_t err = pending_clients_release(domain_cap, ClientType_Cleanup, &cl); + if (err_is_fail(err)) { + DEBUG_ERR(err, "failed to retrieve pending cleanup client based on " + "domain cap"); + return; + } + + if (err_is_fail(cleanup_err)) { + // TODO(razvan): Here, spawnd has failed deleting its local cspace. + // Should we send another cleanup message, until it might succeed? + free(cl); + return; + } + + struct domain_entry *entry; + err = domain_get_by_cap(domain_cap, &entry); + if (err_is_fail(err)) { + DEBUG_ERR(err, "failed to retrieve domain by cap returned by spawnd " + "after cleanup"); + return; + } + + assert(entry->num_spawnds_resources > 0); + assert(entry->status != DOMAIN_STATUS_CLEANED); + + --entry->num_spawnds_resources; + + if (entry->num_spawnds_resources == 0) { + entry->status = DOMAIN_STATUS_CLEANED; + + // At this point, the domain exists in state CLEANED for + // history reasons. For instance, if some other domain + // issues a wait call for this one, the process manager can + // return the exit status directly. + // At some point, however, we might want to just clean up + // the domain entry and recycle the domain cap. + } else { + // Expecting to receive further cleanup replies from other + // spawnds for the same domain cap, hence re-add the + // pending client. + err = pending_clients_add(domain_cap, cl->b, + ClientType_Cleanup, MAX_COREID); + if (err_is_fail(err)) { + DEBUG_ERR(err, "pending_clients_add in cleanup_reply_handler"); + } + } +} + +static void kill_reply_handler(struct spawn_binding *b, + struct capref domain_cap, errval_t kill_err) +{ + event_mutex_unlock(&b->mutex); + + struct pending_client *cl; + errval_t err = pending_clients_release(domain_cap, ClientType_Kill, &cl); + if (err_is_fail(err)) { + DEBUG_ERR(err, "failed to retrieve pending kill client based on domain " + "cap"); + return; + } + + errval_t resp_err; + if (err_is_fail(kill_err)) { + // TODO(razvan): Here, spawnd has failed deleting its local dispatcher. + // Should we send another kill message, until it might succeed? + while (cl != NULL) { + resp_err = cl->b->tx_vtbl.kill_response(cl->b, NOP_CONT, + kill_err); + if (err_is_fail(resp_err)) { + DEBUG_ERR(resp_err, "failed to send kill_response to client"); } - break; - - default: - // TODO(razvan): Handle the other cases, e.g. wait. - debug_printf("Unknown client type %u\n", cl->type); - return; - } - - if (err_is_ok(spawn_err) && err_is_fail(err)) { - // Spawnd has successfully completed its end of the operation, but - // there's been an error in the process manager's book-keeping - // of domains. Therefore, if the request was a spawn or span one, spawnd - // needs to be asked to stop the dispatcher which it has just enqueued. - if (cl->type == ClientType_Spawn || - cl->type == ClientType_SpawnWithCaps || - cl->type == ClientType_Span) { - struct spawnd_state *state = spawnd_state_get(cl->core_id); - assert(state != NULL); - struct spawn_binding *spb = state->b; - assert(spb != NULL); - - err = spb->tx_vtbl.kill_request(spb, NOP_CONT, cap_procmng, - domain_cap); - if (err_is_fail(err)) { - // XXX: How severe is this? Maybe we want something more - // assertive than logging an error message. - DEBUG_ERR(err, "failed to send kill request for dangling " - "dispatcher"); - } else { - pending_clients_add(domain_cap, cl->b, ClientType_Kill, - MAX_COREID); + struct pending_client *tmp = cl; + cl = cl->next; + free(tmp); + } + return; + } + + struct domain_entry *entry; + err = domain_get_by_cap(domain_cap, &entry); + if (err_is_fail(err)) { + DEBUG_ERR(err, "failed to retrieve domain by cap returned by spawnd " + "after kill"); + return; + } + + assert(entry->num_spawnds_running > 0); + assert(entry->status != DOMAIN_STATUS_STOPPED); + + --entry->num_spawnds_running; + + if (entry->num_spawnds_running == 0) { + entry->status = DOMAIN_STATUS_STOPPED; + entry->exit_status = EXIT_STATUS_KILLED; + + err = pending_clients_add(domain_cap, NULL, ClientType_Cleanup, + MAX_COREID); + if (err_is_fail(err)) { + DEBUG_ERR(err, "pending_clients_add in kill_reply_handler"); + } + + // TODO(razvan): Might it be more sane if we respond back + // to the client after the domain has been cleaned up (i.e. + // the cspace root has been revoked for all dispatchers)? + while (cl != NULL) { + resp_err = cl->b->tx_vtbl.kill_response(cl->b, NOP_CONT, + kill_err); + if (err_is_fail(resp_err)) { + DEBUG_ERR(resp_err, "failed to send kill_response to client"); } + struct pending_client *tmp = cl; + cl = cl->next; + free(tmp); + } + + // TODO(razvan): Same problem applies to the waiters: would + // it be better if we sent them wait_responses after the + // cspace root has been revoked, too? (here and in the exit + // case). + struct domain_waiter *waiter = entry->waiters; + while (waiter != NULL) { + waiter->b->tx_vtbl.wait_response(waiter->b, NOP_CONT, + SYS_ERR_OK, + entry->exit_status); + struct domain_waiter *tmp = waiter; + waiter = waiter->next; + free(tmp); + } + + for (coreid_t i = 0; i < MAX_COREID; ++i) { + if (entry->spawnds[i] == NULL) { + continue; + } + + struct spawn_binding *spb = entry->spawnds[i]->b; + + struct pending_kill_exit_cleanup *cleanup = (struct pending_kill_exit_cleanup*) malloc( + sizeof(struct pending_kill_exit_cleanup)); + cleanup->sb = spb; + cleanup->domain_cap = domain_cap; + + spb->rx_vtbl.cleanup_reply = cleanup_reply_handler; + event_mutex_enqueue_lock(&spb->mutex, + &cleanup->qn, + (struct event_closure) { + .handler = cleanup_request_sender, + .arg = cleanup }); + } + } else { + err = pending_clients_add(domain_cap, cl->b, ClientType_Kill, + MAX_COREID); + if (err_is_fail(err)) { + DEBUG_ERR(err, "pending_clients_add in kill_reply_handler"); } } +} - free(cl); +static void exit_reply_handler(struct spawn_binding *b, + struct capref domain_cap, errval_t exit_err) +{ + event_mutex_unlock(&b->mutex); - if (err_is_fail(resp_err)) { - DEBUG_ERR(resp_err, "failed to send response to client"); + struct pending_client *cl; + errval_t err = pending_clients_release(domain_cap, ClientType_Exit, &cl); + if (err_is_fail(err)) { + DEBUG_ERR(err, "failed to retrieve pending exit client based on domain " + "cap"); + return; + } + + if (err_is_fail(exit_err)) { + // TODO(razvan): Here, spawnd has failed deleting its local dispatcher. + // Should we send another kill message, until it might succeed? + free(cl); + return; + } + + struct domain_entry *entry; + err = domain_get_by_cap(domain_cap, &entry); + if (err_is_fail(err)) { + DEBUG_ERR(err, "failed to retrieve domain by cap returned by spawnd " + "after exit"); + return; + } + + assert(entry->num_spawnds_running > 0); + assert(entry->status != DOMAIN_STATUS_STOPPED); + + --entry->num_spawnds_running; + + if (entry->num_spawnds_running == 0) { + entry->status = DOMAIN_STATUS_STOPPED; + + free(cl); + + // TODO(razvan): Same problem applies to the waiters: would + // it be better if we sent them wait_responses after the + // cspace root has been revoked, too? (here and in the exit + // case). + struct domain_waiter *waiter = entry->waiters; + while (waiter != NULL) { + waiter->b->tx_vtbl.wait_response(waiter->b, NOP_CONT, + SYS_ERR_OK, + entry->exit_status); + struct domain_waiter *tmp = waiter; + waiter = waiter->next; + free(tmp); + } + + for (coreid_t i = 0; i < MAX_COREID; ++i) { + if (entry->spawnds[i] == NULL) { + continue; + } + + struct spawn_binding *spb = entry->spawnds[i]->b; + + struct pending_kill_exit_cleanup *cleanup = (struct pending_kill_exit_cleanup*) malloc( + sizeof(struct pending_kill_exit_cleanup)); + cleanup->sb = spb; + cleanup->domain_cap = domain_cap; + + spb->rx_vtbl.cleanup_reply = cleanup_reply_handler; + event_mutex_enqueue_lock(&spb->mutex, + &cleanup->qn, + (struct event_closure) { + .handler = cleanup_request_sender, + .arg = cleanup }); + } + } else { + err = pending_clients_add(domain_cap, cl->b, ClientType_Exit, + MAX_COREID); + if (err_is_fail(err)) { + DEBUG_ERR(err, "pending_clients_add in kill_reply_handler"); + } } } @@ -323,11 +620,8 @@ static errval_t spawn_handler_common(struct proc_mgmt_binding *b, const char *argvbuf, size_t argvbytes, const char *envbuf, size_t envbytes, struct capref inheritcn_cap, - struct capref argcn_cap, uint8_t flags, - struct capref *ret_domain_cap) + struct capref argcn_cap, uint8_t flags) { - assert(ret_domain_cap != NULL); - if (!spawnd_state_exists(core_id)) { return PROC_MGMT_ERR_INVALID_SPAWND; } @@ -355,23 +649,24 @@ static errval_t spawn_handler_common(struct proc_mgmt_binding *b, return err; } - cl->rx_vtbl.spawn_reply = spawn_reply_handler; - if (capref_is_null(inheritcn_cap) && capref_is_null(argcn_cap)) { - err = cl->tx_vtbl.spawn_request(cl, NOP_CONT, cap_procmng, domain_cap, - path, argvbuf, argvbytes, envbuf, - envbytes, flags); - } else { - err = cl->tx_vtbl.spawn_with_caps_request(cl, NOP_CONT, cap_procmng, - domain_cap, path, argvbuf, - argvbytes, envbuf, envbytes, - inheritcn_cap, argcn_cap, - flags); - } - if (err_is_fail(err)) { - DEBUG_ERR(err, "sending spawn request"); - pending_clients_release(domain_cap, NULL); - return err_push(err, PROC_MGMT_ERR_SPAWND_REQUEST); - } + struct pending_spawn *spawn = (struct pending_spawn*) malloc( + sizeof(struct pending_spawn)); + spawn->domain_cap = domain_cap; + spawn->b = cl; + spawn->core_id = core_id; + spawn->path = path; + spawn->argvbuf = argvbuf; + spawn->argvbytes = argvbytes; + spawn->envbuf = envbuf; + spawn->envbytes = envbytes; + spawn->inheritcn_cap = inheritcn_cap; + spawn->argcn_cap = argcn_cap; + spawn->flags = flags; + + event_mutex_enqueue_lock(&cl->mutex, &spawn->qn, + (struct event_closure) { + .handler = spawn_request_sender, + .arg = spawn }); return SYS_ERR_OK; } @@ -382,10 +677,9 @@ static void spawn_handler(struct proc_mgmt_binding *b, coreid_t core_id, uint8_t flags) { errval_t err, resp_err; - struct capref domain_cap; err = spawn_handler_common(b, ClientType_Spawn, core_id, path, argvbuf, argvbytes, envbuf, envbytes, NULL_CAP, NULL_CAP, - flags, &domain_cap); + flags); if (err_is_ok(err)) { // Will respond to client when we get the reply from spawnd. return; @@ -405,10 +699,9 @@ static void spawn_with_caps_handler(struct proc_mgmt_binding *b, struct capref argcn_cap, uint8_t flags) { errval_t err, resp_err; - struct capref domain_cap; err = spawn_handler_common(b, ClientType_SpawnWithCaps, core_id, path, argvbuf, argvbytes, envbuf, envbytes, - inheritcn_cap, argcn_cap, flags, &domain_cap); + inheritcn_cap, argcn_cap, flags); if (err_is_ok(err)) { // Will respond to client when we get the reply from spawnd. return; @@ -446,17 +739,18 @@ static void span_handler(struct proc_mgmt_binding *b, struct capref domain_cap, goto respond_with_err; } - cl->rx_vtbl.spawn_reply = spawn_reply_handler; - err = cl->tx_vtbl.span_request(cl, NOP_CONT, cap_procmng, domain_cap, vroot, - dispframe); - if (err_is_ok(err)) { - // Will respond to client when we get the reply from spawnd. - return; - } else { - DEBUG_ERR(err, "sending span request"); - pending_clients_release(domain_cap, NULL); - err = err_push(err, PROC_MGMT_ERR_SPAWND_REQUEST); - } + struct pending_span *span = (struct pending_span*) malloc( + sizeof(struct pending_span)); + span->domain_cap = domain_cap; + span->b = cl; + span->core_id = core_id; + span->vroot = vroot; + span->dispframe = dispframe; + + event_mutex_enqueue_lock(&cl->mutex, &span->qn, + (struct event_closure) { + .handler = span_request_sender, + .arg = span }); respond_with_err: resp_err = b->tx_vtbl.span_response(b, NOP_CONT, err); @@ -490,20 +784,42 @@ static errval_t kill_handler_common(struct proc_mgmt_binding *b, } struct spawn_binding *spb = entry->spawnds[i]->b; - spb->rx_vtbl.spawn_reply = spawn_reply_handler; - errval_t req_err = spb->tx_vtbl.kill_request(spb, NOP_CONT, cap_procmng, - domain_cap); - if (err_is_fail(req_err)) { - DEBUG_ERR(req_err, "failed to send kill_request to spawnd %u\n", i); + + struct pending_kill_exit_cleanup *cmd = (struct pending_kill_exit_cleanup*) malloc( + sizeof(struct pending_kill_exit_cleanup)); + cmd->domain_cap = domain_cap; + cmd->sb = spb; + + switch (type) { + case ClientType_Kill: + cmd->pmb = b; + + event_mutex_enqueue_lock(&spb->mutex, + &cmd->qn, + (struct event_closure) { + .handler = kill_request_sender, + .arg = cmd }); + break; + + case ClientType_Exit: + event_mutex_enqueue_lock(&spb->mutex, + &cmd->qn, + (struct event_closure) { + .handler = exit_request_sender, + .arg = cmd }); + break; + default: + USER_PANIC("invalid client type for kill: %u\n", type); } } return SYS_ERR_OK; } -static void kill_handler(struct proc_mgmt_binding *b, struct capref domain_cap) +static void kill_handler(struct proc_mgmt_binding *b, + struct capref victim_domain_cap) { - errval_t err = kill_handler_common(b, domain_cap, ClientType_Kill, + errval_t err = kill_handler_common(b, victim_domain_cap, ClientType_Kill, EXIT_STATUS_KILLED); if (err_is_fail(err)) { errval_t resp_err = b->tx_vtbl.kill_response(b, NOP_CONT, err); @@ -522,7 +838,7 @@ static void exit_handler(struct proc_mgmt_binding *b, struct capref domain_cap, DEBUG_ERR(err, "processing exit_handler for requesting domain, exit " "code %u", exit_status); } - // Error or not, there's no client to reply to anymore. + // Error or not, there's no client to respond to anymore. } static void wait_handler(struct proc_mgmt_binding *b, struct capref domain_cap) @@ -560,7 +876,7 @@ static struct proc_mgmt_rx_vtbl monitor_vtbl = { .spawn_with_caps_call = spawn_with_caps_handler, .span_call = span_handler, .kill_call = kill_handler, - .exit = exit_handler, + .exit_call = exit_handler, .wait_call = wait_handler }; @@ -570,7 +886,7 @@ static struct proc_mgmt_rx_vtbl non_monitor_vtbl = { .spawn_with_caps_call = spawn_with_caps_handler, .span_call = span_handler, .kill_call = kill_handler, - .exit = exit_handler, + .exit_call = exit_handler, .wait_call = wait_handler };