Added virtual address handling and speed up paging
This commit is contained in:
parent
07a861ea61
commit
9173726d68
@ -18,6 +18,7 @@
|
||||
#include <aos/solution.h>
|
||||
|
||||
#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_WRITE 0x02 // Writing allowed
|
||||
#define VREGION_FLAGS_EXECUTE 0x04 // Execute allowed
|
||||
@ -51,6 +52,14 @@ struct pt_t {
|
||||
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 paging_state {
|
||||
struct slot_allocator *slot_alloc;
|
||||
@ -71,7 +80,7 @@ struct paging_state {
|
||||
struct capref free_l3_vnode;
|
||||
|
||||
// TODO rueegges: implement more precisely?
|
||||
lvaddr_t next_vaddr;
|
||||
struct pt_vaddr_reg_t *vaddr_head;
|
||||
};
|
||||
|
||||
|
||||
|
||||
174
lib/aos/paging.c
174
lib/aos/paging.c
@ -23,12 +23,14 @@
|
||||
|
||||
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_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)
|
||||
|
||||
|
||||
char pt_pt_slab_buf[PT_PT_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
|
||||
// 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_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);
|
||||
|
||||
// initialize shadow pages
|
||||
// initialize shadow page tables
|
||||
struct pt_t *l0_pt = slab_alloc(&st->pt_slabs);
|
||||
if(l0_pt == NULL) {
|
||||
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->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->l0_pt = l0_pt;
|
||||
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_l3_vnode = NULL_CAP;
|
||||
|
||||
st->next_vaddr = start_vaddr;
|
||||
st->vaddr_head = vaddr_reg;
|
||||
|
||||
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
|
||||
@ -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 err;
|
||||
|
||||
/**
|
||||
* TODO(M2): Implement this function
|
||||
* - Find a region of free virtual address space that is large enough to
|
||||
* accomodate a buffer of size `bytes`.
|
||||
*/
|
||||
|
||||
assert(buf != NULL);
|
||||
if ((alignment & (alignment - 1)) != 0) {
|
||||
return LIB_ERR_VREGION_BAD_ALIGNMENT;
|
||||
}
|
||||
|
||||
*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,
|
||||
struct capref frame, int flags)
|
||||
{
|
||||
errval_t err;
|
||||
// TODO(M2):
|
||||
// - 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
|
||||
@ -469,7 +584,17 @@ errval_t paging_map_frame_attr(struct paging_state *st, void **buf, size_t bytes
|
||||
// Hint:
|
||||
// - 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->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);
|
||||
|
||||
// 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) {
|
||||
|
||||
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 l1_index = VMSAv8_64_L1_INDEX(current_vaddr);
|
||||
capaddr_t l2_index = VMSAv8_64_L2_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
|
||||
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
|
||||
struct pt_t *pt_entry = (struct pt_t *) slab_alloc(&st->pt_slabs);
|
||||
if (pt_entry == NULL) {
|
||||
DEBUG_ERR(err, "Failed to refill slabs before adding page mapping.");
|
||||
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");
|
||||
// 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);
|
||||
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)) {
|
||||
|
||||
@ -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);
|
||||
|
||||
struct paging_state *paging_state = get_current_paging_state();
|
||||
lvaddr_t vaddr = paging_state->next_vaddr;
|
||||
paging_state->next_vaddr += alloc_bytes;
|
||||
|
||||
err = paging_map_fixed_attr(
|
||||
paging_state, vaddr,
|
||||
frame, alloc_bytes, VREGION_FLAGS_READ_WRITE
|
||||
void *vaddr = NULL;
|
||||
err = paging_map_frame_attr(
|
||||
paging_state, &vaddr,
|
||||
alloc_bytes, frame, VREGION_FLAGS_READ_WRITE
|
||||
);
|
||||
if (err_is_fail(err)) {
|
||||
cap_delete(frame);
|
||||
return err;
|
||||
}
|
||||
|
||||
slab_grow(slabs, (void *)vaddr, alloc_bytes);
|
||||
slab_grow(slabs, vaddr, alloc_bytes);
|
||||
|
||||
return SYS_ERR_OK;
|
||||
}
|
||||
|
||||
@ -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));
|
||||
|
||||
struct paging_state *paging_state = get_current_paging_state();
|
||||
lvaddr_t vaddr = paging_state->next_vaddr;
|
||||
paging_state->next_vaddr += bytes;
|
||||
check_err(paging_map_fixed_attr(
|
||||
paging_state, vaddr,
|
||||
*frame_cap_ret, bytes, VREGION_FLAGS_READ_WRITE
|
||||
void *vaddr;
|
||||
check_err(paging_map_frame_attr(
|
||||
paging_state, &vaddr,
|
||||
bytes, *frame_cap_ret, VREGION_FLAGS_READ_WRITE
|
||||
));
|
||||
return (void *)vaddr;
|
||||
return vaddr;
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
err = slot_alloc(retcap);
|
||||
err = slot_alloc(retcap);/* condition */
|
||||
if (err_is_fail(err)) return err_push(err, LIB_ERR_SLOT_ALLOC);
|
||||
|
||||
// Find a suitable range of free RAM
|
||||
|
||||
Loading…
Reference in New Issue
Block a user