Change proc_mgmt/pending_clients.h to use reply queues instead of hashtables.
authorRazvan Damachi <razvan.damachi@gmail.com>
Thu, 13 Jul 2017 17:50:59 +0000 (19:50 +0200)
committerSimon Gerber <simon.gerber@inf.ethz.ch>
Thu, 31 Aug 2017 14:35:09 +0000 (16:35 +0200)
The process manager now enqueues a new client to send a reply to for every
request it sends to a spawnd. This happens for every spawnd instance (the
process manager has a "send" queue and a "receive" queue for every spawnd
instance it knows of -- the latter is now used for pending clients).

Whenever a new reply from spawnd is received, the process manager now pops the
next expected client to reply to from the receive queue, instead of retrieving
it from a hashtable. This approach is faster, as it does not rely on asking the
monitor to identify caps in order to retrieve pending clients from hashtables.

However, this approach assumes:
1) that spawnd is single-threaded & event-based;
2) that Flounder messages arrive in-order (Barrelfish seems to guarantee this).

Signed-off-by: Razvan Damachi <razvan.damachi@gmail.com>

if/spawn.if
usr/proc_mgmt/Hakefile
usr/proc_mgmt/domain.c
usr/proc_mgmt/pending_clients.c [deleted file]
usr/proc_mgmt/pending_clients.h
usr/proc_mgmt/service.c
usr/proc_mgmt/spawnd_state.c
usr/proc_mgmt/spawnd_state.h
usr/spawnd/service.c

index a501255..0e51e96 100644 (file)
@@ -27,7 +27,6 @@ interface spawn "Interface to spawn domains" {
                           char argvbuf[argvbytes, 2048],
                           char envbuf[envbytes, 2048],
                           uint8 flags);
-    message spawn_reply(cap domain_cap, errval err);
 
     message spawn_with_caps_request(cap procmng_cap,
                                     cap domain_cap,
@@ -37,20 +36,15 @@ interface spawn "Interface to spawn domains" {
                                     cap inheritcn_cap,
                                     cap argcn_cap,
                                     uint8 flags);
-    message spawn_with_caps_reply(cap domain_cap, errval err);
 
     message span_request(cap procmng_cap, cap domain_cap, cap vroot,
                          cap dispframe);
-    message span_reply(cap domain_cap, errval err);
 
     message kill_request(cap procmng_cap, cap domain_cap);
-    message kill_reply(cap domain_cap, errval err);
-
-    message exit_request(cap procmng_cap, cap domain_cap);
-    message exit_reply(cap domain_cap, errval err);
 
     message cleanup_request(cap procmng_cap, cap domain_cap);
-    message cleanup_reply(cap domain_cap, errval err);
+    
+    message spawn_reply(errval err);
 
     rpc spawn_proc_mgmt_domain(in cap domain_cap,
                                in String path[2048],
index 6da0259..296f2f9 100644 (file)
@@ -12,7 +12,7 @@
 
 [ build application { target = "proc_mgmt",
                       cFiles = [ "main.c", "service.c", "spawnd_state.c", 
-                                 "domain.c", "pending_clients.c" ],
+                                 "domain.c" ],
                       addLibraries = libDeps [ "skb", "dist", "lwip",
                                                "collections" ],
                       flounderDefs = [ "monitor", "monitor_blocking" ],
@@ -23,7 +23,7 @@
                     },
   build application { target = "proc_mgmt",
                       cFiles = [ "main.c", "service.c", "spawnd_state.c", 
-                                 "domain.c", "pending_clients.c" ],
+                                 "domain.c" ],
                       addLibraries = libDeps [ "skb", "dist", "lwip",
                                                "collections" ],
                       flounderDefs = [ "monitor", "monitor_blocking"],
@@ -34,7 +34,7 @@
                     },
   build application { target = "proc_mgmt",
                       cFiles = [ "main.c", "service.c", "spawnd_state.c", 
-                                 "domain.c", "pending_clients.c" ],
+                                 "domain.c" ],
                       addLibraries = libDeps [ "skb", "dist", "lwip",
                                                "collections" ],
                       flounderDefs = [ "monitor", "monitor_blocking"],
index 1d71ca4..09f7125 100644 (file)
@@ -107,28 +107,6 @@ errval_t domain_spawn(struct capref domain_cap, coreid_t core_id)
     return SYS_ERR_OK;
 }
 
