aboutsummaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authorKimplul <kimi.h.kuparinen@gmail.com>2022-05-12 15:52:25 +0300
committerKimplul <kimi.h.kuparinen@gmail.com>2022-05-12 15:52:25 +0300
commit1ea7d9dec1b460ab76dfd6270596c8138c9c2b40 (patch)
tree0e8799fe0d7a653e864bd90c65a29e4012026f71 /common
parent42a1e087ce99f07b48aaea5552f8e1ce5219d228 (diff)
downloadkmi-1ea7d9dec1b460ab76dfd6270596c8138c9c2b40.tar.gz
kmi-1ea7d9dec1b460ab76dfd6270596c8138c9c2b40.zip
initial workings of shared memory
+ Should document this better but essentially a server requests a shared buffer, and chan then share this buffer to a thread. Freeing the buffer is done through normal means. MR_SHARED is used for buffers that are shared, and MR_OWNED tells whoever is freeing that region that it is allowed to free the physical memory behind the shared buffer.
Diffstat (limited to 'common')
-rw-r--r--common/dmem.c4
-rw-r--r--common/elf.c7
-rw-r--r--common/mem_regions.c24
-rw-r--r--common/uapi/mem.c27
-rw-r--r--common/vmem.c58
5 files changed, 98 insertions, 22 deletions
diff --git a/common/dmem.c b/common/dmem.c
index 226b3f9..59ea666 100644
--- a/common/dmem.c
+++ b/common/dmem.c
@@ -53,10 +53,10 @@ vm_t alloc_devmem(struct tcb *t, pm_t dev_start, size_t bytes, vmflags_t flags)
{
vm_t region = 0;
if (dev_start < __pre_top)
- region = alloc_region(&pre_ram, bytes, 0);
+ region = alloc_region(&pre_ram, bytes, 0, flags);
if (dev_start > __post_base)
- region = alloc_region(&post_ram, bytes, 0);
+ region = alloc_region(&post_ram, bytes, 0, flags);
if (!region)
return 0;
diff --git a/common/elf.c b/common/elf.c
index ddf61c9..459cacf 100644
--- a/common/elf.c
+++ b/common/elf.c
@@ -30,6 +30,7 @@ static void __map_exec(struct tcb *t, vm_t bin, uint8_t ei_c, vm_t phstart,
* filled with zeroes. */
/* TODO: in general, make this a low more clean. */
vm_t runner = phstart;
+ vmflags_t default_flags = VM_V | VM_R | VM_W | VM_X | VM_U;
for (size_t i = 0; i < phnum; ++i, runner += phsize) {
if (program_header_prop(ei_c, runner, p_type) != PT_LOAD)
continue;
@@ -37,15 +38,15 @@ static void __map_exec(struct tcb *t, vm_t bin, uint8_t ei_c, vm_t phstart,
vm_t va = program_header_prop(ei_c, runner, p_vaddr);
size_t vsz = program_header_prop(ei_c, runner, p_memsz);
- vm_t start = alloc_fixed_region(&t->sp_r, va, vsz, &vsz);
+ vm_t start = alloc_fixed_region(&t->sp_r, va, vsz, &vsz,
+ default_flags);
if (!start)
return; /* out of memory or something */
uint8_t elf_flags = program_header_prop(ei_c, runner, p_flags);
uint8_t uvflags = __elf_to_uvflags(elf_flags);
- map_allocd_region(t->b_r, start, vsz,
- VM_V | VM_X | VM_R | VM_W | VM_U);
+ map_allocd_region(t->b_r, start, vsz, default_flags);
vm_t vo = bin + program_header_prop(ei_c, runner, p_offset);
vm_t vfz = program_header_prop(ei_c, runner, p_filesz);
diff --git a/common/mem_regions.c b/common/mem_regions.c
index bbd9a3b..b0cbe0e 100644
--- a/common/mem_regions.c
+++ b/common/mem_regions.c
@@ -1,11 +1,12 @@
#include <apos/mem_regions.h>
#include <apos/mem_nodes.h>
#include <apos/pmem.h>
+#include <apos/bits.h>
#include <apos/mem.h>
-#define mark_region_used(r) ((r) = 1)
-#define mark_region_unused(r) ((r) = 0)
-#define region_used(r) (r)
+#define mark_region_used(r) __set_bit(r, MR_USED)
+#define mark_region_unused(r) __clear_bit(r, MR_USED)
+#define region_used(r) __is_set(r, MR_USED)
/* pretty major slowdown when we get to some really massive numbers, not
* entirely sure why. Will need to check up on this at some point, have I
@@ -245,7 +246,7 @@ struct mem_region *find_free_region(struct mem_region_root *r, size_t size,
}
static vm_t __partition_region(struct mem_region_root *r, struct mem_region *m,
- size_t pages, size_t align)
+ size_t pages, size_t align, vmflags_t flags)
{
sp_remove(&sp_root(&r->free_regions), &m->sp_n);
@@ -280,6 +281,7 @@ static vm_t __partition_region(struct mem_region_root *r, struct mem_region *m,
m->end = end;
m->start = start;
+ m->flags = flags;
mark_region_used(m->flags);
__insert_used_region(r, m);
return __addr(start);
@@ -289,7 +291,8 @@ static vm_t __partition_region(struct mem_region_root *r, struct mem_region *m,
* 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 mem_region_root *r, size_t size, size_t *actual_size)
+vm_t alloc_region(struct mem_region_root *r, size_t size, size_t *actual_size,
+ vmflags_t flags)
{
size_t asize = align_up(size, BASE_PAGE_SIZE);
if (actual_size)
@@ -303,11 +306,11 @@ vm_t alloc_region(struct mem_region_root *r, size_t size, size_t *actual_size)
if (!m)
return 0;
- return __partition_region(r, m, pages, align);
+ return __partition_region(r, m, pages, align, flags);
}
vm_t alloc_fixed_region(struct mem_region_root *r, vm_t start, size_t size,
- size_t *actual_size)
+ size_t *actual_size, vmflags_t flags)
{
size_t asize = align_up(size, BASE_PAGE_SIZE);
if (actual_size)
@@ -337,7 +340,7 @@ vm_t alloc_fixed_region(struct mem_region_root *r, vm_t start, size_t size,
return 0;
/* actually start marking region used */
- return __partition_region(r, m, pages, start - m->start);
+ return __partition_region(r, m, pages, start - m->start, flags);
}
static void __try_coalesce_prev(struct mem_region_root *r, struct mem_region *m)
@@ -403,6 +406,11 @@ stat_t free_region(struct mem_region_root *r, vm_t start)
if (!m)
return ERR_NF;
+ return free_known_region(r, m);
+}
+
+stat_t free_known_region(struct mem_region_root *r, struct mem_region *m)
+{
sp_remove(&sp_root(&r->used_regions), &m->sp_n);
mark_region_unused(m->flags);
diff --git a/common/uapi/mem.c b/common/uapi/mem.c
index bc3c7fa..ace40b9 100644
--- a/common/uapi/mem.c
+++ b/common/uapi/mem.c
@@ -41,10 +41,27 @@ SYSCALL_DEFINE3(req_pmem)(sys_arg_t paddr, sys_arg_t size, sys_arg_t flags)
return (struct sys_ret){ OK, alloc_devmem(r, paddr, size, flags) };
}
-SYSCALL_DEFINE4(req_sharedmem)
-(sys_arg_t tid, sys_arg_t start, sys_arg_t size, sys_arg_t flags)
+SYSCALL_DEFINE2(req_sharedmem)
+(sys_arg_t size, sys_arg_t flags)
{
- /* called by server, take memory allocation at start and map it into
- * tid's address space? */
- return (struct sys_ret){ OK, 0 };
+ /* TODO: check that requester is server */
+ struct tcb *t = cur_tcb();
+ vm_t start = 0;
+ if ((start = alloc_shared_uvmem(t, size, flags)))
+ return (struct sys_ret){ ERR_OOMEM, 0 };
+
+ return (struct sys_ret){ OK, start};
+}
+
+/* add a syscall like ref_sharedmem that adds a reference to an existing shared
+ * memory region to a new tid? */
+SYSCALL_DEFINE3(ref_sharedmem)(sys_arg_t tid, sys_arg_t va, sys_arg_t flags)
+{
+ struct tcb *t1 = cur_tcb();
+ struct tcb *t2 = get_tcb(tid);
+ vm_t start = 0;
+ if ((start = ref_shared_uvmem(t1, t2, va, flags)))
+ return (struct sys_ret){ERR_OOMEM, 0};
+
+ return (struct sys_ret) {OK, start};
}
diff --git a/common/vmem.c b/common/vmem.c
index d0d40ec..89e1140 100644
--- a/common/vmem.c
+++ b/common/vmem.c
@@ -1,4 +1,5 @@
#include <apos/mem_regions.h>
+#include <apos/bits.h>
#include <apos/vmem.h>
#include <arch/vmem.h>
@@ -9,16 +10,51 @@ stat_t init_uvmem(struct tcb *t, vm_t base, vm_t top)
vm_t alloc_uvmem(struct tcb *t, size_t size, vmflags_t flags)
{
- vm_t v = alloc_region(&t->sp_r, size, &size);
+ vm_t v = alloc_region(&t->sp_r, size, &size, flags);
return map_allocd_region(t->b_r, v, size, flags);
}
vm_t alloc_fixed_uvmem(struct tcb *t, vm_t start, size_t size, vmflags_t flags)
{
- vm_t v = alloc_fixed_region(&t->sp_r, start, size, &size);
+ vm_t v = alloc_fixed_region(&t->sp_r, start, size, &size, flags);
return map_allocd_region(t->b_r, v, size, flags);
}
+/* free_shared_uvmem shouldn't be needed, likely to work with free_uvmem */
+vm_t alloc_shared_uvmem(struct tcb *t, size_t size,
+ vmflags_t flags)
+{
+ /* TODO: proper error handling */
+ vm_t v = alloc_region(&t->sp_r, size, &size,
+ flags | MR_SHARED | MR_OWNED);
+ return map_shared_region(t->b_r, v, size, flags);
+}
+
+vm_t ref_shared_uvmem(struct tcb *t1, struct tcb *t2, vm_t va, vmflags_t flags)
+{
+ struct mem_region *m = find_used_region(&t1->sp_r, va);
+ if (!m)
+ return 0;
+
+ if (!__is_set(m->flags, MR_OWNED))
+ return 0;
+
+ /* loop over all pages in this region */
+ size_t size = m->end - m->start;
+ size_t pages = __pages(size);
+ vm_t v = alloc_region(&t2->sp_r, size, &size, flags | MR_SHARED);
+ /* a for each page could be cool? */
+ vm_t runner = v;
+ for (; pages; --pages) {
+ pm_t paddr;
+ stat_vpage(t1->b_r, v, &paddr, 0, 0);
+ map_vpage(t2->b_r, paddr, runner, flags, MM_O0);
+ runner += __o_size(MM_O0);
+ }
+
+ return v;
+}
+
stat_t free_uvmem(struct tcb *t, vm_t va)
{
struct mem_region *m = find_used_region(&t->sp_r, va);
@@ -27,8 +63,9 @@ stat_t free_uvmem(struct tcb *t, vm_t va)
pm_t pa = __addr(m->end - m->start);
+ vmflags_t flags = m->flags;
free_region(&t->sp_r, va);
- unmap_freed_region(t->b_r, va, pa);
+ unmap_freed_region(t->b_r, va, pa, flags);
return 0;
}
@@ -43,6 +80,17 @@ stat_t alloc_uvmem_wrapper(struct vm_branch *b, pm_t *offset, vm_t vaddr,
return OK;
}
+stat_t alloc_shared_wrapper(struct vm_branch *b, pm_t *offset, vm_t vaddr,
+ vmflags_t flags, enum mm_order order)
+{
+ if (order != MM_O0)
+ return REGION_TRY_AGAIN;
+
+ *offset = alloc_page(MM_O0, *offset);
+ map_vpage(b, *offset, vaddr, flags, order);
+ return OK;
+}
+
stat_t free_uvmem_wrapper(struct vm_branch *b, pm_t *offset, vm_t vaddr,
vmflags_t flags, enum mm_order order)
{
@@ -56,6 +104,8 @@ stat_t free_uvmem_wrapper(struct vm_branch *b, pm_t *offset, vm_t vaddr,
return REGION_TRY_AGAIN;
unmap_vpage(b, vaddr);
- free_page(order, paddr);
+ /* don't free shared pages, unless they're owned */
+ if (!__is_set(flags, MR_SHARED) || __is_set(flags, MR_OWNED))
+ free_page(order, paddr);
return OK;
}