T266: Resizing root cnode enabled for libmm slot allocator
authorSimon Gerber <simon.gerber@inf.ethz.ch>
Wed, 27 Jul 2016 09:39:07 +0000 (11:39 +0200)
committerSimon Gerber <simon.gerber@inf.ethz.ch>
Wed, 27 Jul 2016 09:39:07 +0000 (11:39 +0200)
Signed-off-by: Simon Gerber <simon.gerber@inf.ethz.ch>

14 files changed:
errors/errno.fugu
include/barrelfish/capabilities.h
include/barrelfish/invocations.h
include/barrelfish/slot_alloc.h
include/barrelfish_kpi/capabilities.h
include/mm/slot_alloc.h
kernel/arch/x86_64/syscall.c
kernel/include/syscall.h
kernel/syscall.c
lib/barrelfish/capabilities.c
lib/barrelfish/slot_alloc/single_slot_alloc.c
lib/barrelfish/slot_alloc/slot_alloc.c
lib/mm/slot_alloc_2.c
usr/mem_serv/mem_serv.c

index 8909cb2..7a2d511 100755 (executable)
@@ -50,7 +50,8 @@ errors kernel SYS_ERR_ {
     failure CAP_NOT_FOUND       "Capability not found (empty slot encountered)",
     failure IDENTIFY_LOOKUP     "Error while looking up cap to identify",
     failure L1_CNODE_INDEX      "Index into L1 CNode too high",
-    failuRE CAP_LOOKUP_DEPTH    "Invalid capability lookup depth",
+    failure CAP_LOOKUP_DEPTH    "Invalid capability lookup depth",
+    failure RESIZE_NOT_L1       "Trying to resize non-L1 CNode",
 
     // Generic capability manipulation errors
     failure SLOT_IN_USE         "Destination capability slots occupied",
index c4fd39d..584378e 100644 (file)
@@ -41,6 +41,8 @@ errval_t cnode_create_from_mem(struct capref dest, struct capref src,
                                enum objtype cntype, struct cnoderef *cnoderef,
                                size_t slots);
 
+errval_t root_cnode_resize(struct capref new, struct capref ret);
+
 errval_t cap_retype(struct capref dest_start, struct capref src, gensize_t offset,
                     enum objtype new_type, gensize_t objsize, size_t count);
 errval_t cap_create(struct capref dest, enum objtype type, size_t bytes);
index df8dc4e..00311a6 100644 (file)
@@ -156,6 +156,12 @@ static inline errval_t invoke_cnode_get_state(struct capref root, capaddr_t cap,
     return sysret.error;
 }
 
+static inline errval_t invoke_cnode_resize(struct capref root, capaddr_t new_cptr,
+                                           capaddr_t retcn_ptr, cslot_t retslot)
+{
+    return cap_invoke4(root, CNodeCmd_Resize, new_cptr, retcn_ptr, retslot).error;
+}
+
 static inline errval_t invoke_vnode_unmap(struct capref cap,
                                           capaddr_t mapping_addr,
                                           enum cnode_type level)
index dbc23b8..e7c0248 100644 (file)
@@ -84,6 +84,10 @@ errval_t single_slot_alloc_init_raw(struct single_slot_allocator *ret,
                                     struct capref cap, struct cnoderef cnode,
                                     cslot_t nslots, void *buf, size_t buflen);
 
+cslot_t single_slot_alloc_freecount(struct single_slot_allocator *s);
+errval_t single_slot_alloc_resize(struct single_slot_allocator *this,
+                                  cslot_t newslotcount);
+
 errval_t multi_slot_alloc_init(struct multi_slot_allocator *ret,
                                cslot_t nslots, cslot_t *retslots);
 errval_t multi_slot_alloc_init_raw(struct multi_slot_allocator *ret,
@@ -105,6 +109,7 @@ errval_t slot_alloc_init_2(void);
 struct slot_allocator *get_default_slot_allocator(void);
 errval_t slot_alloc(struct capref *ret);
 errval_t slot_alloc_root(struct capref *ret);
+errval_t rootsa_update(cslot_t newslotcount);
 errval_t slot_free(struct capref ret);
 
 errval_t range_slot_alloc(struct range_slot_allocator *alloc, cslot_t nslots,
index c6c22c4..5f2dbdb 100644 (file)
@@ -368,6 +368,7 @@ enum cnode_cmd {
     CNodeCmd_Revoke,    ///< Revoke capability
     CNodeCmd_Create,    ///< Create capability
     CNodeCmd_GetState,  ///< Get distcap state for capability
+    CNodeCmd_Resize,    ///< Resize CNode, only applicable for L1 Cnode
 };
 
 enum vnode_cmd {
index efe4c17..5663d18 100644 (file)
@@ -72,6 +72,9 @@ struct slot_prealloc_2 {
 
     /// RAM allocator to allocate space for new cnodes
     struct mm *mm;
+
+    /// Current number of slots in root cnode
+    cslot_t rootcn_slots;
 };
 
 /// Initialiser for the pre-allocating implementation
index 7072d40..377eb52 100644 (file)
@@ -222,6 +222,14 @@ static struct sysret handle_get_state(struct capability *root,
     return sys_get_state(root, cptr, level);
 }
 
+static struct sysret handle_resize(struct capability *root,
+                                   int cmd, uintptr_t *args)
+{
+    capaddr_t newroot_ptr = args[0];
+    capaddr_t retcn_ptr   = args[1];
+    cslot_t   retslot     = args[2];
+    return sys_resize_l1cnode(root, newroot_ptr, retcn_ptr, retslot);
+}
 
 #if 1
 static struct sysret handle_cnode_cmd_obsolete(struct capability *root,
@@ -1148,6 +1156,8 @@ static invocation_handler_t invocations[ObjType_Num][CAP_MAX_CMD] = {
         [CNodeCmd_Delete] = handle_cnode_cmd_obsolete,
         [CNodeCmd_Revoke] = handle_cnode_cmd_obsolete,
         [CNodeCmd_GetState] = handle_cnode_cmd_obsolete,
+        [CNodeCmd_Resize] = handle_cnode_cmd_obsolete,
+
     },
     [ObjType_L1CNode] = {
         [CNodeCmd_Copy]   = handle_copy,
@@ -1157,6 +1167,7 @@ static invocation_handler_t invocations[ObjType_Num][CAP_MAX_CMD] = {
         [CNodeCmd_Delete] = handle_delete,
         [CNodeCmd_Revoke] = handle_revoke,
         [CNodeCmd_GetState] = handle_get_state,
+        [CNodeCmd_Resize] = handle_resize,
     },
     [ObjType_L2CNode] = {
         [CNodeCmd_Copy]   = handle_copy,
@@ -1166,6 +1177,7 @@ static invocation_handler_t invocations[ObjType_Num][CAP_MAX_CMD] = {
         [CNodeCmd_Delete] = handle_delete,
         [CNodeCmd_Revoke] = handle_revoke,
         [CNodeCmd_GetState] = handle_get_state,
+        [CNodeCmd_Resize] = handle_resize,
     },
     [ObjType_VNode_x86_64_pml4] = {
         [VNodeCmd_Identify] = handle_vnode_identify,
index a751c56..1ff8d03 100644 (file)
@@ -54,6 +54,8 @@ sys_copy_or_mint(struct capability *root, capaddr_t dest_cspace_cptr,
 struct sysret sys_delete(struct capability *root, capaddr_t cptr, uint8_t level);
 struct sysret sys_revoke(struct capability *root, capaddr_t cptr, uint8_t level);
 struct sysret sys_get_state(struct capability *root, capaddr_t cptr, uint8_t level);
+struct sysret sys_resize_l1cnode(struct capability *root, capaddr_t newroot_cptr,
+                                 capaddr_t retcn_cptr, cslot_t retslot);
 struct sysret
 sys_dispatcher_setup_guest (struct capability *to,
                             capaddr_t epp, capaddr_t vnodep,
index c8f7dc3..fff6b58 100644 (file)
@@ -518,6 +518,98 @@ struct sysret sys_get_state(struct capability *root, capaddr_t cptr, uint8_t lev
     return (struct sysret) { .error = SYS_ERR_OK, .value = state };
 }
 
+struct sysret sys_resize_l1cnode(struct capability *root, capaddr_t newroot_cptr,
+                                 capaddr_t retcn_cptr, cslot_t retslot)
+{
+    errval_t err;
+
+    if (root->type != ObjType_L1CNode) {
+        return SYSRET(SYS_ERR_RESIZE_NOT_L1);
+    }
+    assert(root->type == ObjType_L1CNode);
+
+    // Lookup new L1 CNode cap
+    struct cte *newroot;
+    err = caps_lookup_slot_2(root, newroot_cptr, 2, &newroot, CAPRIGHTS_ALLRIGHTS);
+    if (err_is_fail(err)) {
+        return SYSRET(err);
+    }
+    if (newroot->cap.type != ObjType_L1CNode) {
+        return SYSRET(SYS_ERR_INVALID_SOURCE_TYPE);
+    }
+    // TODO: check size of new CNode
+
+    // Lookup slot for returning RAM of old CNode
+    struct capability *retcn;
+    err = caps_lookup_cap_2(root, retcn_cptr, 1, &retcn, CAPRIGHTS_READ_WRITE);
+    if (err_is_fail(err)) {
+        return SYSRET(err);
+    }
+    struct cte *ret = caps_locate_slot(get_address(retcn), retslot);
+    if (ret->cap.type != ObjType_Null) {
+        return SYSRET(SYS_ERR_SLOT_IN_USE);
+    }
+
+    printk(LOG_NOTE, "%s: copying caps to new root cnode\n", __FUNCTION__);
+    // Copy over caps from old root cnode to new root cnode
+    cslot_t root_slots = cnode_get_slots(root);
+    cslot_t newroot_slots = cnode_get_slots(&newroot->cap);
+    for (cslot_t i = 0; i < min(root_slots, newroot_slots); i++) {
+        struct cte *src = caps_locate_slot(get_address(root), i);
+        if (src->cap.type == ObjType_Null) {
+            // skip empty slots in old root cnode
+            continue;
+        }
+        struct cte *dest = caps_locate_slot(get_address(&newroot->cap), i);
+        if (dest->cap.type != ObjType_Null) {
+            // fail if slot in destination cnode occupied
+            return SYSRET(SYS_ERR_SLOT_IN_USE);
+        }
+        // do proper cap copy
+        err = caps_copy_to_cte(dest, src, false, 0, 0);
+        if (err_is_fail(err)) {
+            return SYSRET(err);
+        }
+    }
+
+    printk(LOG_NOTE, "%s: copying old root cnode to return slot (retcn %"PRIxCADDR", retslot %d)\n", __FUNCTION__, retcn_cptr, retslot);
+    // Copy old root cnode into ret slot, this way we can delete the copies
+    // in the task cnode and the dispatcher that we need to update.
+    err = caps_copy_to_cte(ret, cte_for_cap(root), false, 0, 0);
+    if (err_is_fail(err)) {
+        return SYSRET(err);
+    }
+
+    printk(LOG_NOTE, "%s: setting new root cnode in dispatcher\n", __FUNCTION__);
+    // Set new root cnode in dispatcher
+    err = caps_delete(&dcb_current->cspace);
+    if (err_is_fail(err)) {
+        return SYSRET(err);
+    }
+    err = caps_copy_to_cte(&dcb_current->cspace, newroot, false, 0, 0);
+    if (err_is_fail(err)) {
+        return SYSRET(err);
+    }
+
+    printk(LOG_NOTE, "%s: setting new root cnode in taskcn\n", __FUNCTION__);
+    // Set new root cnode in task cnode
+    struct cte *taskcn = caps_locate_slot(get_address(&newroot->cap),
+                                          ROOTCN_SLOT_TASKCN);
+    struct cte *rootcn_cap = caps_locate_slot(get_address(&taskcn->cap),
+                                              TASKCN_SLOT_ROOTCN);
+    assert(rootcn_cap == cte_for_cap(root));
+    err = caps_delete(rootcn_cap);
+    if (err_is_fail(err)) {
+        return SYSRET(err);
+    }
+    err = caps_copy_to_cte(rootcn_cap, newroot, false, 0, 0);
+    if (err_is_fail(err)) {
+        return SYSRET(err);
+    }
+
+    return SYSRET(SYS_ERR_OK);
+}
+
 struct sysret sys_yield(capaddr_t target)
 {
     dispatcher_handle_t handle = dcb_current->disp;
index 920e1f0..ebf9c60 100644 (file)
@@ -419,6 +419,25 @@ errval_t cap_destroy(struct capref cap)
 }
 
 /**
+ * \brief Replace own L1 CNode
+ *
+ * \param new the replacement L1 CNode
+ * \param ret the slot to put the old L1 CNode
+ */
+errval_t root_cnode_resize(struct capref new, struct capref ret)
+{
+    assert(get_croot_addr(new) == CPTR_ROOTCN);
+    assert(get_cap_level(new) == CNODE_TYPE_COUNT);
+    capaddr_t new_cptr = get_cap_addr(new);
+
+    assert(get_croot_addr(ret) == CPTR_ROOTCN);
+    assert(get_cap_level(ret) == CNODE_TYPE_COUNT);
+    capaddr_t retcn_ptr= get_cnode_addr(ret);
+
+    return invoke_cnode_resize(cap_root, new_cptr, retcn_ptr, ret.slot);
+}
+
+/**
  * \brief Create a CNode from a given RAM capability in a specific slot
  *
  * \param dest location in which to place newly-created CNode cap
index 7ec026e..ef2d6cb 100644 (file)
@@ -55,7 +55,8 @@ static errval_t salloc(struct slot_allocator *ca, struct capref *ret)
     return SYS_ERR_OK;
 }
 
-static errval_t free_slot(struct single_slot_allocator *sca, cslot_t slot, struct thread_mutex *mutex)
+static errval_t free_slots(struct single_slot_allocator *sca, cslot_t slot,
+                           cslot_t count, struct thread_mutex *mutex)
 {
     thread_mutex_lock(mutex);
 
@@ -68,15 +69,15 @@ static errval_t free_slot(struct single_slot_allocator *sca, cslot_t slot, struc
     if (!sca->head) {
         sca->head = slab_alloc(&sca->slab);
         sca->head->slot = slot;
-        sca->head->space = 1;
+        sca->head->space = count;
         sca->head->next = NULL;
         goto finish;
     }
 
     // Freeing one before head
-    if (slot + 1 == sca->head->slot) {
+    if (slot + count == sca->head->slot) {
         sca->head->slot = slot;
-        sca->head->space++;
+        sca->head->space += count;
         goto finish;
     }
 
@@ -84,7 +85,7 @@ static errval_t free_slot(struct single_slot_allocator *sca, cslot_t slot, struc
     if (slot < sca->head->slot) {
         struct cnode_meta *new = slab_alloc(&sca->slab);
         new->slot  = slot;
-        new->space = 1;
+        new->space = count;
         new->next  = sca->head;
         sca->head  = new;
         goto finish;
@@ -93,7 +94,7 @@ static errval_t free_slot(struct single_slot_allocator *sca, cslot_t slot, struc
     while (walk != NULL) {
         // Freeing at the edge of walk
         if (slot == walk->slot + walk->space) {
-            walk->space++;
+            walk->space += count;
 
             // check if we can merge walk to next
             struct cnode_meta *next = walk->next;
@@ -111,9 +112,9 @@ static errval_t free_slot(struct single_slot_allocator *sca, cslot_t slot, struc
         }
 
         // Freing just before walk->next
-        if (walk->next && slot + 1 == walk->next->slot) {
+        if (walk->next && slot + count == walk->next->slot) {
             walk->next->slot = slot;
-            walk->next->space++;
+            walk->next->space += count;
             goto finish;
         }
 
@@ -122,7 +123,7 @@ static errval_t free_slot(struct single_slot_allocator *sca, cslot_t slot, struc
             struct cnode_meta *new = walk->next;
             walk->next = slab_alloc(&sca->slab);
             walk->next->slot = slot;
-            walk->next->space = 1;
+            walk->next->space = count;
             walk->next->next = new;
             goto finish;
         }
@@ -133,11 +134,11 @@ static errval_t free_slot(struct single_slot_allocator *sca, cslot_t slot, struc
     // Freeing after the list
     prev->next = slab_alloc(&sca->slab);
     prev->next->slot = slot;
-    prev->next->space = 1;
+    prev->next->space = count;
     prev->next->next = NULL;
 
  finish:
-    sca->a.space++;
+    sca->a.space += count;
 
  unlock:
     thread_mutex_unlock(mutex);
@@ -151,7 +152,43 @@ static errval_t sfree(struct slot_allocator *ca, struct capref cap)
         return LIB_ERR_SLOT_ALLOC_WRONG_CNODE;
     }
 
-    return free_slot(sca, cap.slot, &ca->mutex);
+    return free_slots(sca, cap.slot, 1, &ca->mutex);
+}
+
+cslot_t single_slot_alloc_freecount(struct single_slot_allocator *this)
+{
+    cslot_t free = 0;
+    for (struct cnode_meta *walk = this->head; walk; walk = walk->next) {
+        free += walk->space;
+    }
+    return free;
+}
+
+errval_t single_slot_alloc_resize(struct single_slot_allocator *this,
+                                  cslot_t newslotcount)
+{
+    errval_t err;
+
+    cslot_t grow = newslotcount - this->a.nslots;
+
+    // Refill slab allocator
+    size_t bufgrow = SINGLE_SLOT_ALLOC_BUFLEN(grow);
+    void *buf = malloc(bufgrow);
+    if (!buf) {
+        return LIB_ERR_MALLOC_FAIL;
+    }
+    slab_grow(&this->slab, buf, bufgrow);
+
+    // Update free slot metadata
+    err = free_slots(this, this->a.nslots, grow, &this->a.mutex);
+    if (err_is_fail(err)) {
+        return err;
+    }
+
+    // Update generic metadata
+    this->a.nslots = newslotcount;
+
+    return SYS_ERR_OK;
 }
 
 errval_t single_slot_alloc_init_raw(struct single_slot_allocator *ret,
index 2e5e240..31949a3 100644 (file)
@@ -59,6 +59,13 @@ errval_t slot_alloc_root(struct capref *ret)
     return ca->alloc(ca, ret);
 }
 
+errval_t rootsa_update(cslot_t newslotcount)
+{
+    struct slot_alloc_state *state = get_slot_alloc_state();
+    struct single_slot_allocator *sca = &state->rootca;
+    return single_slot_alloc_resize(sca, newslotcount);
+}
+
 /**
  * \brief Default slot free
  *
index 9d60b2c..ba878e4 100644 (file)
@@ -37,6 +37,52 @@ errval_t slot_prealloc_refill_2(struct slot_prealloc_2 *this)
     // Retype to and build the next cnode
     struct capref cnode_cap;
     err = slot_alloc_root(&cnode_cap);
+    if (err_no(err) == LIB_ERR_SLOT_ALLOC_NO_SPACE) {
+        // Root cnode full, resize it
+        uint8_t rootbits = log2ceil(this->rootcn_slots);
+        assert((1UL << rootbits) == this->rootcn_slots);
+        // Double size
+        struct capref root_ram, newroot_cap;
+        err = mm_alloc(this->mm, rootbits + 1 + OBJBITS_CTE, &root_ram, NULL);
+        if (err_is_fail(err)) {
+            return err_push(err, MM_ERR_SLOT_MM_ALLOC);
+        }
+        err = slot_alloc(&newroot_cap);
+        if (err_is_fail(err)) {
+            return err_push(err, LIB_ERR_SLOT_ALLOC);
+        }
+        err = cnode_create_from_mem(newroot_cap, root_ram, ObjType_L1CNode,
+                NULL, this->rootcn_slots * 2);
+        if (err_is_fail(err)) {
+            return err_push(err, LIB_ERR_CNODE_CREATE_FROM_MEM);
+        }
+        // Delete RAM cap of new CNode
+        err = cap_delete(root_ram);
+        if (err_is_fail(err)) {
+            return err_push(err, LIB_ERR_CAP_DELETE);
+        }
+
+        // Resize rootcn
+        debug_printf("retslot: %"PRIxCADDR"\n", get_cap_addr(root_ram));
+        err = root_cnode_resize(newroot_cap, root_ram);
+        if (err_is_fail(err)) {
+            DEBUG_ERR(err, "resizing root cnode");
+            return err;
+        }
+
+        // Delete old Root CNode and free slot
+        err = cap_destroy(root_ram);
+        if (err_is_fail(err)) {
+            DEBUG_ERR(err, "deleting old root cnode");
+            return err_push(err, LIB_ERR_CAP_DESTROY);
+        }
+
+        // update root slot allocator size and our metadata
+        rootsa_update(this->rootcn_slots *= 2);
+
+        // retry slot_alloc_root
+        err = slot_alloc_root(&cnode_cap);
+    }
     if (err_is_fail(err)) {
         return err_push(err, LIB_ERR_SLOT_ALLOC);
     }
@@ -93,9 +139,8 @@ errval_t slot_prealloc_init_2(struct slot_prealloc_2 *this, uint8_t maxslotbits,
     this->maxslotbits = maxslotbits;
     this->mm = ram_mm;
 
-    //assert(initial_space == (1UL << L2_CNODE_SLOTS));
-    if (initial_space != L2_CNODE_SLOTS &&
-        initial_space != DEFAULT_CNODE_SLOTS) {
+    assert(initial_space == L2_CNODE_SLOTS);
+    if (initial_space != L2_CNODE_SLOTS) {
         debug_printf("Initial CNode for 2 level preallocating slot allocator needs to be 16kB");
         return LIB_ERR_SLOT_ALLOC_INIT;
     }
@@ -105,5 +150,9 @@ errval_t slot_prealloc_init_2(struct slot_prealloc_2 *this, uint8_t maxslotbits,
     this->meta[0].free      = initial_space;
     this->meta[1].free      = 0;
 
+    // XXX: Do something like
+    // this->rootcn_slots = invoke_cnode_get_slots(cap_root);
+    this->rootcn_slots = L2_CNODE_SLOTS;
+
     return SYS_ERR_OK;
 }
index 166eb60..61411dd 100644 (file)
@@ -254,6 +254,9 @@ static void mem_allocate_handler(struct mem_binding *b, uint8_t bits,
 
     /* refill slot allocator if needed */
     err = slot_prealloc_refill_2(mm_ram.slot_alloc_inst);
+    if (err_is_fail(err)) {
+        DEBUG_ERR(err, "slot_prealloc_refill in mem_allocate_handler");
+    }
     assert(err_is_ok(err));
 
     /* refill slab allocator if needed */
@@ -399,7 +402,7 @@ initialize_ram_alloc(void)
 
     /* init slot allocator */
     err = slot_prealloc_init_2(&ram_slot_alloc, MAXCHILDBITS,
-                               cnode_start_cap, DEFAULT_CNODE_SLOTS,
+                               cnode_start_cap, L2_CNODE_SLOTS,
                                &mm_ram);
     assert(err_is_ok(err));