diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/regions.c | 3 | ||||
| -rw-r--r-- | src/tcb.c | 2 | ||||
| -rw-r--r-- | src/uapi/mem.c | 7 | ||||
| -rw-r--r-- | src/vmem.c | 63 |
4 files changed, 65 insertions, 10 deletions
diff --git a/src/regions.c b/src/regions.c index d5f431d..2db1a91 100644 --- a/src/regions.c +++ b/src/regions.c @@ -440,6 +440,9 @@ static vm_t __partition_region(struct mem_region_root *r, struct mem_region *m, m->start = start; m->flags = flags; m->pid = pid; + if (m->pid == 0) + m->refcount = 1; + mark_region_used(m->flags); __insert_used_region(r, m); return __addr(start); @@ -199,7 +199,7 @@ struct tcb *create_thread(struct tcb *p) stat_t r = OK; if (p) r = __init_owned_thread(p, t); - else r = __init_free_thread(t); + else r = __init_free_thread(t); if (r) { free_page(MM_O0, bottom); diff --git a/src/uapi/mem.c b/src/uapi/mem.c index f7237d7..5c18797 100644 --- a/src/uapi/mem.c +++ b/src/uapi/mem.c @@ -29,6 +29,7 @@ SYSCALL_DEFINE2(req_mem)(struct tcb *t, sys_arg_t size, sys_arg_t flags) if (ERR_CODE(start)) return_args1(t, start); + flush_tlb_all(); return_args2(t, OK, start); } @@ -52,6 +53,7 @@ SYSCALL_DEFINE3(req_fixmem)(struct tcb *t, sys_arg_t fixed, sys_arg_t size, if (ERR_CODE(start)) return_args1(t, start); + flush_tlb_all(); return_args2(t, OK, start); } @@ -75,6 +77,7 @@ SYSCALL_DEFINE1(free_mem)(struct tcb *t, sys_arg_t start) if (!(status = free_devmem(r, start))) return_args1(t, OK); + flush_tlb_all(); return_args1(t, status); } @@ -102,6 +105,7 @@ SYSCALL_DEFINE3(req_pmem)(struct tcb *t, sys_arg_t paddr, sys_arg_t size, if (ERR_CODE(start)) return_args1(t, start); + flush_tlb_all(); return_args2(t, OK, start); } @@ -131,6 +135,7 @@ SYSCALL_DEFINE2(req_page)(struct tcb *t, sys_arg_t size, sys_arg_t flags) if (ERR_CODE(start)) return_args1(t, start); + flush_tlb_all(); return_args4(t, OK, start, addr, asize); } @@ -154,6 +159,7 @@ SYSCALL_DEFINE2(req_sharedmem)(struct tcb *t, sys_arg_t size, sys_arg_t flags) if (ERR_CODE(start)) return_args1(t, start); + flush_tlb_all(); return_args2(t, OK, start); } @@ -184,5 +190,6 @@ SYSCALL_DEFINE3(ref_sharedmem)(struct tcb *t, sys_arg_t tid, sys_arg_t addr, if (ERR_CODE(start)) return_args1(t, start); + flush_tlb_all(); return_args2(t, OK, start); } @@ -63,6 +63,37 @@ static stat_t __copy_mapped_region(struct tcb *d, struct tcb *s, return res; } +static void reference_mem(struct tcb *d, struct tcb *s, vm_t ref, vm_t orig) +{ + struct mem_region *src = find_used_region(&s->uvmem.region, orig); + assert(src); + + struct mem_region *dst = find_used_region(&d->uvmem.region, ref); + assert(dst); + + assert(dst->pid == s->rid); + dst->shaddr = orig; + src->refcount++; + + reference_thread(s); +} + +static void __free_mapping(struct tcb *t, struct mem_region *m); + +static void unreference_mem(struct tcb *s, vm_t addr) +{ + struct mem_region *src = find_used_region(&s->uvmem.region, addr); + assert(src); + + assert(src->refcount >= 1); + if (--src->refcount == 0) { + __free_mapping(s, src); + free_known_region(&s->uvmem.region, src); + } + + unreference_thread(s); +} + /** * Copy shared regions to new process. In these cases, we want to both allocate * a fixed region and map some fixed physical memory. @@ -80,7 +111,6 @@ static stat_t __copy_shared_region(struct tcb *d, struct mem_region *m) vm_t start = m->start * BASE_PAGE_SIZE; vm_t end = m->end * BASE_PAGE_SIZE; - reference_thread(s); size_t size = end - start; vm_t v = alloc_shared_fixed_region(&d->uvmem.region, start, size, &size, @@ -91,8 +121,10 @@ static stat_t __copy_shared_region(struct tcb *d, struct mem_region *m) assert(v == start); stat_t res = clone_region(d->uvmem.vmem, s->uvmem.vmem, start, v, size, m->flags); - if (res == OK) + if (res == OK) { + reference_mem(d, s, v, v); return OK; + } /* cleanup on error */ free_region(&d->uvmem.region, v); @@ -115,8 +147,6 @@ static vm_t __clone_shared_region(struct tcb *d, struct tcb *s, vm_t start = m->start * BASE_PAGE_SIZE; vm_t end = m->end * BASE_PAGE_SIZE; - reference_thread(s); - size_t size = end - start; vm_t v = alloc_shared_region(&d->uvmem.region, size, &size, MR_NONBACKED | m->flags, s->rid); @@ -125,18 +155,19 @@ static vm_t __clone_shared_region(struct tcb *d, struct tcb *s, stat_t res = clone_region(d->uvmem.vmem, s->uvmem.vmem, start, v, size, flags); - if (res == OK) + if (res == OK) { + reference_mem(d, s, v, start); return v; + } /* cleanup on error */ - unreference_thread(s); free_region(&d->uvmem.region, v); unmap_fixed_region(d->uvmem.vmem, v, size); return res; } /** - * Unmap and free private memory region. + * Unmap and free memory region. * * @param t Current thread. * @param m Memory region to free. @@ -145,7 +176,7 @@ static void __free_mapping(struct tcb *t, struct mem_region *m) { struct tcb *owner = get_tcb(m->pid); if (owner) - unreference_thread(owner); + unreference_mem(owner, m->shaddr); if (is_set(m->flags, MR_NONBACKED)) return; @@ -173,6 +204,17 @@ void clear_uvmem(struct tcb *t) if (!is_set(m->flags, MR_USED)) continue; + if (m->pid == 0 && m->refcount > 1) { + /* we own this shared region and it's used by someone + * else so we can't outright free it yet, but remove our + * 'reference' to it so the refcount reaches zero when all + * referees unmap it, instead of waiting for this thread to be + * completely destroyed. + * */ + m->refcount--; + continue; + } + __free_mapping(t, m); free_known_region(&t->uvmem.region, m); } @@ -272,7 +314,7 @@ vm_t map_shared_fixed_uvmem(struct tcb *t, pm_t start, size_t size, assert(is_aligned(start, BASE_PAGE_SIZE)); const vm_t v = alloc_shared_region(&t->uvmem.region, size, &size, flags, - get_rproc(t)->tid); + 0); if (ERR_CODE(v)) return v; @@ -354,6 +396,9 @@ stat_t free_uvmem(struct tcb *r, vm_t va) if (!m) return ERR_NF; + if (m->pid == 0 && m->refcount > 1) + return ERR_INVAL; + __free_mapping(r, m); free_known_region(&r->uvmem.region, m); return OK; |
