diff options
| author | Kimplul <kimi.h.kuparinen@gmail.com> | 2022-05-12 15:52:25 +0300 |
|---|---|---|
| committer | Kimplul <kimi.h.kuparinen@gmail.com> | 2022-05-12 15:52:25 +0300 |
| commit | 1ea7d9dec1b460ab76dfd6270596c8138c9c2b40 (patch) | |
| tree | 0e8799fe0d7a653e864bd90c65a29e4012026f71 | |
| parent | 42a1e087ce99f07b48aaea5552f8e1ce5219d228 (diff) | |
| download | kmi-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.
| -rw-r--r-- | TODO.txt | 1 | ||||
| -rw-r--r-- | common/dmem.c | 4 | ||||
| -rw-r--r-- | common/elf.c | 7 | ||||
| -rw-r--r-- | common/mem_regions.c | 24 | ||||
| -rw-r--r-- | common/uapi/mem.c | 27 | ||||
| -rw-r--r-- | common/vmem.c | 58 | ||||
| -rw-r--r-- | include/apos/mem.h | 7 | ||||
| -rw-r--r-- | include/apos/mem_regions.h | 6 | ||||
| -rw-r--r-- | include/apos/vmem.h | 12 |
9 files changed, 120 insertions, 26 deletions
@@ -46,6 +46,7 @@ under a common process ID. (sort of done, not sure if linked lists even + Timers should use the sp_tree thing sorted by time of completion, and then each timer interrupt just pops the smallest value off and jumps to the associated program. Easy. (started) ++ Add more flags to memory regions, for example MR_SHARED and MR_OWNED? !!! FUTURE: + Cache locality? 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; } diff --git a/include/apos/mem.h b/include/apos/mem.h index ece8ee3..bc60f21 100644 --- a/include/apos/mem.h +++ b/include/apos/mem.h @@ -39,6 +39,13 @@ __page((x) + BASE_PAGE_SIZE)) #define __bytes(x) (__addr(x)) +/* if memory region is used or not */ +#define MR_USED (1 << 8) +/* if memory region is shared */ +#define MR_SHARED (1 << 9) +/* owner of shared region */ +#define MR_OWNED (1 << 10) + extern size_t __mm_shifts[10]; extern size_t __mm_widths[10]; extern size_t __mm_sizes[10]; diff --git a/include/apos/mem_regions.h b/include/apos/mem_regions.h index e2ab64b..7841dde 100644 --- a/include/apos/mem_regions.h +++ b/include/apos/mem_regions.h @@ -28,10 +28,12 @@ struct mem_region { stat_t init_region(struct mem_region_root *r, vm_t start, size_t arena_size); void destroy_region(struct mem_region_root *r); -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); 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); stat_t free_region(struct mem_region_root *r, vm_t start); +stat_t free_known_region(struct mem_region_root *r, struct mem_region *m); struct mem_region *find_used_region(struct mem_region_root *r, vm_t start); struct mem_region *find_closest_used_region(struct mem_region_root *r, diff --git a/include/apos/vmem.h b/include/apos/vmem.h index 571b906..5b5037d 100644 --- a/include/apos/vmem.h +++ b/include/apos/vmem.h @@ -8,20 +8,28 @@ vm_t alloc_uvmem(struct tcb *r, size_t size, vmflags_t flags); vm_t alloc_fixed_uvmem(struct tcb *r, vm_t start, size_t size, vmflags_t flags); +vm_t alloc_shared_uvmem(struct tcb *r, size_t size, + vmflags_t flags); +vm_t ref_shared_uvmem(struct tcb *r1, struct tcb *r2, vm_t va, vmflags_t flags); stat_t free_uvmem(struct tcb *r, vm_t a); stat_t init_uvmem(struct tcb *r, vm_t base, vm_t top); stat_t alloc_uvmem_wrapper(struct vm_branch *b, pm_t *offset, vm_t vaddr, vmflags_t flags, enum mm_order order); +stat_t alloc_shared_wrapper(struct vm_branch *b, pm_t *offset, vm_t vaddr, + vmflags_t flags, enum mm_order order); stat_t free_uvmem_wrapper(struct vm_branch *b, pm_t *offset, vm_t vaddr, vmflags_t flags, enum mm_order order); #define map_allocd_region(b, start, bytes, flags) \ map_fill_region(b, &alloc_uvmem_wrapper, 0, start, bytes, flags) -#define unmap_freed_region(b, start, bytes) \ - map_fill_region(b, &free_uvmem_wrapper, 0, start, bytes, 0) +#define map_shared_region(b, start, bytes, flags)\ + map_fill_region(b, &alloc_shared_wrapper, 0, start, bytes, flags) + +#define unmap_freed_region(b, start, bytes, flags) \ + map_fill_region(b, &free_uvmem_wrapper, 0, start, bytes, flags) #define vm_flags(x) ((x) & ~0xff) #define vp_flags(x) ((x)&0xff) |
