diff --git a/sandbox/base/memory_test.c b/sandbox/base/memory_test.c index d84dd1f..f152829 100644 --- a/sandbox/base/memory_test.c +++ b/sandbox/base/memory_test.c @@ -47,7 +47,7 @@ static void *system_memory = NULL; static void print_free_pages(vm_zone_t *z) { printf(" * %s:\n", z->z_info.zd_name); - + for (int i = VM_PAGE_MIN_ORDER; i <= VM_PAGE_MAX_ORDER; i++) { if (queue_length(&z->z_free_pages[i]) == 0) { continue; @@ -55,8 +55,8 @@ static void print_free_pages(vm_zone_t *z) char size_str[64]; data_size_to_string(vm_page_order_to_bytes(i), size_str, sizeof size_str); - - printf(" - %u pages with size %s (order-%u)\n", queue_length(&z->z_free_pages[i]), size_str, i); + + printf(" - %u pages with size %s (order-%u)\n", queue_length(&z->z_free_pages[i]), size_str, i); } } @@ -184,11 +184,58 @@ int memory_test(void) assert(a->p_flags & VM_PAGE_HEAD); assert(b->p_flags & VM_PAGE_HEAD); - size_t nr_frames = vm_page_order_to_pages(VM_PAGE_128K); - for (size_t i = 0; i < nr_frames; i++) { - printf(" 0x%lx: order:%u, flags:0x%x\n", vm_page_get_paddr(a + i), a[i].p_order, a[i].p_flags); - assert(a[i].p_flags & VM_PAGE_HUGE); - assert((a[i].p_flags & VM_PAGE_RESERVED) == 0); + printf("first page block:\n"); + vm_page_foreach (a, i) { + printf(" 0x%lx: order:%u, flags:0x%x\n", vm_page_get_paddr(i), i->p_order, i->p_flags); + assert(i->p_flags & VM_PAGE_HUGE); + assert((i->p_flags & VM_PAGE_RESERVED) == 0); + } + printf("second page block:\n"); + vm_page_foreach (b, i) { + printf(" 0x%lx: order:%u, flags:0x%x\n", vm_page_get_paddr(i), i->p_order, i->p_flags); + assert(i->p_flags & VM_PAGE_HUGE); + assert((i->p_flags & VM_PAGE_RESERVED) == 0); + } + + pg = vm_page_merge(a, b); + if (pg) { + char size_str[64]; + data_size_to_string(vm_page_order_to_bytes(pg->p_order), size_str, sizeof size_str); + printf("merged pages 0x%lx and 0x%lx to single page of size %s:\n", vm_page_get_paddr(a), vm_page_get_paddr(b), size_str); + + size_t block_sz = 0; + vm_page_foreach (pg, i) { + printf(" 0x%lx: order:%u, flags:0x%x\n", vm_page_get_paddr(i), i->p_order, i->p_flags); + assert(i->p_flags & VM_PAGE_HUGE); + assert((i->p_flags & VM_PAGE_RESERVED) == 0); + block_sz += VM_PAGE_SIZE; + } + + assert(block_sz == vm_page_order_to_bytes(pg->p_order)); + + vm_page_free(pg); + } else { + printf("cannot merge pages 0x%lx and 0x%lx\n", vm_page_get_paddr(a), vm_page_get_paddr(b)); + } + } + + pg = vm_page_alloc(VM_PAGE_128K, 0); + printf("allocated 128K at 0x%lx\n", vm_page_get_paddr(pg)); + + if (vm_page_split(pg, &a, &b) == 0) { + assert(a->p_order == VM_PAGE_64K); + assert(b->p_order == VM_PAGE_64K); + + printf("split 128K block into two 64K blocks\n"); + vm_page_free(a); + vm_page_free(b); + + /* if these conditions are true, the two blocks were successfully + merged after being freed. */ + if (a->p_order == VM_PAGE_128K && b->p_order == VM_PAGE_128K) { + printf("two 64K blocks were merged into one 128K block after free\n"); + } else { + printf("two 64K blocks were NOT merged into one 128K block after free!\n"); } } diff --git a/sandbox/vm/include/socks/vm.h b/sandbox/vm/include/socks/vm.h index 2ab244d..23db87f 100644 --- a/sandbox/vm/include/socks/vm.h +++ b/sandbox/vm/include/socks/vm.h @@ -17,7 +17,7 @@ #define VM_PAGE_SIZE 0x1000 #define VM_PAGE_SHIFT 12 -#define VM_PAGE_IS_RESERVED(pg) ((pg)->p_flags & VM_PAGE_RESERVED) +#define VM_PAGE_IS_FREE(pg) (((pg)->p_flags & (VM_PAGE_RESERVED | VM_PAGE_ALLOC)) == 0) #define vm_page_foreach(pg, i) \ for (vm_page_t *i = (pg); i; i = vm_page_get_next_tail(i)) @@ -101,10 +101,12 @@ typedef enum vm_page_flags { /* page is reserved (probably by a call to memblock_reserve()) and cannot be returned by any allocation function */ VM_PAGE_RESERVED = 0x01u, + /* page has been allocated by a zone's buddy allocator, and is in-use */ + VM_PAGE_ALLOC = 0x02u, /* page is the first page of a huge-page */ - VM_PAGE_HEAD = 0x02u, + VM_PAGE_HEAD = 0x04u, /* page is part of a huge-page */ - VM_PAGE_HUGE = 0x04u, + VM_PAGE_HUGE = 0x08u, } vm_page_flags_t; typedef struct vm_page { diff --git a/sandbox/vm/page.c b/sandbox/vm/page.c index 0b01054..40f402c 100644 --- a/sandbox/vm/page.c +++ b/sandbox/vm/page.c @@ -190,9 +190,12 @@ int vm_page_split(vm_page_t *pg, vm_page_t **a, vm_page_t **b) return -1; } + /* NOTE that we cannot use vm_page_foreach here, + as we are modifying the flags that vm_page_foreach + uses to determine where a given page block ends */ size_t nr_frames = vm_page_order_to_pages(pg->p_order); for (size_t i = 0; i < nr_frames; i++) { - pg[i].p_order = pg->p_order - 1; + pg[i].p_order--; } vm_page_t *buddy = vm_page_get_buddy(pg); @@ -224,12 +227,8 @@ vm_page_t *vm_page_merge(vm_page_t *a, vm_page_t *b) if (vm_page_get_buddy(a) != b) { return NULL; } - - if (VM_PAGE_IS_RESERVED(a) && !VM_PAGE_IS_RESERVED(b)) { - return NULL; - } - if (!VM_PAGE_IS_RESERVED(a) && VM_PAGE_IS_RESERVED(b)) { + if ((a->p_flags & (VM_PAGE_ALLOC | VM_PAGE_RESERVED)) != (b->p_flags & (VM_PAGE_ALLOC | VM_PAGE_RESERVED))) { return NULL; } @@ -242,6 +241,9 @@ vm_page_t *vm_page_merge(vm_page_t *a, vm_page_t *b) a->p_order++; + /* NOTE that we cannot use vm_page_foreach here, + as we are modifying the flags that vm_page_foreach + uses to determine where a given page block ends */ size_t nr_frames = vm_page_order_to_pages(a->p_order); for (size_t i = 0; i < nr_frames; i++) { a[i].p_flags &= ~VM_PAGE_HEAD; @@ -251,7 +253,7 @@ vm_page_t *vm_page_merge(vm_page_t *a, vm_page_t *b) a->p_flags |= VM_PAGE_HEAD; - return 0; + return a; } vm_page_t *vm_page_get_buddy(vm_page_t *pg) diff --git a/sandbox/vm/zone.c b/sandbox/vm/zone.c index 86e7f14..67c082f 100644 --- a/sandbox/vm/zone.c +++ b/sandbox/vm/zone.c @@ -184,7 +184,7 @@ static int replenish_free_page_list(vm_zone_t *z, vm_page_order_t order) queue_push_back(&z->z_free_pages[order - 1], &a->p_free_list); queue_push_back(&z->z_free_pages[order - 1], &b->p_free_list); - + return 0; } @@ -194,13 +194,32 @@ vm_page_t *vm_zone_alloc_page(vm_zone_t *z, vm_page_order_t order, vm_flags_t fl if (result != 0) { return NULL; } - queue_entry_t *pg_entry = queue_pop_front(&z->z_free_pages[order]); - return QUEUE_CONTAINER(vm_page_t, p_free_list, pg_entry); + vm_page_t *pg = QUEUE_CONTAINER(vm_page_t, p_free_list, pg_entry); + vm_page_foreach (pg, i) { + i->p_flags |= VM_PAGE_ALLOC; + } + + return pg; } void vm_zone_free_page(vm_zone_t *z, vm_page_t *pg) { + pg->p_flags &= ~VM_PAGE_ALLOC; + queue_push_back(&z->z_free_pages[pg->p_order], &pg->p_free_list); + while (1) { + vm_page_t *buddy = vm_page_get_buddy(pg); + vm_page_t *huge = vm_page_merge(pg, buddy); + if (!huge) { + break; + } + + queue_delete(&z->z_free_pages[buddy->p_order - 1], &buddy->p_free_list); + queue_delete(&z->z_free_pages[buddy->p_order - 1], &pg->p_free_list); + queue_push_back(&z->z_free_pages[huge->p_order], &huge->p_free_list); + + pg = huge; + } }