sandbox: vm: implement page freeing; merge/split bugfix

This commit is contained in:
2023-02-01 17:05:14 +00:00
parent ca92093c10
commit 9409ebbb19
4 changed files with 91 additions and 21 deletions

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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;
}
}