Added virtual address handling and speed up paging

This commit is contained in:
Sparchatus 2022-03-22 15:51:23 +00:00
parent 07a861ea61
commit 9173726d68
5 changed files with 185 additions and 25 deletions

View File

@ -18,6 +18,7 @@
#include <aos/solution.h> #include <aos/solution.h>
#define VADDR_OFFSET ((lvaddr_t)512UL*1024*1024*1024) // 1GB #define VADDR_OFFSET ((lvaddr_t)512UL*1024*1024*1024) // 1GB
#define VADDR_SIZE (1UL << 48)
#define VREGION_FLAGS_READ 0x01 // Reading allowed #define VREGION_FLAGS_READ 0x01 // Reading allowed
#define VREGION_FLAGS_WRITE 0x02 // Writing allowed #define VREGION_FLAGS_WRITE 0x02 // Writing allowed
#define VREGION_FLAGS_EXECUTE 0x04 // Execute allowed #define VREGION_FLAGS_EXECUTE 0x04 // Execute allowed
@ -51,6 +52,14 @@ struct pt_t {
struct pt_t **children; struct pt_t **children;
}; };
// NOTE rueegges: store information about a region of virtual address space
struct pt_vaddr_reg_t {
lvaddr_t base;
size_t size;
uint8_t free;
struct pt_vaddr_reg_t *next;
};
// struct to store the paging status of a process // struct to store the paging status of a process
struct paging_state { struct paging_state {
struct slot_allocator *slot_alloc; struct slot_allocator *slot_alloc;
@ -71,7 +80,7 @@ struct paging_state {
struct capref free_l3_vnode; struct capref free_l3_vnode;
// TODO rueegges: implement more precisely? // TODO rueegges: implement more precisely?
lvaddr_t next_vaddr; struct pt_vaddr_reg_t *vaddr_head;
}; };

View File

@ -23,12 +23,14 @@
static struct paging_state current; static struct paging_state current;
#define PT_PT_SLAB_MIN_SPACE 16 #define PT_PT_SLAB_MIN_SPACE 18
#define PT_CHILDREN_SLAB_MIN_SPACE 12 #define PT_CHILDREN_SLAB_MIN_SPACE 12
#define PT_PT_SLAB_INITIAL_SPACE SLAB_STATIC_SIZE(PT_PT_SLAB_MIN_SPACE, sizeof(struct pt_t)) #define PT_META_MAX_SIZE MAX(sizeof(struct pt_t), sizeof(struct pt_vaddr_reg_t))
#define PT_PT_SLAB_INITIAL_SPACE SLAB_STATIC_SIZE(PT_PT_SLAB_MIN_SPACE, PT_META_MAX_SIZE)
#define PT_CHILDREN_SLAB_INITIAL_SPACE SLAB_STATIC_SIZE(PT_CHILDREN_SLAB_MIN_SPACE, BASE_PAGE_SIZE) #define PT_CHILDREN_SLAB_INITIAL_SPACE SLAB_STATIC_SIZE(PT_CHILDREN_SLAB_MIN_SPACE, BASE_PAGE_SIZE)
char pt_pt_slab_buf[PT_PT_SLAB_INITIAL_SPACE]; char pt_pt_slab_buf[PT_PT_SLAB_INITIAL_SPACE];
char pt_children_slab_buf[PT_CHILDREN_SLAB_INITIAL_SPACE]; char pt_children_slab_buf[PT_CHILDREN_SLAB_INITIAL_SPACE];
@ -321,12 +323,12 @@ errval_t paging_init_state(struct paging_state *st, lvaddr_t start_vaddr,
// initialize slab allocators // initialize slab allocators
// TODO rueegges: is this how we should initialize the slab allocators? // TODO rueegges: is this how we should initialize the slab allocators?
slab_init(&st->pt_slabs, sizeof(struct pt_t), NULL); slab_init(&st->pt_slabs, PT_META_MAX_SIZE, NULL);
slab_init(&st->pt_children_slabs, BASE_PAGE_SIZE, NULL); slab_init(&st->pt_children_slabs, BASE_PAGE_SIZE, NULL);
slab_grow(&st->pt_slabs, pt_pt_slab_buf, PT_PT_SLAB_INITIAL_SPACE); slab_grow(&st->pt_slabs, pt_pt_slab_buf, PT_PT_SLAB_INITIAL_SPACE);
slab_grow(&st->pt_children_slabs, pt_children_slab_buf, PT_CHILDREN_SLAB_INITIAL_SPACE); slab_grow(&st->pt_children_slabs, pt_children_slab_buf, PT_CHILDREN_SLAB_INITIAL_SPACE);
// initialize shadow pages // initialize shadow page tables
struct pt_t *l0_pt = slab_alloc(&st->pt_slabs); struct pt_t *l0_pt = slab_alloc(&st->pt_slabs);
if(l0_pt == NULL) { if(l0_pt == NULL) {
debug_printf("Failed to alloc l0 meta\n"); debug_printf("Failed to alloc l0 meta\n");
@ -345,6 +347,19 @@ errval_t paging_init_state(struct paging_state *st, lvaddr_t start_vaddr,
l0_pt->cap_mapping = NULL_CAP; l0_pt->cap_mapping = NULL_CAP;
l0_pt->children = l0_children; l0_pt->children = l0_children;
// initialize virtual address space
struct pt_vaddr_reg_t *vaddr_reg = (struct pt_vaddr_reg_t *) slab_alloc(&st->pt_slabs);
if (vaddr_reg == NULL) {
debug_printf("Failed to alloc l0 children\n");
slab_free(&st->pt_slabs, l0_pt);
slab_free(&st->pt_children_slabs, l0_children);
return LIB_ERR_SLAB_ALLOC_FAIL;
}
vaddr_reg->base = start_vaddr;
vaddr_reg->size = VADDR_SIZE - start_vaddr;
vaddr_reg->free = true;
vaddr_reg->next = NULL;
st->slot_alloc = ca; st->slot_alloc = ca;
st->l0_pt = l0_pt; st->l0_pt = l0_pt;
st->refilling = 0; st->refilling = 0;
@ -353,7 +368,7 @@ errval_t paging_init_state(struct paging_state *st, lvaddr_t start_vaddr,
st->free_l2_vnode = NULL_CAP; st->free_l2_vnode = NULL_CAP;
st->free_l3_vnode = NULL_CAP; st->free_l3_vnode = NULL_CAP;
st->next_vaddr = start_vaddr; st->vaddr_head = vaddr_reg;
return SYS_ERR_OK; return SYS_ERR_OK;
} }
@ -421,6 +436,70 @@ errval_t paging_init_onthread(struct thread *t)
} }
static errval_t paging_insert_vaddr_reg(struct paging_state *st, struct pt_vaddr_reg_t *target_region, size_t prefix_size, size_t alloc_size)
{
assert(target_region->free);
assert(target_region->size >= prefix_size + alloc_size);
// calculate the number of bytes that are left overafter the region
size_t postfix_size = target_region->size - prefix_size - alloc_size;
struct pt_vaddr_reg_t *prefix_reg = NULL;
struct pt_vaddr_reg_t *main_reg = NULL;
struct pt_vaddr_reg_t *postfix_reg = NULL;
if (prefix_size > 0) {
prefix_reg = target_region;
main_reg = slab_alloc(&st->pt_slabs);
if (main_reg == NULL) {
return LIB_ERR_SLAB_ALLOC_FAIL;
}
} else {
main_reg = target_region;
}
if (postfix_size > 0) {
postfix_reg = slab_alloc(&st->pt_slabs);
if (postfix_reg == NULL) {
if (prefix_size > 0) {
slab_free(&st->pt_slabs, main_reg);
}
return LIB_ERR_SLAB_ALLOC_FAIL;
}
}
size_t base = target_region->base;
// update vaddr metadata structure
if (prefix_size > 0) {
prefix_reg->base = base;
prefix_reg->size = prefix_size;
prefix_reg->free = true;
main_reg->next = prefix_reg->next;
prefix_reg->next = main_reg;
base += prefix_size;
}
main_reg->base = base;
main_reg->size = alloc_size;
main_reg->free = false;
base += alloc_size;
if (postfix_size > 0) {
postfix_reg->base = base;
postfix_reg->size = postfix_size;
postfix_reg->free = true;
postfix_reg->next = main_reg->next;
main_reg->next = postfix_reg;
}
return SYS_ERR_OK;
}
/** /**
* @brief Find a free region of virtual address space that is large enough to accomodate a * @brief Find a free region of virtual address space that is large enough to accomodate a
@ -435,14 +514,49 @@ errval_t paging_init_onthread(struct thread *t)
*/ */
errval_t paging_alloc(struct paging_state *st, void **buf, size_t bytes, size_t alignment) errval_t paging_alloc(struct paging_state *st, void **buf, size_t bytes, size_t alignment)
{ {
errval_t err;
/** /**
* TODO(M2): Implement this function * TODO(M2): Implement this function
* - Find a region of free virtual address space that is large enough to * - Find a region of free virtual address space that is large enough to
* accomodate a buffer of size `bytes`. * accomodate a buffer of size `bytes`.
*/ */
assert(buf != NULL);
if ((alignment & (alignment - 1)) != 0) {
return LIB_ERR_VREGION_BAD_ALIGNMENT;
}
*buf = NULL; *buf = NULL;
return LIB_ERR_NOT_IMPLEMENTED; // start by allocating the new metadata space so we don't have to after checking the state
err = pt_ensure_slabs(st);
if (err_is_fail(err)) {
return err;
}
struct pt_vaddr_reg_t *vaddr_reg = st->vaddr_head;
while(vaddr_reg != NULL) {
// calculate the number of bytes to skip to achieve alignment
size_t prefix_size = (alignment - (vaddr_reg->base & (alignment - 1))) % alignment;
// check if it is a free region of sufficient size
if (vaddr_reg->free && vaddr_reg->size >= prefix_size + bytes) {
// allocate the new region, potentially splitting off a prefix and postfix from the region
err = paging_insert_vaddr_reg(st, vaddr_reg, prefix_size, bytes);
if (err_is_fail(err)) {
return err;
}
*buf = (void *) (vaddr_reg->base + prefix_size);
return SYS_ERR_OK;
}
vaddr_reg = vaddr_reg -> next;
}
return LIB_ERR_OUT_OF_VIRTUAL_ADDR;
} }
@ -461,6 +575,7 @@ errval_t paging_alloc(struct paging_state *st, void **buf, size_t bytes, size_t
errval_t paging_map_frame_attr(struct paging_state *st, void **buf, size_t bytes, errval_t paging_map_frame_attr(struct paging_state *st, void **buf, size_t bytes,
struct capref frame, int flags) struct capref frame, int flags)
{ {
errval_t err;
// TODO(M2): // TODO(M2):
// - Find and allocate free region of virtual address space of at least bytes in size. // - Find and allocate free region of virtual address space of at least bytes in size.
// - Map the user provided frame at the free virtual address // - Map the user provided frame at the free virtual address
@ -469,7 +584,17 @@ errval_t paging_map_frame_attr(struct paging_state *st, void **buf, size_t bytes
// Hint: // Hint:
// - think about what mapping configurations are actually possible // - think about what mapping configurations are actually possible
return LIB_ERR_NOT_IMPLEMENTED; // we can only map full pages
assert(bytes % BASE_PAGE_SIZE == 0);
// get address space to map to
err = paging_alloc(st, buf, bytes, BASE_PAGE_SIZE);
if (err_is_fail(err)) {
return err;
}
// map the virtual space to the given frame
return paging_map_fixed_attr(st, (lvaddr_t) *buf, frame, bytes, flags);
} }
@ -508,16 +633,46 @@ errval_t paging_map_fixed_attr(struct paging_state *st, lvaddr_t vaddr,
assert(st != NULL); assert(st != NULL);
assert(st->slot_alloc != NULL); assert(st->slot_alloc != NULL);
// make sure the virtual address space is reserved for this mapping. For this we require either an allocated vaddr reg to
// precisely exist as required or not at all, i.e. it is not overlapping multiple existing regions
// TODO rueegges: not sure this is the best way to do this
struct pt_vaddr_reg_t *vaddr_reg = st->vaddr_head;
while(vaddr_reg != NULL) {
vaddr_reg = vaddr_reg->next;
// we have found the region it belongs to
if(vaddr_reg->base <= vaddr && vaddr + bytes <= vaddr_reg->base + vaddr_reg->size) {
if (vaddr_reg->size >= bytes && vaddr_reg->free == 0) {
// this region was probably allocated with paging_alloc beforehand and that is ok
} else {
// make sure the virtual memory is not used by anyone else
err = paging_insert_vaddr_reg(st, vaddr_reg, vaddr - vaddr_reg->base, bytes);
if (err_is_fail(err)) {
return err;
}
}
break;
}
}
if (vaddr_reg == NULL) {
return LIB_ERR_PMAP_ADDR_NOT_FREE;
}
// debug_printf("DEBUG rueegges: paging_map_fixed_attr(%p, 0x%lx, cap, %lu, %d)\n", st, vaddr, bytes, flags); // debug_printf("DEBUG rueegges: paging_map_fixed_attr(%p, 0x%lx, cap, %lu, %d)\n", st, vaddr, bytes, flags);
// TODO rueegges: nicefy simple fix for mapping over multiple l3? // TODO rueegges: nicefy simple fix for mapping over multiple l3?
// TODO rueegges: cleanup partially completed mapping? // TODO rueegges: cleanup partially completed mapping?
for(lvaddr_t current_vaddr = vaddr; current_vaddr < vaddr + bytes; current_vaddr += BASE_PAGE_SIZE) {
size_t mapping_size;
for(lvaddr_t current_vaddr = vaddr; current_vaddr < vaddr + bytes; current_vaddr += mapping_size * BASE_PAGE_SIZE) {
capaddr_t l0_index = VMSAv8_64_L0_INDEX(current_vaddr); capaddr_t l0_index = VMSAv8_64_L0_INDEX(current_vaddr);
capaddr_t l1_index = VMSAv8_64_L1_INDEX(current_vaddr); capaddr_t l1_index = VMSAv8_64_L1_INDEX(current_vaddr);
capaddr_t l2_index = VMSAv8_64_L2_INDEX(current_vaddr); capaddr_t l2_index = VMSAv8_64_L2_INDEX(current_vaddr);
capaddr_t l3_index = VMSAv8_64_L3_INDEX(current_vaddr); capaddr_t l3_index = VMSAv8_64_L3_INDEX(current_vaddr);
// get the size of the mapping
mapping_size = MIN(PTABLE_ENTRIES - l3_index, (vaddr + bytes - current_vaddr) / BASE_PAGE_SIZE);
// Cannot map anything with l0_index = 0 since this part of the page table was created by the kernel for us // Cannot map anything with l0_index = 0 since this part of the page table was created by the kernel for us
assert(l0_index != 0); assert(l0_index != 0);
@ -556,6 +711,7 @@ errval_t paging_map_fixed_attr(struct paging_state *st, lvaddr_t vaddr,
// create structures for the new metadata // create structures for the new metadata
struct pt_t *pt_entry = (struct pt_t *) slab_alloc(&st->pt_slabs); struct pt_t *pt_entry = (struct pt_t *) slab_alloc(&st->pt_slabs);
if (pt_entry == NULL) { if (pt_entry == NULL) {
DEBUG_ERR(err, "Failed to refill slabs before adding page mapping.");
return LIB_ERR_SLAB_ALLOC_FAIL; return LIB_ERR_SLAB_ALLOC_FAIL;
} }
@ -569,8 +725,6 @@ errval_t paging_map_fixed_attr(struct paging_state *st, lvaddr_t vaddr,
// debug_printf("DEBUG rueegges: paging_map_fixed_attr - add new mapping\n"); // debug_printf("DEBUG rueegges: paging_map_fixed_attr - add new mapping\n");
// create the new mapping // create the new mapping
size_t mapping_size = 1;
// if (mapping_size * BASE_PAGE_SIZE < bytes) ++mapping_size;
// debug_printf("DEBUG rueegges: vnode_map(ll_pt, frame, %u, %d, %lu, %lu, cap_mapping)\n", l3_index, flags, 0, mapping_size); // debug_printf("DEBUG rueegges: vnode_map(ll_pt, frame, %u, %d, %lu, %lu, cap_mapping)\n", l3_index, flags, 0, mapping_size);
err = vnode_map(l3_pt->cap_pt, frame, l3_index, flags, current_vaddr - vaddr, mapping_size, pt_entry->cap_mapping); err = vnode_map(l3_pt->cap_pt, frame, l3_index, flags, current_vaddr - vaddr, mapping_size, pt_entry->cap_mapping);
if (err_is_fail(err)) { if (err_is_fail(err)) {

View File

@ -222,19 +222,17 @@ errval_t slab_refill_no_pagefault(struct slab_allocator *slabs, struct capref fr
if (err_is_fail(err)) return err_push(err, LIB_ERR_FRAME_ALLOC); if (err_is_fail(err)) return err_push(err, LIB_ERR_FRAME_ALLOC);
struct paging_state *paging_state = get_current_paging_state(); struct paging_state *paging_state = get_current_paging_state();
lvaddr_t vaddr = paging_state->next_vaddr; void *vaddr = NULL;
paging_state->next_vaddr += alloc_bytes; err = paging_map_frame_attr(
paging_state, &vaddr,
err = paging_map_fixed_attr( alloc_bytes, frame, VREGION_FLAGS_READ_WRITE
paging_state, vaddr,
frame, alloc_bytes, VREGION_FLAGS_READ_WRITE
); );
if (err_is_fail(err)) { if (err_is_fail(err)) {
cap_delete(frame); cap_delete(frame);
return err; return err;
} }
slab_grow(slabs, (void *)vaddr, alloc_bytes); slab_grow(slabs, vaddr, alloc_bytes);
return SYS_ERR_OK; return SYS_ERR_OK;
} }

View File

@ -45,13 +45,12 @@ static void *alloc_frame(size_t bytes, size_t alignment,
check_err(cap_retype(*frame_cap_ret, *ram_cap_ret, 0, ObjType_Frame, bytes, 1)); check_err(cap_retype(*frame_cap_ret, *ram_cap_ret, 0, ObjType_Frame, bytes, 1));
struct paging_state *paging_state = get_current_paging_state(); struct paging_state *paging_state = get_current_paging_state();
lvaddr_t vaddr = paging_state->next_vaddr; void *vaddr;
paging_state->next_vaddr += bytes; check_err(paging_map_frame_attr(
check_err(paging_map_fixed_attr( paging_state, &vaddr,
paging_state, vaddr, bytes, *frame_cap_ret, VREGION_FLAGS_READ_WRITE
*frame_cap_ret, bytes, VREGION_FLAGS_READ_WRITE
)); ));
return (void *)vaddr; return vaddr;
} }
void void

View File

@ -133,7 +133,7 @@ errval_t mm_alloc_aligned(struct mm *mm, size_t size, size_t alignment, struct c
return MM_ERR_OUT_OF_RAM; return MM_ERR_OUT_OF_RAM;
} }
err = slot_alloc(retcap); err = slot_alloc(retcap);/* condition */
if (err_is_fail(err)) return err_push(err, LIB_ERR_SLOT_ALLOC); if (err_is_fail(err)) return err_push(err, LIB_ERR_SLOT_ALLOC);
// Find a suitable range of free RAM // Find a suitable range of free RAM