sandbox: vm: implement page freeing; merge/split bugfix
This commit is contained in:
@@ -47,7 +47,7 @@ static void *system_memory = NULL;
|
|||||||
static void print_free_pages(vm_zone_t *z)
|
static void print_free_pages(vm_zone_t *z)
|
||||||
{
|
{
|
||||||
printf(" * %s:\n", z->z_info.zd_name);
|
printf(" * %s:\n", z->z_info.zd_name);
|
||||||
|
|
||||||
for (int i = VM_PAGE_MIN_ORDER; i <= VM_PAGE_MAX_ORDER; i++) {
|
for (int i = VM_PAGE_MIN_ORDER; i <= VM_PAGE_MAX_ORDER; i++) {
|
||||||
if (queue_length(&z->z_free_pages[i]) == 0) {
|
if (queue_length(&z->z_free_pages[i]) == 0) {
|
||||||
continue;
|
continue;
|
||||||
@@ -55,8 +55,8 @@ static void print_free_pages(vm_zone_t *z)
|
|||||||
|
|
||||||
char size_str[64];
|
char size_str[64];
|
||||||
data_size_to_string(vm_page_order_to_bytes(i), size_str, sizeof size_str);
|
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(a->p_flags & VM_PAGE_HEAD);
|
||||||
assert(b->p_flags & VM_PAGE_HEAD);
|
assert(b->p_flags & VM_PAGE_HEAD);
|
||||||
|
|
||||||
size_t nr_frames = vm_page_order_to_pages(VM_PAGE_128K);
|
printf("first page block:\n");
|
||||||
for (size_t i = 0; i < nr_frames; i++) {
|
vm_page_foreach (a, i) {
|
||||||
printf(" 0x%lx: order:%u, flags:0x%x\n", vm_page_get_paddr(a + i), a[i].p_order, a[i].p_flags);
|
printf(" 0x%lx: order:%u, flags:0x%x\n", vm_page_get_paddr(i), i->p_order, i->p_flags);
|
||||||
assert(a[i].p_flags & VM_PAGE_HUGE);
|
assert(i->p_flags & VM_PAGE_HUGE);
|
||||||
assert((a[i].p_flags & VM_PAGE_RESERVED) == 0);
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
#define VM_PAGE_SIZE 0x1000
|
#define VM_PAGE_SIZE 0x1000
|
||||||
#define VM_PAGE_SHIFT 12
|
#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) \
|
#define vm_page_foreach(pg, i) \
|
||||||
for (vm_page_t *i = (pg); i; i = vm_page_get_next_tail(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
|
/* page is reserved (probably by a call to memblock_reserve()) and cannot be
|
||||||
returned by any allocation function */
|
returned by any allocation function */
|
||||||
VM_PAGE_RESERVED = 0x01u,
|
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 */
|
/* page is the first page of a huge-page */
|
||||||
VM_PAGE_HEAD = 0x02u,
|
VM_PAGE_HEAD = 0x04u,
|
||||||
/* page is part of a huge-page */
|
/* page is part of a huge-page */
|
||||||
VM_PAGE_HUGE = 0x04u,
|
VM_PAGE_HUGE = 0x08u,
|
||||||
} vm_page_flags_t;
|
} vm_page_flags_t;
|
||||||
|
|
||||||
typedef struct vm_page {
|
typedef struct vm_page {
|
||||||
|
|||||||
@@ -190,9 +190,12 @@ int vm_page_split(vm_page_t *pg, vm_page_t **a, vm_page_t **b)
|
|||||||
return -1;
|
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);
|
size_t nr_frames = vm_page_order_to_pages(pg->p_order);
|
||||||
for (size_t i = 0; i < nr_frames; i++) {
|
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);
|
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) {
|
if (vm_page_get_buddy(a) != b) {
|
||||||
return NULL;
|
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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,6 +241,9 @@ vm_page_t *vm_page_merge(vm_page_t *a, vm_page_t *b)
|
|||||||
|
|
||||||
a->p_order++;
|
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);
|
size_t nr_frames = vm_page_order_to_pages(a->p_order);
|
||||||
for (size_t i = 0; i < nr_frames; i++) {
|
for (size_t i = 0; i < nr_frames; i++) {
|
||||||
a[i].p_flags &= ~VM_PAGE_HEAD;
|
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;
|
a->p_flags |= VM_PAGE_HEAD;
|
||||||
|
|
||||||
return 0;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
vm_page_t *vm_page_get_buddy(vm_page_t *pg)
|
vm_page_t *vm_page_get_buddy(vm_page_t *pg)
|
||||||
|
|||||||
@@ -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], &a->p_free_list);
|
||||||
queue_push_back(&z->z_free_pages[order - 1], &b->p_free_list);
|
queue_push_back(&z->z_free_pages[order - 1], &b->p_free_list);
|
||||||
|
|
||||||
return 0;
|
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) {
|
if (result != 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
queue_entry_t *pg_entry = queue_pop_front(&z->z_free_pages[order]);
|
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)
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user