-errval_t domain_can_span(struct capref domain_cap, coreid_t core_id)
-{
-    struct domain_entry *entry = NULL;
-    errval_t err = domain_get_by_cap(domain_cap, &entry);
-    if (err_is_fail(err)) {
-        return err;
-    }
-
-    assert(entry != NULL);
-    if (entry->status != DOMAIN_STATUS_RUNNING) {
-        return PROC_MGMT_ERR_DOMAIN_NOT_RUNNING;
-    }
-
-    if (entry->spawnds[core_id] != NULL) {
-        // TODO(razvan): Maybe we want to allow the same domain to span multiple
-        // dispatchers onto the same core?
-        return PROC_MGMT_ERR_ALREADY_SPANNED;
-    }
-
-    return SYS_ERR_OK;
-}
-
 errval_t domain_span(struct capref domain_cap, coreid_t core_id)
 {
     struct domain_entry *entry = NULL;
diff --git a/usr/proc_mgmt/pending_clients.c b/usr/proc_mgmt/pending_clients.c
deleted file mode 100644 (file)
index 36f2ab6..0000000
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * \brief Client handling internals for the process manager.
- *
- * Copyright (c) 2017, ETH Zurich.
- * All rights reserved.
- *
- * This file is distributed under the terms in the attached LICENSE file.
- * If you do not find this file, copies can be found by writing to:
- * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
- */
-
-#include <collections/hash_table.h>
-
-#include "domain.h"
-#include "pending_clients.h"
-
-static collections_hash_table *spawn_table = NULL;
-static collections_hash_table *spawn_with_caps_table = NULL;
-static collections_hash_table *span_table = NULL;
-static collections_hash_table *kill_table = NULL;
-static collections_hash_table *exit_table = NULL;
-static collections_hash_table *cleanup_table = NULL;
-
-errval_t pending_clients_add(struct capref domain_cap,
-                             struct proc_mgmt_binding *b, enum ClientType type,
-                             coreid_t core_id)
-{
-    collections_hash_table **table;
-    switch (type) {
-        case ClientType_Spawn:
-            table = &spawn_table;
-            break;
-        case ClientType_SpawnWithCaps:
-            table = &spawn_with_caps_table;
-            break;
-        case ClientType_Span:
-            table = &span_table;
-            break;
-        case ClientType_Kill:
-            table = &kill_table;
-            break;
-        case ClientType_Exit:
-            table = &exit_table;
-            break;
-        case ClientType_Cleanup:
-            table = &cleanup_table;
-            break;
-        default:
-            USER_PANIC("Unhandled client type %d\n", type);
-    }
-
-    if (*table == NULL) {
-        collections_hash_create_with_buckets(table, HASH_INDEX_BUCKETS, NULL);
-        if (*table == NULL) {
-            return PROC_MGMT_ERR_CREATE_CLIENTS_TABLE;
-        }
-    }
-
-    uint64_t key;
-    errval_t err = domain_cap_hash(domain_cap, &key);
-    if (err_is_fail(err)) {
-        return err;
-    }
-
-    struct pending_client *cl = (struct pending_client*) malloc(
-            sizeof(struct pending_client));
-    cl->b = b;
-    cl->domain_cap = domain_cap;
-    cl->core_id = core_id;
-    cl->type = type;
-    cl->next = NULL;
-
-    if (type == ClientType_Kill) {
-        // Special case: multiple clients might have issued a kill for some
-        // domain. Need to chain them together.
-        void *entry = collections_hash_find(*table, key);
-        if (entry != NULL) {
-            struct pending_client* old = (struct pending_client*) entry;
-            collections_hash_delete(*table, key);
-            cl->next = old;
-        }
-    }
-    
-    collections_hash_insert(*table, key, cl);
-
-    return SYS_ERR_OK;
-}
-
-errval_t pending_clients_release(struct capref domain_cap, enum ClientType type,
-                                 struct pending_client **ret_cl)
-{
-    uint64_t key;
-    errval_t err = domain_cap_hash(domain_cap, &key);
-    if (err_is_fail(err)) {
-        return err;
-    }
-
-    collections_hash_table *table;
-    switch (type) {
-        case ClientType_Spawn:
-            table = spawn_table;
-            break;
-        case ClientType_SpawnWithCaps:
-            table = spawn_with_caps_table;
-            break;
-        case ClientType_Span:
-            table = span_table;
-            break;
-        case ClientType_Kill:
-            table = kill_table;
-            break;
-        case ClientType_Exit:
-            table = exit_table;
-            break;
-        case ClientType_Cleanup:
-            table = cleanup_table;
-            break;
-        default:
-            USER_PANIC("Unhandled client type %d\n", type);
-    }
-
-    assert(table != NULL);
-
-    void *entry = collections_hash_find(table, key);
-    if (entry == NULL) {
-        return PROC_MGMT_ERR_CLIENTS_TABLE_FIND;
-    }
-    struct pending_client *cl = (struct pending_client*) entry;
-    if (ret_cl != NULL) {
-        *ret_cl = cl;
-    } else {
-        free(cl);
-    }
-
-    collections_hash_delete(table, key);
-
-    return SYS_ERR_OK;
-}
-
-errval_t pending_clients_release_one(struct capref domain_cap,
-                                     enum ClientType type,
-                                     struct proc_mgmt_binding *b,
-                                     struct pending_client **ret_cl)
-{
-    uint64_t key;
-    errval_t err = domain_cap_hash(domain_cap, &key);
-    if (err_is_fail(err)) {
-        return err;
-    }
-
-    collections_hash_table **table;
-    switch (type) {
-        case ClientType_Spawn:
-            table = &spawn_table;
-            break;
-        case ClientType_SpawnWithCaps:
-            table = &spawn_with_caps_table;
-            break;
-        case ClientType_Span:
-            table = &span_table;
-            break;
-        case ClientType_Kill:
-            table = &kill_table;
-            break;
-        case ClientType_Exit:
-            table = &exit_table;
-            break;
-        case ClientType_Cleanup:
-            table = &cleanup_table;
-            break;
-        default:
-            USER_PANIC("Unhandled client type %d\n", type);
-    }
-
-    void *entry = collections_hash_find(*table, key);
-    if (entry == NULL) {
-        return PROC_MGMT_ERR_CLIENTS_TABLE_FIND;
-    }
-    struct pending_client *cl = (struct pending_client*) entry;
-    if (cl->b == b) {
-        struct pending_client *tmp = cl;
-        cl = cl->next;
-        if (ret_cl != NULL) {
-            *ret_cl = tmp;
-        } else {
-            free(tmp);
-        }
-    } else {
-        while (cl->next != NULL) {
-            if (cl->next->b == b) {
-                struct pending_client *tmp = cl->next;
-                cl->next = cl->next->next;
-                if (ret_cl != NULL) {
-                    *ret_cl = tmp;
-                } else {
-                    free(tmp);
-                }
-                break;
-            }
-            cl = cl->next;
-        }
-    }
-
-    collections_hash_delete(*table, key);
-    if (cl != NULL) {
-        collections_hash_insert(*table, key, cl);
-    }
-
-    return SYS_ERR_OK;
-}
index 703bcc1..9199ef5 100644 (file)
@@ -16,6 +16,8 @@
 #include <if/proc_mgmt_defs.h>
 #include <if/spawn_defs.h>
 
+#include "domain.h"
+
 #define HASH_INDEX_BUCKETS 6151
 
 enum ClientType {
@@ -48,7 +50,8 @@ struct pending_spawn {
 };
 
 struct pending_span {
-    struct capref domain_cap;
+       struct capref domain_cap;
+    struct domain_entry *entry;
 
        struct spawn_binding *b;
 
@@ -57,31 +60,16 @@ struct pending_span {
        struct capref dispframe;
 };
 
-struct pending_kill_exit_cleanup {
-    struct capref domain_cap;
-       struct spawn_binding *sb;
-       struct proc_mgmt_binding *pmb;
+struct pending_kill_cleanup {
+       struct capref domain_cap;
+    struct domain_entry *entry;
+       struct spawn_binding *b;
 };
 
 struct pending_client {
-    struct proc_mgmt_binding *b;
-
-    struct capref domain_cap;
-
-    coreid_t core_id;
-    enum ClientType type;
-
-    struct pending_client *next;
+       struct proc_mgmt_binding *b;
+       enum ClientType type;
+       void *st;
 };
 
-errval_t pending_clients_add(struct capref domain_cap,
-                             struct proc_mgmt_binding *b, enum ClientType type,
-                             coreid_t core_id);
-errval_t pending_clients_release(struct capref domain_cap, enum ClientType type,
-                                 struct pending_client **ret_cl);
-errval_t pending_clients_release_one(struct capref domain_cap,
-                                        enum ClientType type,
-                                        struct proc_mgmt_binding *b,
-                                     struct pending_client **ret_cl);
-
 #endif  // PENDING_CLIENTS_H
\ No newline at end of file
index 40a8416..83d82fd 100644 (file)
@@ -57,30 +57,162 @@ static void add_spawnd_handler_non_monitor(struct proc_mgmt_binding *b,
     //              err_getstring(PROC_MGMT_ERR_NOT_MONITOR));
 }
 
-static void spawn_reply_handler(struct spawn_binding *b,
-                                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 bool cleanup_request_sender(struct msg_queue_elem *m);
+
+static void spawn_reply_handler(struct spawn_binding *b, errval_t spawn_err)
+{
+    struct pending_client *cl =
+            (struct pending_client*) spawnd_state_dequeue_recv(b->st);
+
+    struct pending_spawn *spawn = NULL;
+    struct pending_span *span = NULL;
+    struct pending_kill_cleanup *kc = NULL;
+
+    struct domain_entry *entry;
+    
+    errval_t err, resp_err;
+
+    switch (cl->type) {
+        case ClientType_Spawn:
+        case ClientType_SpawnWithCaps:
+            spawn = (struct pending_spawn*) cl->st;
+            err = spawn_err;
+            if (err_is_ok(spawn_err)) {
+                err = domain_spawn(spawn->domain_cap, spawn->core_id);
+                if (cl->type == ClientType_Spawn) {
+                    resp_err = cl->b->tx_vtbl.spawn_response(cl->b, NOP_CONT,
+                                                             err,
+                                                             spawn->domain_cap);
+                } else {
+                    resp_err = cl->b->tx_vtbl.spawn_with_caps_response(cl->b,
+                                                                       NOP_CONT,
+                                                                       err,
+                                                                       spawn->domain_cap);
+                }
+            }
+
+            free(spawn);
+            break;
+
+        case ClientType_Span:
+            span = (struct pending_span*) cl->st;
+            entry = span->entry;
+            if (entry->status == DOMAIN_STATUS_RUNNING) {
+                resp_err = cl->b->tx_vtbl.span_response(cl->b, NOP_CONT,
+                                                        spawn_err);
+            }
+
+            free(span);
+            break;
+
+        case ClientType_Cleanup:
+            kc = (struct pending_kill_cleanup*) cl->st;
+            entry = kc->entry;
+
+            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.
+            }
+
+            free(kc);
+            break;
+
+        case ClientType_Kill:
+        case ClientType_Exit:
+            kc = (struct pending_kill_cleanup*) cl->st;
+            entry = kc->entry;
+
+            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;
+
+                if (cl->type == ClientType_Kill) {
+                    entry->exit_status = EXIT_STATUS_KILLED;
+                    resp_err = cl->b->tx_vtbl.kill_response(cl->b, NOP_CONT,
+                                                            spawn_err);
+                }
+
+                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_cleanup *cleanup =
+                            (struct pending_kill_cleanup*) malloc(
+                                    sizeof(struct pending_kill_cleanup));
+                    cleanup->b = spb;
+                    cleanup->domain_cap = kc->domain_cap;
+                    cleanup->entry = entry;
+
+                    struct pending_client *cleanup_cl =
+                            (struct pending_client*) malloc(
+                                    sizeof(struct pending_client));
+                    cleanup_cl->b = cl->b;
+                    cleanup_cl->type = ClientType_Cleanup;
+                    cleanup_cl->st = cleanup;
+
+                    struct msg_queue_elem *msg = (struct msg_queue_elem*) malloc(
+                            sizeof(struct msg_queue_elem));
+                    msg->st = cleanup_cl;
+                    msg->cont = cleanup_request_sender;
+
+                    err = spawnd_state_enqueue_send(entry->spawnds[i], msg);
+
+                    if (err_is_fail(err)) {
+                        DEBUG_ERR(err, "enqueuing cleanup request");
+                        free(cleanup);
+                        free(cleanup_cl);
+                        free(msg);
+                    }
+                }
+            }
+
+            free(kc);
+            break;
+
+        default:
+            USER_PANIC("Unknown client type in spawn_reply_handler: %u\n",
+                       cl->type);
+    }
+
+    free(cl);
+}
 
 static bool spawn_request_sender(struct msg_queue_elem *m)
 {
-    struct pending_spawn *spawn = (struct pending_spawn*) m->st;
+    struct pending_client *cl = (struct pending_client*) m->st;
+    struct pending_spawn *spawn = (struct pending_spawn*) cl->st;
+    spawn->b->rx_vtbl.spawn_reply = spawn_reply_handler;
 
     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,
@@ -93,7 +225,6 @@ static bool spawn_request_sender(struct msg_queue_elem *m)
                                                         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,
@@ -109,7 +240,6 @@ static bool spawn_request_sender(struct msg_queue_elem *m)
         }
     }
 
-    free(spawn);
     free(m);
 
     return true;
@@ -117,10 +247,11 @@ static bool spawn_request_sender(struct msg_queue_elem *m)
 
 static bool span_request_sender(struct msg_queue_elem *m)
 {
-    struct pending_span *span = (struct pending_span*) m->st;
+    struct pending_client *cl = (struct pending_client*) m->st;
+    struct pending_span *span = (struct pending_span*) cl->st;
 
     errval_t err;
-    span->b->rx_vtbl.span_reply = span_reply_handler;
+    span->b->rx_vtbl.spawn_reply = spawn_reply_handler;
     err = span->b->tx_vtbl.span_request(span->b, NOP_CONT, cap_procmng,
                                         span->domain_cap, span->vroot,
                                         span->dispframe);
@@ -133,7 +264,6 @@ static bool span_request_sender(struct msg_queue_elem *m)
         }
     }
 
