Fixed leaking of allocated stuff in paging (but not partially completed mapping)

This commit is contained in:
Sparchatus 2022-03-22 13:58:13 +00:00
parent 347f3a1155
commit 07a861ea61
2 changed files with 122 additions and 114 deletions

View File

@ -40,8 +40,6 @@ typedef int paging_flags_t;
// NOTE rueegges: page table metadata used to create child mappings and unmap
struct pt_t {
// the level of the page table. since we abuse this struct also to track l3 entries we set it to 4 there.
size_t level;
// capref that holds this page table
struct capref cap_pt;
// capref that maps this page table in the higher level page table
@ -67,7 +65,9 @@ struct paging_state {
// NOTE rueegges: remember if we are refilling so we can skip the checks
uint8_t refilling;
// TODO rueegges: use to track an allocated slot in failure conditions
// NOTE rueegges: use to track an already allocated page table in failure conditions
struct capref free_l1_vnode;
struct capref free_l2_vnode;
struct capref free_l3_vnode;
// TODO rueegges: implement more precisely?

View File

@ -29,7 +29,6 @@ static struct paging_state current;
#define PT_PT_SLAB_INITIAL_SPACE SLAB_STATIC_SIZE(PT_PT_SLAB_MIN_SPACE, sizeof(struct pt_t))
#define PT_CHILDREN_SLAB_INITIAL_SPACE SLAB_STATIC_SIZE(PT_CHILDREN_SLAB_MIN_SPACE, BASE_PAGE_SIZE)
// NOTE rueegges: temporary (?) solution for initial slab space
char pt_pt_slab_buf[PT_PT_SLAB_INITIAL_SPACE];
char pt_children_slab_buf[PT_CHILDREN_SLAB_INITIAL_SPACE];
@ -102,20 +101,20 @@ void pt_print_state(struct paging_state *st) {
// - this slab
// - child slab
// - mm slab
static errval_t pt_ensure_slots_and_slabs(struct paging_state *st) {
static errval_t pt_ensure_slabs(struct paging_state *st) {
errval_t err;
// ensure there is enough space to refill the page tables at any time
if(slab_freecount(&st->pt_slabs) <= PT_PT_SLAB_MIN_SPACE && !st->refilling) {
// ASSESSMENT M1: show refilling
// debug_printf("DEBUG rueegges: pt_ensure_slots_and_slabs - refilling pt_slab\n");
// debug_printf("DEBUG rueegges: pt_ensure_slabs - refilling pt_slab\n");
st->refilling = 1;
err = slab_default_refill(&st->pt_slabs);
st->refilling = 0;
// debug_printf("DEBUG rueegges: pt_ensure_slots_and_slabs - refilling pt_slab DONE\n");
// debug_printf("DEBUG rueegges: pt_ensure_slabs - refilling pt_slab DONE\n");
if(err_is_fail(err)) {
return err_push(err, LIB_ERR_SLAB_REFILL);
@ -124,7 +123,7 @@ static errval_t pt_ensure_slots_and_slabs(struct paging_state *st) {
if(slab_freecount(&st->pt_children_slabs) <= PT_CHILDREN_SLAB_MIN_SPACE && !st->refilling) {
// ASSESSMENT M1: show refilling
// debug_printf("DEBUG rueegges: pt_ensure_slots_and_slabs - refilling pt_children_slabs\n");
// debug_printf("DEBUG rueegges: pt_ensure_slabs - refilling pt_children_slabs\n");
st->refilling = 1;
@ -137,7 +136,7 @@ static errval_t pt_ensure_slots_and_slabs(struct paging_state *st) {
err = slab_refill_no_pagefault(&st->pt_children_slabs, frame_cap, 10 * 4096);
st->refilling = 0;
// debug_printf("DEBUG rueegges: pt_ensure_slots_and_slabs - refilling pt_children_slabs DONE\n");
// debug_printf("DEBUG rueegges: pt_ensure_slabs - refilling pt_children_slabs DONE\n");
if(err_is_fail(err)) {
return err_push(err, LIB_ERR_SLAB_REFILL);
@ -147,53 +146,83 @@ static errval_t pt_ensure_slots_and_slabs(struct paging_state *st) {
return SYS_ERR_OK;
}
// NOTE rueegges: ensures that a page table exists at the specified index in the parent page table and return it
static errval_t pt_ensure(struct paging_state *st, struct pt_t *pt_parent, size_t pt_index, struct pt_t **pt_ret){
// debug_printf("DEBUG rueegges: pt_ensure l%u\n", pt_parent->level+1);
static errval_t pt_alloc_level(struct paging_state *st, struct capref *pt_cap, uint8_t level) {
errval_t err;
// If this fails we got an l3 pt as the parent
assert(pt_parent->children != NULL);
assert(pt_parent->level < 3);
// MUST DO before check if table already exists since it might create it
// make sure slot and slab refilling is performed in time
err = pt_ensure_slots_and_slabs(st);
if(err_is_fail(err)) {
DEBUG_ERR(err, "Could not restock slots and slabs");
return err;
}
// check if the page table already exists
struct pt_t *res = (pt_parent->children)[pt_index];
if (res != NULL) {
*pt_ret = res;
return SYS_ERR_OK;
}
// create the page table
struct capref pt_cap;
switch(pt_parent->level){
case 0:
err = pt_alloc_l1(st, &pt_cap);
break;
switch(level){
case 1:
err = pt_alloc_l2(st, &pt_cap);
if(!capref_is_null(st->free_l1_vnode)){
*pt_cap = st->free_l1_vnode;
st->free_l1_vnode = NULL_CAP;
err = SYS_ERR_OK;
} else {
err = pt_alloc_l1(st, pt_cap);
}
break;
case 2:
err = pt_alloc_l3(st, &pt_cap);
if(!capref_is_null(st->free_l2_vnode)){
*pt_cap = st->free_l2_vnode;
st->free_l2_vnode = NULL_CAP;
err = SYS_ERR_OK;
} else {
err = pt_alloc_l2(st, pt_cap);
}
break;
case 3:
if(!capref_is_null(st->free_l3_vnode)){
*pt_cap = st->free_l3_vnode;
st->free_l3_vnode = NULL_CAP;
err = SYS_ERR_OK;
} else {
err = pt_alloc_l3(st, pt_cap);
}
break;
default:
err = ERR_INVALID_ARGS;
break;
}
if (err_is_fail(err)) {
DEBUG_ERR(err, "Failed pt_alloc l%u", pt_parent->level + 1);
return err;
}
static void pt_save_allocation(struct paging_state *st, struct capref cap, uint8_t level) {
switch(level){
case 1:
assert(capref_is_null(st->free_l1_vnode));
st->free_l1_vnode = cap;
break;
case 2:
assert(capref_is_null(st->free_l2_vnode));
st->free_l2_vnode = cap;
break;
case 3:
assert(capref_is_null(st->free_l3_vnode));
st->free_l3_vnode = cap;
break;
default:
assert(false);
break;
}
}
// NOTE rueegges: ensures that a page table exists at the specified index in the parent page table and return it
static errval_t pt_ensure(struct paging_state *st, struct pt_t *pt_parent, size_t pt_index, uint8_t level, struct pt_t **pt_ret){
errval_t err;
struct pt_t *res;
// debug_printf("DEBUG rueegges: pt_ensure l%u\n", pt_parent->level+1);
// If this fails we got an l3 pt as the parent
assert(pt_parent->children != NULL);
assert(level > 0 && level <= 3);
// MUST DO before check if table already exists since it might create it
// make sure slot and slab refilling is performed in time
err = pt_ensure_slabs(st);
if(err_is_fail(err)) {
return err;
}
// TODO rueegges: store the allocated pt
// check if the page table already exists
res = (pt_parent->children)[pt_index];
if (res != NULL) {
@ -201,77 +230,61 @@ static errval_t pt_ensure(struct paging_state *st, struct pt_t *pt_parent, size_
return SYS_ERR_OK;
}
// debug_printf("DEBUG rueegges: allocated pt l%u\n", pt_parent->level+1);
// create new mapping
struct capref pt_mapping;
err = st->slot_alloc->alloc(st->slot_alloc, &pt_mapping);
if (err_is_fail(err))
{
// NOTE rueegges: cannot free the pt allocated above since we do not have the ram cap
DEBUG_ERR(err, "Failed slot alloc for new pt mapping");
// create the page table
struct capref pt_cap;
err = pt_alloc_level(st, &pt_cap, level);
if (err_is_fail(err)) {
DEBUG_ERR(err, "Failed pt_alloc l%u", level);
return err;
}
err = vnode_map(pt_parent->cap_pt, pt_cap, pt_index, 0, 0, 1, pt_mapping);
if (err_is_fail(err))
{
// free the already allocated capref
errval_t err_err = st->slot_alloc->free(st->slot_alloc, pt_mapping);
if (err_is_fail(err_err)) {
DEBUG_ERR(err_err, "Failed to free capability slot during error handling");
// check if the page table already exists
res = (pt_parent->children)[pt_index];
if (res != NULL) {
*pt_ret = res;
return SYS_ERR_OK;
}
// NOTE rueegges: cannot free the pt allocated above since we do not have the ram cap
DEBUG_ERR(err, "Failed vnode_map for pt l%u", pt_parent->level + 1);
return err_push(err, LIB_ERR_VNODE_MAP);
}
// debug_printf("DEBUG rueegges: inserted mapping for pt l%u\n", pt_parent->level+1);
// allocate shadow page table space
struct pt_t *pt_meta = (struct pt_t *) slab_alloc(&st->pt_slabs);
if(pt_meta == NULL) {
err = vnode_unmap(pt_cap, pt_mapping);
if (err_is_fail(err)) {
DEBUG_ERR(err, "Failed to unmap pt during error handling");
}
err = st->slot_alloc->free(st->slot_alloc, pt_mapping);
if (err_is_fail(err)) {
DEBUG_ERR(err, "Failed to free capability slot during error handling");
}
// NOTE rueegges: cannot free the pt allocated above since we do not have the ram cap
pt_save_allocation(st, pt_cap, level);
return LIB_ERR_SLAB_ALLOC_FAIL;
}
struct pt_t **pt_children = (struct pt_t **) slab_alloc(&st->pt_children_slabs);
if(pt_children == NULL) {
err = vnode_unmap(pt_cap, pt_mapping);
if (err_is_fail(err)) {
DEBUG_ERR(err, "Failed to unmap pt during error handling");
slab_free(&st->pt_slabs, pt_meta);
pt_save_allocation(st, pt_cap, level);
return LIB_ERR_SLAB_ALLOC_FAIL;
}
err = st->slot_alloc->free(st->slot_alloc, pt_mapping);
// create new mapping
struct capref pt_mapping;
err = st->slot_alloc->alloc(st->slot_alloc, &pt_mapping);
if (err_is_fail(err)) {
DEBUG_ERR(err, "Failed to free capability slot during error handling");
slab_free(&st->pt_slabs, pt_meta);
slab_free(&st->pt_children_slabs, pt_children);
pt_save_allocation(st, pt_cap, level);
return err_push(err, LIB_ERR_SLOT_ALLOC);
}
err = vnode_map(pt_parent->cap_pt, pt_cap, pt_index, 0, 0, 1, pt_mapping);
if (err_is_fail(err)) {
errval_t err_err = st->slot_alloc->free(st->slot_alloc, pt_mapping);
if (err_is_fail(err_err)) {
DEBUG_ERR(err_err, "Failed to free capability slot during error handling");
}
slab_free(&st->pt_slabs, pt_meta);
// NOTE rueegges: cannot free the pt allocated above since we do not have the ram cap
return LIB_ERR_SLAB_ALLOC_FAIL;
slab_free(&st->pt_children_slabs, pt_children);
pt_save_allocation(st, pt_cap, level);
DEBUG_ERR(err, "Failed vnode_map for pt l%u", level);
return err_push(err, LIB_ERR_VNODE_MAP);
}
// save metadata
// make sure all child pointers are initialized to NULL
memset(pt_children, 0, st->pt_children_slabs.blocksize);
// debug_printf("DEBUG rueegges: completed shadow allocation for pt l%u\n", pt_parent->level+1);
pt_meta->level = pt_parent->level + 1;
pt_meta->cap_pt = pt_cap;
pt_meta->cap_mapping = pt_mapping;
pt_meta->children = pt_children;
@ -306,9 +319,6 @@ errval_t paging_init_state(struct paging_state *st, lvaddr_t start_vaddr,
assert(ca != NULL);
st->slot_alloc = ca;
st->refilling = 0;
// initialize slab allocators
// TODO rueegges: is this how we should initialize the slab allocators?
slab_init(&st->pt_slabs, sizeof(struct pt_t), NULL);
@ -331,11 +341,18 @@ errval_t paging_init_state(struct paging_state *st, lvaddr_t start_vaddr,
// make sure all child pointers are initialized to NULL
memset(l0_children, 0, st->pt_children_slabs.blocksize);
l0_pt->level = 0;
l0_pt->cap_pt = pdir;
l0_pt->cap_mapping = NULL_CAP;
l0_pt->children = l0_children;
st->slot_alloc = ca;
st->l0_pt = l0_pt;
st->refilling = 0;
st->free_l1_vnode = NULL_CAP;
st->free_l2_vnode = NULL_CAP;
st->free_l3_vnode = NULL_CAP;
st->next_vaddr = start_vaddr;
return SYS_ERR_OK;
@ -488,15 +505,13 @@ errval_t paging_map_fixed_attr(struct paging_state *st, lvaddr_t vaddr,
lvaddr_t end_vaddr = vaddr + bytes;
assert(VADDR_OFFSET <= vaddr && vaddr < end_vaddr &&
end_vaddr <= VADDR_OFFSET + PTABLE_ENTRIES * PTABLE_ENTRIES *PTABLE_ENTRIES * PTABLE_ENTRIES * BASE_PAGE_SIZE);
// consistentcy checks
assert(st != NULL);
assert(st->slot_alloc != NULL);
assert(st->l0_pt->level == 0);
// debug_printf("DEBUG rueegges: paging_map_fixed_attr(%p, 0x%lx, cap, %lu, %d)\n", st, vaddr, bytes, flags);
// simple fix for now
// TODO rueegges: nicefy simple fix for mapping over multiple l3?
// TODO rueegges: cleanup partially completed mapping?
for(lvaddr_t current_vaddr = vaddr; current_vaddr < vaddr + bytes; current_vaddr += BASE_PAGE_SIZE) {
capaddr_t l0_index = VMSAv8_64_L0_INDEX(current_vaddr);
capaddr_t l1_index = VMSAv8_64_L1_INDEX(current_vaddr);
@ -508,36 +523,32 @@ errval_t paging_map_fixed_attr(struct paging_state *st, lvaddr_t vaddr,
// get l1 page table
struct pt_t *l1_pt;
err = pt_ensure(st, st->l0_pt, l0_index, &l1_pt);
err = pt_ensure(st, st->l0_pt, l0_index, 1, &l1_pt);
if(err_is_fail(err)) {
return err;
}
assert(l1_pt->level == 1);
assert(l1_pt->children != NULL);
// get l2 page table
struct pt_t *l2_pt;
err = pt_ensure(st, l1_pt, l1_index, &l2_pt);
err = pt_ensure(st, l1_pt, l1_index, 2, &l2_pt);
if(err_is_fail(err)) {
return err;
}
assert(l2_pt->level == 2);
assert(l2_pt->children != NULL);
// get l3 page table
struct pt_t *l3_pt;
err = pt_ensure(st, l2_pt, l2_index, &l3_pt);
err = pt_ensure(st, l2_pt, l2_index, 3, &l3_pt);
if(err_is_fail(err)) {
return err;
}
assert(l3_pt->level == 3);
assert(l3_pt->children != NULL);
// make sure we have enough slot and slab space left
err = pt_ensure_slots_and_slabs(st);
err = pt_ensure_slabs(st);
if(err_is_fail(err)) {
DEBUG_ERR(err, "Could not ensure presence of sufficient slabs and slots");
return err;
return err_push(err, LIB_ERR_SLAB_REFILL);
}
// debug_printf("DEBUG rueegges: paging_map_fixed_attr - allocate mapping meta\n");
@ -550,9 +561,6 @@ errval_t paging_map_fixed_attr(struct paging_state *st, lvaddr_t vaddr,
// set to null to indicate it is a frame mapping and not a page table mapping
pt_entry->children = NULL;
// NOTE rueegges: we set this to 4 to indicate its an entry not a map to another
// table even though for superpages the level would be 3
pt_entry->level = 4;
// allocate the new mapping
err = st->slot_alloc->alloc(st->slot_alloc, &pt_entry->cap_mapping);
if (err_is_fail(err)) {