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",
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);
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)
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,
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,
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 {
/// 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
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,
[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,
[CNodeCmd_Delete] = handle_delete,
[CNodeCmd_Revoke] = handle_revoke,
[CNodeCmd_GetState] = handle_get_state,
+ [CNodeCmd_Resize] = handle_resize,
},
[ObjType_L2CNode] = {
[CNodeCmd_Copy] = handle_copy,
[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,
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,
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;
}
/**
+ * \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
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);
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;
}
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;
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;
}
// 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;
}
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;
}
// 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);
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,
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
*
// 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);
}
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;
}
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;
}
/* 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 */
/* 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));