-    free(span);
     free(m);
 
     return true;
@@ -141,11 +271,12 @@ static bool span_request_sender(struct msg_queue_elem *m)
 
 static bool kill_request_sender(struct msg_queue_elem *m)
 {
-    struct pending_kill_exit_cleanup *kill = (struct pending_kill_exit_cleanup*) m->st;
+    struct pending_client *cl = (struct pending_client*) m->st;
+    struct pending_kill_cleanup *kill = (struct pending_kill_cleanup*) cl->st;
 
     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->b->rx_vtbl.spawn_reply = spawn_reply_handler;
+    err = kill->b->tx_vtbl.kill_request(kill->b, NOP_CONT, cap_procmng,
                                         kill->domain_cap);
 
     if (err_is_fail(err)) {
@@ -156,30 +287,6 @@ static bool kill_request_sender(struct msg_queue_elem *m)
         }
     }
 
-    free(kill);
-    free(m);
-
-    return true;
-}
-
-static bool exit_request_sender(struct msg_queue_elem *m)
-{
-    struct pending_kill_exit_cleanup *exit = (struct pending_kill_exit_cleanup*) m->st;
-
-    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_fail(err)) {
-        if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
-            return false;
-        } else {
-            USER_PANIC_ERR(err, "sending exit request");
-        }
-    }
-
-    free(exit);
     free(m);
 
     return true;
