Implemented support for "give_away" transfers in capops_copy.
authorMark Nevill <nevillm@ethz.ch>
Fri, 25 May 2012 11:27:52 +0000 (13:27 +0200)
committerSimon Gerber <simon.gerber@inf.ethz.ch>
Thu, 18 Jul 2013 12:58:33 +0000 (14:58 +0200)
13 files changed:
if/intermon.if
include/barrelfish_kpi/capabilities.h
kernel/arch/x86_64/syscall.c
kernel/include/syscall.h
kernel/syscall.c
usr/monitor/capops/copy.c
usr/monitor/capops/internal.h
usr/monitor/include/arch/x86_64/monitor_invocations_arch.h
usr/monitor/include/capops.h
usr/monitor/include/monitor_invocations.h
usr/monitor/include/send_cap.h
usr/monitor/invocations.c
usr/monitor/send_cap.c

index c38784a..3689079 100644 (file)
@@ -148,7 +148,7 @@ interface intermon "The Interface between monitors" {
 
     // cap operation messages
     message capops_request_copy(coreid dest, caprep cap, capop_st st);
-    message capops_recv_copy(caprep cap, capop_st st);
+    message capops_recv_copy(caprep cap, uint8 owner_relations, capop_st st);
     message capops_recv_copy_result(errval status, capaddr_t cap, capbits_t bits, capslot_t slot, capop_st st);
 
     message capops_move_request(caprep cap, uint8 relations, capop_st st);
index 505f8fa..93b4834 100644 (file)
@@ -127,6 +127,7 @@ enum kernel_cmd {
     KernelCmd_Identify_cap,       ///< Return the meta data of a capability
     KernelCmd_Identify_domains_cap, ///< Return the meta data of another domain's capability
     KernelCmd_Remote_relations,   ///< Set capability as being remote
+    KernelCmd_Cap_has_relations,      ///< Return presence of local relations
     KernelCmd_Create_cap,         ///< Create a new capability
     KernelCmd_Copy_existing,
     KernelCmd_Iden_cnode_get_cap, ///< Look up cnode, return cap within
index d0156b7..8dee994 100644 (file)
@@ -308,6 +308,16 @@ static struct sysret monitor_identify_domains_cap(struct capability *kernel_cap,
     return monitor_identify_cap_common(kernel_cap, root, &args[2]);
 }
 
+static struct sysret monitor_cap_has_relations(struct capability *kernel_cap,
+                                               int cmd, uintptr_t *args)
+{
+    capaddr_t caddr = args[0];
+    uint8_t vbits = args[1];
+    uint8_t mask = args[2];
+
+    return sys_cap_has_relations(caddr, vbits, mask);
+}
+
 static struct sysret monitor_remote_relations(struct capability *kernel_cap,
                                               int cmd, uintptr_t *args)
 {
@@ -766,7 +776,8 @@ static invocation_handler_t invocations[ObjType_Num][CAP_MAX_CMD] = {
         [KernelCmd_Get_arch_id]  = monitor_get_arch_id,
         [KernelCmd_Identify_cap] = monitor_identify_cap,
         [KernelCmd_Identify_domains_cap] = monitor_identify_domains_cap,
-        [KernelCmd_Remote_relations]   = monitor_remote_relations,
+        [KernelCmd_Remote_relations] = monitor_remote_relations,
+        [KernelCmd_Cap_has_relations] = monitor_cap_has_relations,
         [KernelCmd_Iden_cnode_get_cap] = monitor_iden_cnode_get_cap,
         [KernelCmd_Create_cap]   = monitor_create_cap,
         [KernelCmd_Copy_existing] = monitor_copy_existing,
index a33147b..944c2ca 100644 (file)
@@ -45,6 +45,7 @@ struct sysret sys_delete(struct capability *root, capaddr_t cptr, uint8_t bits);
 struct sysret sys_revoke(struct capability *root, capaddr_t cptr, uint8_t bits);
 struct sysret sys_get_state(struct capability *root, capaddr_t cptr, uint8_t bits);
 struct sysret sys_monitor_register(capaddr_t ep_caddr);
+struct sysret sys_cap_has_relations(capaddr_t caddr, uint8_t vbits, uint8_t mask);
 struct sysret sys_monitor_remote_relations(capaddr_t root_addr, uint8_t root_bits,
                                            capaddr_t cptr, uint8_t bits,
                                            uint8_t relations, uint8_t mask);
index ce0880e..885e762 100644 (file)
@@ -394,6 +394,34 @@ struct sysret sys_monitor_register(capaddr_t ep_caddr)
     return SYSRET(SYS_ERR_OK);
 }
 
+struct sysret sys_cap_has_relations(capaddr_t caddr, uint8_t vbits,
+                                    uint8_t mask)
+{
+    errval_t err;
+
+    caddr >>= (CPTR_BITS-vbits);
+
+    struct cte *cap;
+    err = caps_lookup_slot(&dcb_current->cspace.cap, caddr, vbits, &cap,
+                           CAPRIGHTS_READ);
+    if (err_is_fail(err)) {
+        return SYSRET(err);
+    }
+
+    uint8_t res = 0;
+    if (mask & RRELS_COPY_BIT && has_copies(cap)) {
+        res |= RRELS_COPY_BIT;
+    }
+    if (mask & RRELS_ANCS_BIT && has_ancestors(cap)) {
+        res |= RRELS_ANCS_BIT;
+    }
+    if (mask & RRELS_DESC_BIT && has_descendants(cap)) {
+        res |= RRELS_DESC_BIT;
+    }
+
+    return (struct sysret) { .error = SYS_ERR_OK, .value = res };
+}
+
 struct sysret sys_monitor_remote_relations(capaddr_t root_addr, uint8_t root_bits,
                                            capaddr_t cptr, uint8_t bits,
                                            uint8_t relations, uint8_t mask)
index 52f8065..6526a5c 100644 (file)
@@ -12,6 +12,7 @@
 #include <if/intermon_defs.h>
 #include "monitor.h"
 #include "capops.h"
+#include "caplock.h"
 #include "capsend.h"
 #include "magic.h"
 #include "internal.h"
@@ -25,8 +26,14 @@ struct cap_copy_rpc_st {
     genvaddr_t st;
     // sender if acting as intermediary
     coreid_t from;
+    // cap that is being copied out
+    struct capref cap;
     // result handler if being called directly
     copy_result_handler_t result_handler;
+    // whether the local cap should be deleted when the rpc is complete
+    bool delete_after;
+    // cap was last copy on request source (only relevant if delete_after is true)
+    bool is_last;
 };
 
 /*
@@ -103,6 +110,7 @@ struct owner_copy_msg_st {
     struct intermon_msg_queue_elem queue_elem;
     struct capref capref;
     intermon_caprep_t caprep;
+    uint8_t owner_relations;
     genvaddr_t st;
 };
 
@@ -115,7 +123,8 @@ owner_copy_send_cont(struct intermon_binding *b, struct intermon_msg_queue_elem
     assert(rpc_st);
     errval_t err, cleanup_err;
 
-    err = intermon_capops_recv_copy__tx(b, NOP_CONT, msg_st->caprep, msg_st->st);
+    err = intermon_capops_recv_copy__tx(b, NOP_CONT, msg_st->caprep,
+                                        msg_st->owner_relations, msg_st->st);
 
     if (rpc_st->from != my_core_id) {
         // need to cleanup cap allocated in request_copy__rx_handler
@@ -152,15 +161,12 @@ owner_copy_send_cont(struct intermon_binding *b, struct intermon_msg_queue_elem
 
 // enqueueing function for owner_copy
 static errval_t
-owner_copy(struct capref capref, struct capability *cap, coreid_t from, coreid_t dest, copy_result_handler_t result_handler, genvaddr_t st)
+owner_copy(struct capref capref, struct capability *cap, coreid_t from,
+           coreid_t dest, bool give_away, copy_result_handler_t result_handler,
+           genvaddr_t st)
 {
     errval_t err;
 
-    err = monitor_remote_relations(capref, RRELS_COPY_BIT, RRELS_COPY_BIT, NULL);
-    if (err_is_fail(err)) {
-        return err;
-    }
-
     // create new rpc state to associate return message
     struct cap_copy_rpc_st *rpc_st = malloc(sizeof(struct cap_copy_rpc_st));
     if (!rpc_st) {
@@ -168,8 +174,57 @@ owner_copy(struct capref capref, struct capability *cap, coreid_t from, coreid_t
     }
     rpc_st->st = st;
     rpc_st->from = from;
+    rpc_st->cap = capref;
+    rpc_st->delete_after = give_away;
+    rpc_st->is_last = false;
     rpc_st->result_handler = result_handler;
 
+    // check for special handling of giving away last copy
+    uint8_t remote_relations = RRELS_COPY_BIT;
+    if (rpc_st->delete_after) {
+        uint8_t relations = 0;
+        err = monitor_cap_has_relations(capref, RRELS_COPY_BIT, &relations);
+        if (err_is_fail(err)) {
+            DEBUG_ERR(err, "checking for local copies of give_away cap");
+        }
+        else {
+            rpc_st->is_last = !(relations & RRELS_COPY_BIT);
+        }
+    }
+    if (rpc_st->is_last) {
+        err = monitor_remote_relations(capref, 0, 0, &remote_relations);
+        if (err_is_fail(err)) {
+            DEBUG_ERR(err, "checking for remote copies of give_away cap");
+            rpc_st->is_last = false;
+            remote_relations = RRELS_COPY_BIT;
+        }
+        else {
+            rpc_st->is_last = !(remote_relations & RRELS_COPY_BIT);
+        }
+    }
+
+    // unless we're performing a "true" give_away, set the remote relations
+    // copy bit
+    if (!(rpc_st->delete_after && rpc_st->is_last)) {
+        err = monitor_remote_relations(capref, RRELS_COPY_BIT, RRELS_COPY_BIT, NULL);
+        if (err_is_fail(err)) {
+            free(rpc_st);
+            return err;
+        }
+    }
+
+    // if we're performing a "true" give_away, lock the cap locally as the
+    // intermediate state will be inconsistent
+    if (rpc_st->delete_after && rpc_st->is_last) {
+        err = monitor_lock_cap(cap_root, get_cap_addr(capref),
+                               get_cap_valid_bits(capref));
+        if (err_is_fail(err)) {
+            // callers of owner_copy should already check cap lock state
+            USER_PANIC_ERR(err, "locking cap for true give_away failed");
+        }
+        assert(!(remote_relations & RRELS_COPY_BIT));
+    }
+
     // create send state
     struct owner_copy_msg_st *msg_st = calloc(1, sizeof(struct owner_copy_msg_st));
     if (!msg_st) {
@@ -178,6 +233,7 @@ owner_copy(struct capref capref, struct capability *cap, coreid_t from, coreid_t
     }
     msg_st->queue_elem.cont = owner_copy_send_cont;
     capability_to_caprep(cap, &msg_st->caprep);
+    msg_st->owner_relations = remote_relations;
     msg_st->st = (genvaddr_t)rpc_st;
 
     // enqueue message
@@ -223,7 +279,8 @@ request_copy_send_cont(struct intermon_binding *b, struct intermon_msg_queue_ele
 
 // enqueueing function for request_copy
 static errval_t
-request_copy(struct capref capref, coreid_t dest, copy_result_handler_t result_handler, genvaddr_t st)
+request_copy(struct capref capref, coreid_t dest, bool give_away,
+             copy_result_handler_t result_handler, genvaddr_t st)
 {
     errval_t err;
     struct capability cap;
@@ -242,6 +299,8 @@ request_copy(struct capref capref, coreid_t dest, copy_result_handler_t result_h
     }
     rpc_st->st = st;
     rpc_st->from = my_core_id;
+    rpc_st->delete_after = give_away;
+    rpc_st->is_last = false;
     rpc_st->result_handler = result_handler;
 
     // create send state
@@ -271,18 +330,54 @@ request_copy(struct capref capref, coreid_t dest, copy_result_handler_t result_h
  */
 
 void
-recv_copy_result__rx_handler(struct intermon_binding *b, errval_t status, capaddr_t capaddr, uint8_t vbits, cslot_t slot, genvaddr_t st)
+recv_copy_result__rx_handler(struct intermon_binding *b, errval_t status,
+                             capaddr_t capaddr, uint8_t vbits, cslot_t slot,
+                             genvaddr_t st)
 {
     assert(st);
+    errval_t err;
+    struct intermon_state *inter_st = (struct intermon_state*)b->st;
     struct cap_copy_rpc_st *rpc_st = (struct cap_copy_rpc_st*)st;
 
     if (rpc_st->from != my_core_id) {
         // acting as intermediary, forward to origin
-        assert(!rpc_st->result_handler);
+        assert(!rpc_st->result_handler); // must not have a result handler
+        assert(rpc_st->delete_after); // also temp cap should be del'd
+        assert(!rpc_st->is_last); // and must have other local copies
+        err = cap_destroy(rpc_st->cap);
+        if (err_is_fail(err)) {
+            USER_PANIC_ERR(err, "destroying temp cap for f-to-f copy");
+        }
         recv_copy_result(rpc_st->from, status, capaddr, vbits, slot, rpc_st->st);
     }
     else {
-        // origin of copy, call result handler
+        // origin of copy
+        if (rpc_st->delete_after) {
+            if (rpc_st->is_last) {
+                // a give_away was performed, need to unlock and set new owner
+                if (err_is_ok(status)) {
+                    // only set new owner if give_away succeeded
+                    err = monitor_set_cap_owner(cap_root,
+                                                get_cap_addr(rpc_st->cap),
+                                                get_cap_valid_bits(rpc_st->cap),
+                                                inter_st->core_id);
+                    if (err_is_fail(err)) {
+                        USER_PANIC_ERR(err, "updating owner after true"
+                                       " give_away failed");
+                    }
+                }
+                caplock_unlock(get_cap_domref(rpc_st->cap));
+            }
+            if (err_is_ok(status)) {
+                // this should always succeed either because there are local
+                // copies or because the cap is now foreign
+                err = cap_destroy(rpc_st->cap);
+                if (err_is_fail(err)) {
+                    USER_PANIC_ERR(err, "cap_destroy after give_away failed");
+                }
+            }
+        }
+        // call result handler
         if (rpc_st->result_handler) {
             rpc_st->result_handler(status, capaddr, vbits, slot, (void*)rpc_st->st);
         }
@@ -291,7 +386,8 @@ recv_copy_result__rx_handler(struct intermon_binding *b, errval_t status, capadd
 }
 
 void
-recv_copy__rx_handler(struct intermon_binding *b, intermon_caprep_t caprep, genvaddr_t st)
+recv_copy__rx_handler(struct intermon_binding *b, intermon_caprep_t caprep,
+                      uint8_t owner_relations, genvaddr_t st)
 {
     errval_t err;
     struct intermon_state *inter_st = (struct intermon_state*)b->st;
@@ -310,8 +406,16 @@ recv_copy__rx_handler(struct intermon_binding *b, intermon_caprep_t caprep, genv
 
     coreid_t owner;
     if (distcap_needs_locality(cap.type)) {
-        // if cap needs locality, message source is owner
-        owner = from;
+        if (owner_relations & RRELS_COPY_BIT) {
+            // if cap needs locality and ownership is not being transferred,
+            // message source is owner
+            owner = from;
+        }
+        else {
+            // if there are no remote copies, ownership is being transferred to
+            // this core
+            owner = my_core_id;
+        }
     }
     else {
         // otherwise every core is owner
@@ -324,6 +428,12 @@ recv_copy__rx_handler(struct intermon_binding *b, intermon_caprep_t caprep, genv
         // may fail if given owner does not match owner of existing copies
         goto free_slot;
     }
+    if (owner == my_core_id && owner_relations) {
+        err = monitor_remote_relations(dest, owner_relations, ~(uint8_t)0, NULL);
+        if (err_is_fail(err)) {
+            USER_PANIC_ERR(err, "setting remote rels on copy recv with ownership");
+        }
+    }
 
     goto send_result;
 
@@ -336,7 +446,8 @@ send_result:
 }
 
 void
-request_copy__rx_handler(struct intermon_binding *b, coreid_t dest, intermon_caprep_t caprep, genvaddr_t st)
+request_copy__rx_handler(struct intermon_binding *b, coreid_t dest,
+                         intermon_caprep_t caprep, genvaddr_t st)
 {
     errval_t err, send_err;
     struct intermon_state *inter_st = (struct intermon_state*)b->st;
@@ -378,7 +489,8 @@ request_copy__rx_handler(struct intermon_binding *b, coreid_t dest, intermon_cap
 
     if (dest == my_core_id) {
         // tried to send copy to owning core, success!
-        recv_copy_result(from, SYS_ERR_OK, capref.cnode.address, capref.cnode.address_bits, capref.slot, st);
+        recv_copy_result(from, SYS_ERR_OK, get_cnode_addr(capref),
+                         get_cnode_valid_bits(capref), capref.slot, st);
     }
     else {
         // mark cap as having remote copies
@@ -388,7 +500,7 @@ request_copy__rx_handler(struct intermon_binding *b, coreid_t dest, intermon_cap
         }
 
         // forward copy to destination core
-        err = owner_copy(capref, &cap, from, dest, NULL, st);
+        err = owner_copy(capref, &cap, from, dest, true, NULL, st);
         if (err_is_fail(err)) {
             goto send_err;
         }
@@ -413,7 +525,8 @@ end:
  */
 
 errval_t
-capops_copy(struct capref capref, coreid_t dest, copy_result_handler_t result_handler, void *st)
+capops_copy(struct capref capref, coreid_t dest, bool give_away,
+            copy_result_handler_t result_handler, void *st)
 {
     errval_t err;
     struct capability cap;
@@ -437,16 +550,26 @@ capops_copy(struct capref capref, coreid_t dest, copy_result_handler_t result_ha
         }
         err = cap_copy(capref, res);
         if (err_is_fail(err)) {
+            errval_t err2 = slot_free(res);
+            if (err_is_fail(err2)) {
+                USER_PANIC_ERR(err2, "while freeing slot"
+                               " due to local copy failure");
+            }
             return err;
         }
+        if (give_away) {
+            err = cap_delete(capref);
+        }
 
-        result_handler(SYS_ERR_OK, res.cnode.address, res.cnode.address_bits, res.slot, st);
+        result_handler(err, get_cnode_addr(res),
+                       get_cnode_valid_bits(res), res.slot, st);
         return SYS_ERR_OK;
     }
 
     if (distcap_state_is_foreign(state)) {
         // sending copy from non-owner, send copy request to owner
-        return request_copy(capref, dest, result_handler, (genvaddr_t)st);
+        return request_copy(capref, dest, give_away, result_handler,
+                            (genvaddr_t)st);
     }
     else {
         // sending copy from owner
@@ -454,6 +577,7 @@ capops_copy(struct capref capref, coreid_t dest, copy_result_handler_t result_ha
         if (err_is_fail(err)) {
             return err;
         }
-        return owner_copy(capref, &cap, my_core_id, dest, result_handler, (genvaddr_t)st);
+        return owner_copy(capref, &cap, my_core_id, dest, give_away,
+                          result_handler, (genvaddr_t)st);
     }
 }
index 3095376..b066131 100644 (file)
@@ -14,7 +14,8 @@ void recv_copy_result__rx_handler(struct intermon_binding *b, errval_t status,
                                   capaddr_t capaddr, uint8_t vbits,
                                   cslot_t slot, genvaddr_t st);
 void recv_copy__rx_handler(struct intermon_binding *b,
-                           intermon_caprep_t caprep, genvaddr_t st);
+                           intermon_caprep_t caprep, uint8_t owner_relations,
+                           genvaddr_t st);
 void request_copy__rx_handler(struct intermon_binding *b, coreid_t dest,
                               intermon_caprep_t caprep, genvaddr_t st);
 void delete_remote__rx_handler(struct intermon_binding *b,
index a146f8d..f046074 100644 (file)
@@ -51,6 +51,19 @@ invoke_monitor_remote_relations(capaddr_t root_cap, int root_bits,
 }
 
 static inline errval_t
+invoke_monitor_cap_has_relations(capaddr_t caddr, uint8_t bits, uint8_t mask,
+                                 uint8_t *res)
+{
+    assert(res);
+    struct sysret ret = cap_invoke4(cap_kernel, KernelCmd_Cap_has_relations,
+                                    caddr, bits, mask);
+    if (err_is_ok(ret.error)) {
+        *res = ret.value;
+    }
+    return ret.error;
+}
+
+static inline errval_t
 invoke_monitor_identify_cap(capaddr_t cap, int bits, struct capability *out)
 {
     return cap_invoke4(cap_kernel, KernelCmd_Identify_cap, cap, bits,
index dbead09..9ae1ef7 100644 (file)
@@ -16,7 +16,7 @@
 #include "domcap.h"
 
 typedef void (*copy_result_handler_t)(errval_t, capaddr_t, uint8_t, cslot_t, void*);
-errval_t capops_copy(struct capref src, coreid_t dest,
+errval_t capops_copy(struct capref src, coreid_t dest, bool give_away,
                      copy_result_handler_t result_handler, void *st);
 
 typedef void (*move_result_handler_t)(errval_t, void*);
index c6dbe3b..628708f 100644 (file)
@@ -27,6 +27,8 @@ errval_t monitor_domcap_remote_relations(struct capref croot, capaddr_t cptr,
                                          mask, uint8_t *ret_relations);
 errval_t monitor_remote_relations(struct capref cap, uint8_t relations, uint8_t
                                   mask, uint8_t *ret_relations);
+errval_t monitor_cap_has_relations(struct capref cap, uint8_t mask,
+                                   uint8_t *res);
 errval_t monitor_cap_create(struct capref dest, struct capability *cap,
                             coreid_t owner);
 errval_t monitor_identify_cnode_get_cap(struct capability *cnode_raw, 
index 87e8ca7..f1b9f5b 100644 (file)
@@ -17,8 +17,6 @@ typedef void (*captx_send_cont)(errval_t, struct captx_prepare_state*,
                                 intermon_captx_t*, void*);
 struct captx_prepare_state {
     intermon_captx_t captx;
-    struct capref cap;
-    bool give_away;
     captx_send_cont send_cont;
     void *st;
 };
index 00f2cab..843e9e6 100644 (file)
@@ -91,6 +91,18 @@ errval_t monitor_remote_relations(struct capref cap, uint8_t relations,
 }
 
 /**
+ *
+ */
+
+errval_t monitor_cap_has_relations(struct capref cap, uint8_t mask,
+                                   uint8_t *res)
+{
+    capaddr_t caddr = get_cap_addr(cap);
+    uint8_t bits = get_cap_valid_bits(cap);
+    return invoke_monitor_cap_has_relations(caddr, bits, mask, res);
+}
+
+/**
  * \brief Invoke the kernel cap to set the type of #cap to null
  */
 errval_t monitor_nullify_cap(struct capref cap)
index 38c9e73..f102037 100644 (file)
 #include <capops.h>
 #include <barrelfish/debug.h>
 #include <monitor_invocations_arch.h>
-
-static void
-captx_prepare_finished_cont(errval_t status, void *st_)
-{
-    struct captx_prepare_state *st = (struct captx_prepare_state*)st_;
-    intermon_captx_t *tx = err_is_ok(status) ? &st->captx : NULL;
-    st->send_cont(status, st, tx, st->st);
-}
+#include <string.h>
 
 static void
 captx_prepare_copy_result_cont(errval_t status, capaddr_t cnaddr,
@@ -29,17 +22,9 @@ captx_prepare_copy_result_cont(errval_t status, capaddr_t cnaddr,
         st->captx.cnptr = cnaddr;
         st->captx.cnbits = cnbits;
         st->captx.slot = slot;
-        if (st->give_away) {
-            status = capops_delete(get_cap_domref(st->cap),
-                                   captx_prepare_finished_cont, st);
-            if (err_is_fail(status)) {
-                DEBUG_ERR(status, "new remote copy will leak");
-                st->send_cont(status, st, NULL, st->st);
-            }
-            return;
-        }
     }
-    captx_prepare_finished_cont(status, st);
+    intermon_captx_t *tx = err_is_ok(status) ? &st->captx : NULL;
+    st->send_cont(status, st, tx, st->st);
 }
 
 errval_t
@@ -49,11 +34,10 @@ captx_prepare_send(struct capref cap, coreid_t dest, bool give_away,
 {
     assert(state);
     assert(send_cont);
-    state->cap = cap;
-    state->give_away = give_away;
+    memset(state, 0, sizeof(*state));
     state->send_cont = send_cont;
     state->st = st;
-    return capops_copy(cap, dest, captx_prepare_copy_result_cont, state);
+    return capops_copy(cap, dest, give_away, captx_prepare_copy_result_cont, state);
 }
 
 static errval_t