aboutsummaryrefslogtreecommitdiff
path: root/common/vmem.c
diff options
context:
space:
mode:
authorKimplul <kimi.h.kuparinen@gmail.com>2021-12-23 18:23:50 +0200
committerKimplul <kimi.h.kuparinen@gmail.com>2021-12-23 18:23:50 +0200
commitf2300b433e152968a0fcabd79fb9d0f5fda6bca3 (patch)
tree1f41b5e9277215d83965bc61985ee18d3da608eb /common/vmem.c
parent64ef02c4baa0a1181a28e2e1b1517de3ba4907be (diff)
downloadkmi-f2300b433e152968a0fcabd79fb9d0f5fda6bca3.tar.gz
kmi-f2300b433e152968a0fcabd79fb9d0f5fda6bca3.zip
Added alloc_fixed_region
+ Not used at the moment anywhere, but should come in handy when emulating stuff like (s)brk and when mapping ELF stuff
Diffstat (limited to 'common/vmem.c')
-rw-r--r--common/vmem.c100
1 files changed, 80 insertions, 20 deletions
diff --git a/common/vmem.c b/common/vmem.c
index 3795d0b..70ef24e 100644
--- a/common/vmem.c
+++ b/common/vmem.c
@@ -177,6 +177,32 @@ static size_t po_align(size_t s)
return 0;
}
+static struct sp_mem *sp_find_used_closest(struct sp_reg_root *r, vm_t start)
+{
+ struct sp_mem *closest = 0;
+ size_t md = (size_t)(-1);
+ struct sp_node *n = sp_root(r->used_regions);
+ while(n){
+ struct sp_mem *t = mem_container(n);
+ size_t d = ABS((ssize_t)start - (ssize_t)t->start);
+
+ if(d == 0) /* exact match */
+ return t;
+
+ if(d < md){ /* closest so far */
+ closest = t;
+ md = d;
+ }
+
+ if(start < t->start)
+ n = sp_left(n);
+ else
+ n = sp_right(n);
+ }
+
+ return closest;
+}
+
/* 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
@@ -211,27 +237,14 @@ static struct sp_mem *sp_find_free_best(struct sp_reg_root *r, size_t size, size
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
- * fs server */
-vm_t alloc_region(struct sp_reg_root *r,
- size_t size, size_t *actual_size)
+static size_t sp_use_region(struct sp_reg_root *r, struct sp_mem *m,
+ size_t pages, size_t align)
{
- *actual_size = align_up(size, BASE_PAGE_SIZE);
- size_t pages = __page(*actual_size);
-
- /* 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 pre_start = m->start;
vm_t pre_end = pre_start + align;
-
+
vm_t start = pre_end;
vm_t end = start + pages;
@@ -243,8 +256,6 @@ vm_t alloc_region(struct sp_reg_root *r,
m->prev = n;
if(n->prev)
n->prev->next = n;
-
- sp_free_insert_region(r, n);
}
if(post_start != post_end){
@@ -252,8 +263,6 @@ vm_t alloc_region(struct sp_reg_root *r,
m->next = n;
if(n->next)
n->next->prev = n;
-
- sp_free_insert_region(r, n);
}
m->end = end;
@@ -263,6 +272,57 @@ vm_t alloc_region(struct sp_reg_root *r,
return __addr(start);
}
+/* 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
+ * fs server */
+vm_t alloc_region(struct sp_reg_root *r,
+ size_t size, size_t *actual_size)
+{
+ *actual_size = align_up(size, BASE_PAGE_SIZE);
+ size_t pages = __page(*actual_size);
+
+ /* find best fitting, alignment etc. */
+ size_t align = 0;
+ struct sp_mem *m = sp_find_free_best(r, pages, &align);
+ if(!m)
+ return 0;
+
+ return sp_use_region(r, m, pages, align);
+}
+
+
+vm_t alloc_fixed_region(struct sp_reg_root *r,
+ vm_t start, size_t size, size_t *actual_size)
+{
+ *actual_size = align_up(size, BASE_PAGE_SIZE);
+ size_t pages = __page(*actual_size);
+ start = __page(start);
+
+ struct sp_mem *m = sp_find_used_closest(r, start);
+ if(!m)
+ return 0;
+
+ /* locate actual region where start is between the region start and end */
+ while(!((m->start <= start) && (start <= m->end))){
+ if(start < m->start)
+ m = m->next;
+ else
+ m = m->prev;
+ }
+
+ /* if region is already in use, forget it */
+ if(is_region_used(m->flags))
+ return 0;
+
+ /* region is too small */
+ if(start + pages > m->end)
+ return 0;
+
+ /* actually start marking region used */
+ return sp_use_region(r, m, pages, start - m->start);
+}
+
static void __sp_try_coalesce_prev(struct sp_reg_root *r, struct sp_mem *m)
{
while(m){