@@ -187,11 +294,13 @@ static bool exit_request_sender(struct msg_queue_elem *m)
 
 static bool cleanup_request_sender(struct msg_queue_elem *m)
 {
-    struct pending_kill_exit_cleanup *cleanup = (struct pending_kill_exit_cleanup*) m->st;
+    struct pending_client *cl = (struct pending_client*) m->st;
+    struct pending_kill_cleanup *cleanup = (struct pending_kill_cleanup*) cl->st;
 
     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->b->rx_vtbl.spawn_reply = spawn_reply_handler;
+    err = cleanup->b->tx_vtbl.cleanup_request(cleanup->b, NOP_CONT,
+                                              cap_procmng,
                                               cleanup->domain_cap);
 
     if (err_is_fail(err)) {
@@ -202,355 +311,11 @@ static bool cleanup_request_sender(struct msg_queue_elem *m)
         }
     }
 
-    free(cleanup);
     free(m);
 
     return true;
 }
 
-static void spawn_reply_handler(struct spawn_binding *b,
-                                struct capref domain_cap, errval_t spawn_err)
-{
-    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 = 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_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);
-}
-
-static void spawn_with_caps_reply_handler(struct spawn_binding *b,
-                                          struct capref domain_cap,
-                                          errval_t spawn_err)
-{
-    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)
-{
-    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)
-{
-    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)
-{
-    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");
-            }
-            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;
-
-            struct msg_queue_elem *msg = (struct msg_queue_elem*) malloc(
-                    sizeof(struct msg_queue_elem));
-            msg->st = cleanup;
-            msg->cont = cleanup_request_sender;
-
-            err = spawnd_state_enqueue_send(entry->spawnds[i], msg);
-
-            if (err_is_fail(err)) {
-                DEBUG_ERR(err, "enqueuing cleanup request");
-                free(cleanup);
-                free(msg);
-            }
-        }
-    } 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");
-        }
-    }
-}
-
-static void exit_reply_handler(struct spawn_binding *b,
-                               struct capref domain_cap, errval_t exit_err)
-{
-    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;
-
-        err = pending_clients_add(domain_cap, NULL, ClientType_Cleanup,
-                                  MAX_COREID);
-        if (err_is_fail(err)) {
-            DEBUG_ERR(err, "pending_clients_add in exit_reply_handler");
-        }
-
-        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;
-
-            struct msg_queue_elem *msg = (struct msg_queue_elem*) malloc(
-                    sizeof(struct msg_queue_elem));
-            msg->st = cleanup;
-            msg->cont = cleanup_request_sender;
-
-            err = spawnd_state_enqueue_send(entry->spawnds[i], msg);
-
-            if (err_is_fail(err)) {
-                DEBUG_ERR(err, "enqueuing cleanup request");
-                free(cleanup);
-                free(msg);
-            }
-        }
-    } 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");
-        }
-    }
-}
-
 static errval_t spawn_handler_common(struct proc_mgmt_binding *b,
                                      enum ClientType type,
                                      coreid_t core_id, const char *path,
@@ -580,12 +345,6 @@ static errval_t spawn_handler_common(struct proc_mgmt_binding *b,
         return err_push(err, PROC_MGMT_ERR_CREATE_DOMAIN_CAP);
     }
 
-    err = pending_clients_add(domain_cap, b, type, core_id);
-    if (err_is_fail(err)) {
-        DEBUG_ERR(err, "pending_clients_add");
-        return err;
-    }
-
     struct pending_spawn *spawn = (struct pending_spawn*) malloc(
             sizeof(struct pending_spawn));
     spawn->domain_cap = domain_cap;
@@ -600,9 +359,15 @@ static errval_t spawn_handler_common(struct proc_mgmt_binding *b,
     spawn->argcn_cap = argcn_cap;
     spawn->flags = flags;
 
+    struct pending_client *spawn_cl = (struct pending_client*) malloc(
+            sizeof(struct pending_client));
+    spawn_cl->b = b;
+    spawn_cl->type = type;
+    spawn_cl->st = spawn;
+
     struct msg_queue_elem *msg = (struct msg_queue_elem*) malloc(
             sizeof(struct msg_queue_elem));
-    msg->st = spawn;
+    msg->st = spawn_cl;
     msg->cont = spawn_request_sender;
 
     err = spawnd_state_enqueue_send(spawnd, msg);
@@ -610,6 +375,7 @@ static errval_t spawn_handler_common(struct proc_mgmt_binding *b,
     if (err_is_fail(err)) {
         DEBUG_ERR(err, "enqueuing spawn request");
         free(spawn);
+        free(spawn_cl);
         free(msg);
     }
 
@@ -664,11 +430,25 @@ static void span_handler(struct proc_mgmt_binding *b, struct capref domain_cap,
                          struct capref dispframe)
 {
     errval_t err, resp_err;
-    err = domain_can_span(domain_cap, core_id);
+    struct domain_entry *entry = NULL;
+    err = domain_get_by_cap(domain_cap, &entry);
     if (err_is_fail(err)) {
         goto respond_with_err;
     }
 
+    assert(entry != NULL);
+    if (entry->status != DOMAIN_STATUS_RUNNING) {
+        err = PROC_MGMT_ERR_DOMAIN_NOT_RUNNING;
+        goto respond_with_err;
+    }
+
+    if (entry->spawnds[core_id] != NULL) {
+        // TODO(razvan): Maybe we want to allow the same domain to span multiple
+        // dispatchers onto the same core?
+        err = PROC_MGMT_ERR_ALREADY_SPANNED;
+        goto respond_with_err;
+    }
+
     if (!spawnd_state_exists(core_id)) {
         err = PROC_MGMT_ERR_INVALID_SPAWND;
         goto respond_with_err;
@@ -679,22 +459,24 @@ static void span_handler(struct proc_mgmt_binding *b, struct capref domain_cap,
     struct spawn_binding *cl = spawnd->b;
     assert(cl != NULL);
 
-    err = pending_clients_add(domain_cap, b, ClientType_Span, core_id);
-    if (err_is_fail(err)) {
-        goto respond_with_err;
-    }
-
     struct pending_span *span = (struct pending_span*) malloc(
             sizeof(struct pending_span));
     span->domain_cap = domain_cap;
+    span->entry = entry;
     span->b = cl;
     span->core_id = core_id;
     span->vroot = vroot;
     span->dispframe = dispframe;
 
+    struct pending_client *span_cl = (struct pending_client*) malloc(
+            sizeof(struct pending_client));
+    span_cl->b = b;
+    span_cl->type = ClientType_Span;
+    span_cl->st = span;
+
     struct msg_queue_elem *msg = (struct msg_queue_elem*) malloc(
             sizeof(struct msg_queue_elem));
-    msg->st = span;
+    msg->st = span_cl;
     msg->cont = span_request_sender;
 
     err = spawnd_state_enqueue_send(spawnd, msg);
@@ -702,6 +484,7 @@ static void span_handler(struct proc_mgmt_binding *b, struct capref domain_cap,
     if (err_is_fail(err)) {
         DEBUG_ERR(err, "enqueuing span request");
         free(span);
+        free(span_cl);
         free(msg);
     }
 
@@ -717,13 +500,8 @@ static errval_t kill_handler_common(struct proc_mgmt_binding *b,
                                     enum ClientType type,
                                     uint8_t exit_status)
 {
-    errval_t err = pending_clients_add(domain_cap, b, type, MAX_COREID);
-    if (err_is_fail(err)) {
-        return err;
-    }
-
     struct domain_entry *entry;
-    err = domain_get_by_cap(domain_cap, &entry);
+    errval_t err = domain_get_by_cap(domain_cap, &entry);
     if (err_is_fail(err)) {
         return err;
     }
@@ -738,43 +516,29 @@ static errval_t kill_handler_common(struct proc_mgmt_binding *b,
 
         struct spawn_binding *spb = entry->spawnds[i]->b;
 
-        struct pending_kill_exit_cleanup *cmd = (struct pending_kill_exit_cleanup*) malloc(
-                sizeof(struct pending_kill_exit_cleanup));
+        struct pending_kill_cleanup *cmd = (struct pending_kill_cleanup*) malloc(
+                sizeof(struct pending_kill_cleanup));
         cmd->domain_cap = domain_cap;
-        cmd->sb = spb;
+        cmd->entry = entry;
+        cmd->b = spb;
+
+        struct pending_client *cl = (struct pending_client*) malloc(
+                sizeof(struct pending_client));
+        cl->b = b;
+        cl->type = type;
+        cl->st = cmd;
 
         struct msg_queue_elem *msg = (struct msg_queue_elem*) malloc(
                 sizeof(struct msg_queue_elem));
-        msg->st = cmd;
-
-        switch (type) {
-            case ClientType_Kill:
-                cmd->pmb = b;
-                msg->cont = kill_request_sender;
-
-                err = spawnd_state_enqueue_send(entry->spawnds[i], msg);
+        msg->st = cl;
+        msg->cont = kill_request_sender;
 
-                if (err_is_fail(err)) {
-                    DEBUG_ERR(err, "enqueuing kill request");
-                    free(cmd);
-                    free(msg);
-                }
-                break;
-
-            case ClientType_Exit:
-                msg->cont = exit_request_sender;
-
-                err = spawnd_state_enqueue_send(entry->spawnds[i], msg);
-
-                if (err_is_fail(err)) {
-                    DEBUG_ERR(err, "enqueuing exit request");
-                    free(cmd);
-                    free(msg);
-                }
-                break;
-
-            default:
-                USER_PANIC("invalid client type for kill: %u\n", type);
+        err = spawnd_state_enqueue_send(entry->spawnds[i], msg);
+        if (err_is_fail(err)) {
+            DEBUG_ERR(err, "enqueuing kill request");
+            free(cmd);
+            free(cl);
+            free(msg);
         }
     }
 
index 219a000..3fb7535 100644 (file)
@@ -25,8 +25,12 @@ errval_t spawnd_state_alloc(coreid_t core_id, struct spawn_binding *b)
 
     spawnds[core_id]->b = b;
     spawnds[core_id]->core_id = core_id;
-    spawnds[core_id]->queue.head = NULL;
-    spawnds[core_id]->queue.tail = NULL;
+    spawnds[core_id]->sendq.head = NULL;
+    spawnds[core_id]->sendq.tail = NULL;
+    spawnds[core_id]->recvq.head = NULL;
+    spawnds[core_id]->recvq.tail = NULL;
+
+    b->st = spawnds[core_id];
 
     return SYS_ERR_OK;
 }
@@ -56,7 +60,7 @@ inline struct spawnd_state *spawnd_state_get(coreid_t core_id)
  *
  * \return true if queue was empty, false if not.
  */
-static bool enqueue_send(struct msg_queue *q, struct msg_queue_elem *m)
+static bool enqueue(struct msg_queue *q, struct msg_queue_elem *m)
 {
     assert(m->next == NULL);
 
@@ -79,7 +83,7 @@ static bool enqueue_send(struct msg_queue *q, struct msg_queue_elem *m)
  *
  * \return the newly dequeued element.
  */
-static struct msg_queue_elem *dequeue_send(struct msg_queue *q)
+static struct msg_queue_elem *dequeue(struct msg_queue *q)
 {
     // Queue should have at least one element
     assert(q->head != NULL && q->tail != NULL);
@@ -101,7 +105,7 @@ static struct msg_queue_elem *dequeue_send(struct msg_queue *q)
  *
  * \return true if queue was empty, false if not.
  */
-static bool enqueue_send_at_front(struct msg_queue *q, struct msg_queue_elem *m)
+static bool enqueue_at_front(struct msg_queue *q, struct msg_queue_elem *m)
 {
     assert(m->next == NULL);
     if(q->tail == NULL) {
@@ -118,17 +122,24 @@ static bool enqueue_send_at_front(struct msg_queue *q, struct msg_queue_elem *m)
 static void spawnd_send_handler(void *arg)
 {
     struct spawnd_state *spawnd = (struct spawnd_state*) arg;
-    struct msg_queue *q = &spawnd->queue;
+    struct msg_queue *q = &spawnd->sendq;
 
     // Dequeue next element from the queue
-    struct msg_queue_elem *m = (struct msg_queue_elem*) dequeue_send(q);
+    struct msg_queue_elem *m = (struct msg_queue_elem*) dequeue(q);
 
     assert(m->cont != NULL);
-    if (!m->cont(m)) {
-        // Flounder TX busy, need to re-enqueue message.
+    if (m->cont(m)) {
+        // Send continuation succeeded, need to enqueue a receive.
+        struct msg_queue_elem *recvm = (struct msg_queue_elem*) malloc(
+                sizeof(struct msg_queue_elem));
+        recvm->st = m->st;
+        recvm->next = NULL;
+        enqueue(&spawnd->recvq, recvm);
+    } else {
+        // Send continuation failed, need to re-enqueue message.
         // TODO(razvan): Re-enqueuing at the front of the queue, to preserve
         // original message order. Could a different strategy be preferrable?
-        enqueue_send_at_front(q, m);
+        enqueue_at_front(q, m);
     }
 
     if (q->head != NULL) {
@@ -149,10 +160,17 @@ errval_t spawnd_state_enqueue_send(struct spawnd_state *spawnd,
     msg->next = NULL;
 
     // If queue was empty, enqueue on waitset
-    if(enqueue_send(&spawnd->queue, msg)) {
+    if(enqueue(&spawnd->sendq, msg)) {
         return spawnd->b->register_send(spawnd->b, spawnd->b->waitset,
                                         MKCONT(spawnd_send_handler, spawnd));
     } else {
         return SYS_ERR_OK;
     }
+}
+
+void *spawnd_state_dequeue_recv(struct spawnd_state *spawnd)
+{
+    struct msg_queue_elem *m = dequeue(&spawnd->recvq);
+    assert(m != NULL);
+    return m->st;
 }
\ No newline at end of file
index 7fbc39f..4d801ab 100644 (file)
@@ -36,7 +36,8 @@ struct spawnd_state {
     coreid_t core_id;
     struct spawn_binding *b;
 
-    struct msg_queue queue;
+    struct msg_queue sendq;
+    struct msg_queue recvq;
 };
 
 errval_t spawnd_state_alloc(coreid_t core_id, struct spawn_binding *b);
@@ -46,5 +47,6 @@ struct spawnd_state *spawnd_state_get(coreid_t core_id);
 
 errval_t spawnd_state_enqueue_send(struct spawnd_state *spawnd,
                                    struct msg_queue_elem *msg);
+void *spawnd_state_dequeue_recv(struct spawnd_state *spawnd);
 
 #endif  // SPAWND_STATE
index 5fea71c..0706aae 100644 (file)
@@ -386,7 +386,7 @@ static void spawn_with_caps_request_handler(struct spawn_binding *b,
                                  &dummy_domain_id);
 
 reply:
-    reply_err = b->tx_vtbl.spawn_with_caps_reply(b, NOP_CONT, domain_cap, err);
+    reply_err = b->tx_vtbl.spawn_reply(b, NOP_CONT, err);
     if (err_is_fail(reply_err)) {
         DEBUG_ERR(err, "failed to send spawn_with_caps_reply");
     }
@@ -419,7 +419,7 @@ static void spawn_request_handler(struct spawn_binding *b,
                                  &dummy_domain_id);
 
 reply:
-    reply_err = b->tx_vtbl.spawn_reply(b, NOP_CONT, domain_cap, err);
+    reply_err = b->tx_vtbl.spawn_reply(b, NOP_CONT, err);
     if (err_is_fail(reply_err)) {
         DEBUG_ERR(err, "failed to send spawn_reply");
     }
@@ -564,7 +564,7 @@ static void span_request_handler(struct spawn_binding *b,
     }
 
 reply:
-    reply_err = b->tx_vtbl.span_reply(b, NOP_CONT, domain_cap, err);
+    reply_err = b->tx_vtbl.spawn_reply(b, NOP_CONT, err);
     if (err_is_fail(reply_err)) {
         DEBUG_ERR(err, "failed to send span_reply");
     }
@@ -584,53 +584,39 @@ static void cleanup_cap(struct capref cap)
     }
 }
 
-static errval_t kill_handler_common(struct capref procmng_cap,
-                                    struct capref domain_cap)
+static void kill_request_handler(struct spawn_binding *b,
+                                 struct capref procmng_cap,
+                                 struct capref victim_domain_cap)
 {
+    errval_t err, reply_err;
     struct capability ret;
-    errval_t err = monitor_cap_identify_remote(procmng_cap, &ret);
+    err = monitor_cap_identify_remote(procmng_cap, &ret);
     if (err_is_fail(err)) {
-        return err_push(err, SPAWN_ERR_IDENTIFY_PROC_MNGR_CAP);
+        err = err_push(err, SPAWN_ERR_IDENTIFY_PROC_MNGR_CAP);
+        goto reply;
     }
 
     if (ret.type != ObjType_ProcessManager) {
-        return SPAWN_ERR_NOT_PROC_MNGR;
+        err = SPAWN_ERR_NOT_PROC_MNGR;
+        goto reply;
     }
 
     struct ps_entry *pe;
-    err = ps_get_domain(domain_cap, &pe, NULL);
+    err = ps_get_domain(victim_domain_cap, &pe, NULL);
     if (err_is_fail(err)) {
-        return err_push(err, SPAWN_ERR_DOMAIN_NOTFOUND);
+        err = err_push(err, SPAWN_ERR_DOMAIN_NOTFOUND);
+        goto reply;
     }
 
     cleanup_cap(pe->dcb);
 
-    return SYS_ERR_OK;
-}
-
-static void kill_request_handler(struct spawn_binding *b,
-                                 struct capref procmng_cap,
-                                 struct capref victim_domain_cap)
-{
-    errval_t err = kill_handler_common(procmng_cap, victim_domain_cap);
-    errval_t reply_err = b->tx_vtbl.kill_reply(b, NOP_CONT, victim_domain_cap,
-                                               err);
+reply:
+    reply_err = b->tx_vtbl.spawn_reply(b, NOP_CONT, err);
     if (err_is_fail(reply_err)) {
         DEBUG_ERR(err, "failed to send kill_reply");
     }
 }
 
-static void exit_request_handler(struct spawn_binding *b,
-                                 struct capref procmng_cap,
-                                 struct capref domain_cap)
-{
-    errval_t err = kill_handler_common(procmng_cap, domain_cap);
-    errval_t reply_err = b->tx_vtbl.exit_reply(b, NOP_CONT, domain_cap, err);
-    if (err_is_fail(reply_err)) {
-        DEBUG_ERR(err, "failed to send exit_reply");
-    }
-}
-
 static void cleanup_request_handler(struct spawn_binding *b,
                                     struct capref procmng_cap,
                                     struct capref domain_cap)
@@ -664,7 +650,7 @@ static void cleanup_request_handler(struct spawn_binding *b,
     free(pe);
 
 reply:
-    reply_err = b->tx_vtbl.cleanup_reply(b, NOP_CONT, domain_cap, err);
+    reply_err = b->tx_vtbl.spawn_reply(b, NOP_CONT, err);
     if (err_is_fail(reply_err)) {
         DEBUG_ERR(err, "failed to send cleanup_reply");
     }
@@ -865,7 +851,6 @@ static struct spawn_rx_vtbl rx_vtbl = {
     .spawn_with_caps_request = spawn_with_caps_request_handler,
     .span_request            = span_request_handler,
     .kill_request            = kill_request_handler,
-    .exit_request            = exit_request_handler,
     .cleanup_request         = cleanup_request_handler,
 
     .use_local_memserv_call = use_local_memserv_handler,