diff options
| author | Kimplul <kimi.h.kuparinen@gmail.com> | 2021-12-21 13:24:33 +0200 |
|---|---|---|
| committer | Kimplul <kimi.h.kuparinen@gmail.com> | 2021-12-21 13:24:33 +0200 |
| commit | 0e1617f1e68c9900538296f1186c0e9768d6cb87 (patch) | |
| tree | 706ea8942cc2ddf290bc72760e81bc8569d31995 /common/vmem.c | |
| parent | 41c405fdb18b139bbde475063230e14a59304ef6 (diff) | |
| download | kmi-0e1617f1e68c9900538296f1186c0e9768d6cb87.tar.gz kmi-0e1617f1e68c9900538296f1186c0e9768d6cb87.zip | |
Started allowing higher order allocs
Diffstat (limited to 'common/vmem.c')
| -rw-r--r-- | common/vmem.c | 71 |
1 files changed, 67 insertions, 4 deletions
diff --git a/common/vmem.c b/common/vmem.c index c6a4706..865ffdc 100644 --- a/common/vmem.c +++ b/common/vmem.c @@ -165,8 +165,9 @@ static struct sp_mem *sp_mem_create_region(vm_t start, vm_t end, return m; } -static struct sp_mem *sp_free_find_first(struct sp_reg_root *r, size_t size) +static struct sp_mem *sp_free_find_first(struct sp_reg_root *r, size_t size, size_t *align) { + *align = 0; struct sp_node *n = sp_root(r->free_regions); while(n){ struct sp_mem *t = mem_container(n); @@ -181,6 +182,52 @@ static struct sp_mem *sp_free_find_first(struct sp_reg_root *r, size_t size) return 0; } +/* TODO: should probably check if this actually works :D seems to do, but that's + * just from really quick checking */ +static size_t po_align(size_t s) +{ + for(size_t o = __mm_max_order; o > 0; --o){ + if(s >= __o_size(o)) + return __o_size(o); + } + + return 0; +} + +/* should probably document this a bit better but in short, look for the "best" + * free block, meaning one that is hopefully aligned so as to allow us to later + * map it to higher order pages. If no block is found such that that is + * possible, also keep track of the smallest block that we found that the region + * still fits in, unaligned. If none of these criteria are met, a NULL is + * returned. Note that this does not check *all* possible memory blocks, only + * going up in increasing size so as to save time. */ +static struct sp_mem *sp_find_free_best(struct sp_reg_root *r, size_t size, size_t *align) +{ + *align = 0; + size_t offset = __page(po_align(__addr(size))); + struct sp_mem *quick_best = 0; + struct sp_node *n = sp_root(r->free_regions); + while(n){ + struct sp_mem *t = mem_container(n); + vm_t start = align_up(t->start, offset); + + size_t qsize = t->end - t->start; + size_t bsize = t->end - start; + + if(!quick_best && size <= qsize) + quick_best = t; + + if(size <= bsize){ + *align = start - t->start; + return t; + } + + n = sp_right(n); + } + + return quick_best; +} + /* apparently Linux doesn't necessarily give a shit about mmap hints, so I'll * just ignore them for now. Note that alloc_region should only be used when * mmap is called with MAP_ANON, all other situations should be handled in some @@ -190,18 +237,33 @@ vm_t alloc_region(struct sp_reg_root *r, { *actual_size = align_up(size, BASE_PAGE_SIZE); size_t pages = __page(*actual_size); - struct sp_mem *m = sp_free_find_first(r, pages); + + /* find best fitting, alignment etc. */ + size_t align = 0; + struct sp_mem *m = sp_find_free_best(r, pages, &align); if(!m) return 0; sp_remove(&sp_root(r->free_regions), &m->sp_n); - vm_t start = m->start; - vm_t end = m->start + pages; + vm_t pre_start = m->start; + vm_t pre_end = pre_start + align; + + vm_t start = pre_end; + vm_t end = start + pages; vm_t post_start = end; vm_t post_end = m->end; + if(pre_start != pre_end){ + struct sp_mem *n = sp_mem_create_region(pre_start, pre_end, m->prev, m); + m->prev = n; + if(n->prev) + n->prev->next = n; + + sp_free_insert_region(r, n); + } + if(post_start != post_end){ struct sp_mem *n = sp_mem_create_region(post_start, post_end, m, m->next); m->next = n; @@ -297,6 +359,7 @@ size_t uvmem_size() return __uvmem_size; } +/* TODO: add in ability to notice when a page can be mapped to a higher order */ vm_t map_fill_region(struct vm_branch_t *b, vm_t start, size_t bytes, uint8_t flags) { size_t pages = bytes / BASE_PAGE_SIZE; |
