From 5793a61eb824a7b97e9bab5913b595a778205cef Mon Sep 17 00:00:00 2001 From: Kimplul Date: Sun, 22 May 2022 15:05:39 +0300 Subject: improved tcb handling in preparation for processes --- arch/riscv64/kernel/vmem.c | 23 ++++++++--- common/mem_regions.c | 34 +++++++++++----- common/proc.c | 7 ++-- common/tcb.c | 98 ++++++++++++++++++++++++++++++++++++++-------- common/uapi/dispatch.c | 3 +- common/uapi/ipc.c | 17 +++++--- common/vmem.c | 51 ++++++++++++++++++------ include/apos/assert.h | 13 +++--- include/apos/mem_regions.h | 5 ++- include/apos/syscalls.h | 3 +- include/apos/tcb.h | 23 ++++++++--- include/apos/uapi.h | 3 +- include/apos/vmem.h | 2 + include/arch/vmem.h | 6 ++- 14 files changed, 221 insertions(+), 67 deletions(-) diff --git a/arch/riscv64/kernel/vmem.c b/arch/riscv64/kernel/vmem.c index 7ffde86..0285f39 100644 --- a/arch/riscv64/kernel/vmem.c +++ b/arch/riscv64/kernel/vmem.c @@ -168,16 +168,28 @@ static void __start_vmem(struct vm_branch *branch, enum mm_mode m) struct vm_branch *init_vmem(void *fdt) { UNUSED(fdt); + struct vm_branch *b = create_vmem(); + /* update which memory branch to use */ + __start_vmem(b, Sv39); + return b; +} + +struct vm_branch *create_vmem() +{ struct vm_branch *b = (struct vm_branch *)alloc_page(MM_KPAGE, 0); memset(b, 0, MM_KPAGE_SIZE); - populate_root_branch(b); - /* update which memory branch to use */ - __start_vmem(b, Sv39); + populate_kvmem(b); return b; } -void populate_root_branch(struct vm_branch *b) +stat_t destroy_vmem(struct vm_branch *b) +{ + free_page(MM_KPAGE, (pm_t)b); + return OK; +} + +stat_t populate_kvmem(struct vm_branch *b) { size_t flags = VM_V | VM_R | VM_W | VM_X | VM_G; for (size_t i = KSTART_PAGE; i < IO_PAGE; ++i) @@ -187,6 +199,7 @@ void populate_root_branch(struct vm_branch *b) /* map kernel IO to zero for now, this will be overridden later * (if at all) */ b->leaf[IO_PAGE] = (struct vm_branch *)to_pte(0, flags); + return OK; } #if defined(DEBUG) @@ -200,7 +213,7 @@ vm_t setup_kernel_io(struct vm_branch *b, vm_t paddr) } #endif -stat_t clone_vmbranch(struct vm_branch *r, struct vm_branch *b) +stat_t clone_uvmem(struct vm_branch *r, struct vm_branch *b) { /* TODO: error checking? */ for (size_t i = 0; i <= CSTACK_PAGE; ++i) diff --git a/common/mem_regions.c b/common/mem_regions.c index bbc894e..9f8f98e 100644 --- a/common/mem_regions.c +++ b/common/mem_regions.c @@ -6,7 +6,6 @@ #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 @@ -121,17 +120,22 @@ static void __destroy_region(struct sp_node *n) if (!n) return; - __destroy_region(sp_left(n)); - __destroy_region(sp_right(n)); + if (sp_left(n)) + __destroy_region(sp_left(n)); + + if (sp_right(n)) + __destroy_region(sp_right(n)); struct mem_region *m = mem_container(n); free_mem_node(m); } -void destroy_region(struct mem_region_root *r) +stat_t destroy_region(struct mem_region_root *r) { __destroy_region(sp_root(&r->free_regions)); __destroy_region(sp_root(&r->used_regions)); + /* TODO: error checking? */ + return OK; } /* interestingly this is now the main bottleneck :D @@ -245,6 +249,18 @@ struct mem_region *find_free_region(struct mem_region_root *r, size_t size, return quick_best; } +struct mem_region *find_first_region(struct mem_region_root *r) +{ + /* get used region with smallest address, likely also close to the start + * of the linked list */ + struct mem_region *m = find_closest_used_region(r, 0); + while (m->prev) { + m = m->prev; + } + + return m; +} + static vm_t __partition_region(struct mem_region_root *r, struct mem_region *m, size_t pages, size_t align, vmflags_t flags) { @@ -332,7 +348,7 @@ vm_t alloc_fixed_region(struct mem_region_root *r, vm_t start, size_t size, } /* if region is already in use, forget it */ - if (region_used(m->flags)) + if (is_region_used(m)) return 0; /* region is too small */ @@ -346,11 +362,11 @@ vm_t alloc_fixed_region(struct mem_region_root *r, vm_t start, size_t size, static void __try_coalesce_prev(struct mem_region_root *r, struct mem_region *m) { while (m) { - if (!m || region_used(m->flags)) + if (!m || is_region_used(m)) return; struct mem_region *p = m->prev; - if (!p || region_used(p->flags)) + if (!p || is_region_used(p)) return; m->start = p->start; @@ -369,11 +385,11 @@ static void __try_coalesce_prev(struct mem_region_root *r, struct mem_region *m) static void __try_coalesce_next(struct mem_region_root *r, struct mem_region *m) { while (m) { - if (!m || region_used(m->flags)) + if (!m || is_region_used(m)) return; struct mem_region *n = m->next; - if (!n || region_used(n->flags)) + if (!n || is_region_used(n)) return; m->end = n->end; diff --git a/common/proc.c b/common/proc.c index 8793d00..8824c5a 100644 --- a/common/proc.c +++ b/common/proc.c @@ -29,12 +29,13 @@ stat_t init_proc(void *fdt, struct vm_branch *b) { init_tcbs(); - /* todo: cleanup or something */ - struct tcb *t = new_thread(); + /* TODO: cleanup or something */ + struct tcb *t = create_thread(NULL); if (!t) return ERR_OOMEM; - t->b_r = b; + /* use existing branch */ + t->b_r = b; init_uvmem(t, UVMEM_START, UVMEM_END); /* TODO: this stuff should be placed in __sys_exec */ diff --git a/common/tcb.c b/common/tcb.c index 212e20f..32c31e1 100644 --- a/common/tcb.c +++ b/common/tcb.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -12,7 +13,6 @@ static id_t start_tid; static size_t num_tids; -static struct node_root root; static struct tcb **tcbs; /* if we ever support systems with massive amounts of cpus, this should probably @@ -31,8 +31,7 @@ void init_tcbs() void destroy_tcbs() { - destroy_nodes(&root); - free_page(MM_O2, (pm_t)tcbs); + free_page(MM_O1, (pm_t)tcbs); } static id_t __alloc_tid(struct tcb *t) @@ -50,10 +49,9 @@ static id_t __alloc_tid(struct tcb *t) return ERR_NF; } -struct tcb *new_thread() +struct tcb *create_thread(struct tcb *p) { - if (unlikely(!tcbs)) - return 0; + hard_assert(tcbs, 0); vm_t bottom = alloc_page(MM_O0, 0); /* move tcb to top of kernel stack, keeping alignment in check @@ -67,20 +65,80 @@ struct tcb *new_thread() tcbs[tid] = t; t->tid = tid; + if (p) { + t->pid = p->pid; + t->proc = p; + } else { + t->pid = t->tid; + } + return t; } -void destroy_thread(struct tcb *t) +static stat_t __clone_proc(struct tcb *p, struct tcb *n) +{ + /* TODO: clone memory regions, and mark them MR_COW, as well as copy + * bm_branch tree but with VM_W off, also at some point write COW + * handler */ + return OK; +} + +struct tcb *create_proc(struct tcb *p) { - if (unlikely(!tcbs)) - return; + hard_assert(tcbs, 0); - /* remove thread id from list */ - tcbs[t->tid] = 0; + /* create a new thread outside the current process */ + struct tcb *n = create_thread(0); + n->b_r = create_vmem(); - /* free associated kernel stack */ + if (likely(p)) + __clone_proc(p, n); /* we have a parent thread */ + else + init_uvmem(n, UVMEM_START, UVMEM_END); + + return n; +} + +static stat_t __destroy_thread_data(struct tcb *t) +{ + /* free vm_branch */ + destroy_vmem(t->b_r); + + /* free associated kernel stack and the structure itself */ vm_t bottom = align_down((vm_t)t, __o_size(MM_O0)); free_page(MM_O0, (pm_t)bottom); + + return OK; +} + +stat_t destroy_thread(struct tcb *t) +{ + hard_assert(tcbs, ERR_NOINIT); + hard_assert(!is_proc(t), ERR_INVAL); + + /* remove thread id from list */ + tcbs[t->tid] = 0; + + /* remove thread from process list */ + if (t->next) + t->next->prev = t->prev; + + if (t->prev) + t->prev->next = t->next; + + return __destroy_thread_data(t); +} + +stat_t destroy_proc(struct tcb *p) +{ + hard_assert(tcbs, ERR_NOINIT); + hard_assert(is_proc(p), ERR_INVAL); + + for (struct tcb *iter = p; (iter = iter->next);) + destroy_thread(iter); + + catastrophic_assert(destroy_uvmem(p)); + return __destroy_thread_data(p); } struct tcb *cur_tcb() @@ -88,6 +146,15 @@ struct tcb *cur_tcb() return cpu_tcb[cpu_id()]; } +struct tcb *cur_proc() +{ + struct tcb *t = cur_tcb(); + if (likely(is_proc(t))) + return t; + else + return t->proc; +} + void use_tcb(struct tcb *t) { cpu_tcb[cpu_id()] = t; @@ -95,18 +162,17 @@ void use_tcb(struct tcb *t) struct tcb *get_tcb(id_t tid) { - if (unlikely(!tcbs)) - return 0; + hard_assert(tcbs, 0); return tcbs[tid]; } stat_t clone_tcb_maps(struct tcb *r) { - hard_assert(r && !r->parent, ERR_INVAL); + hard_assert(r && is_proc(r), ERR_INVAL); struct tcb *t = r; while ((t = t->next)) { - stat_t ret = clone_vmbranch(r->b_r, t->b_r); + stat_t ret = clone_uvmem(r->b_r, t->b_r); if (ret) return ret; } diff --git a/common/uapi/dispatch.c b/common/uapi/dispatch.c index 97fc0d8..5e2e788 100644 --- a/common/uapi/dispatch.c +++ b/common/uapi/dispatch.c @@ -17,7 +17,8 @@ static const sys_t syscall_table[] = { /* ipc */ [SYS_IPC_SERVER] = sys_ipc_server, - [SYS_IPC_REQ] = sys_ipc_req, + [SYS_IPC_REQ_PROC] = sys_ipc_req_proc, + [SYS_IPC_REQ_THREAD] = sys_ipc_req_thread, [SYS_IPC_RESP] = sys_ipc_resp, /* proc */ diff --git a/common/uapi/ipc.c b/common/uapi/ipc.c index d7c9d16..0d2d287 100644 --- a/common/uapi/ipc.c +++ b/common/uapi/ipc.c @@ -11,16 +11,23 @@ SYSCALL_DEFINE1(ipc_server)(sys_arg_t callback) return (struct sys_ret){ OK, 0 }; } -SYSCALL_DEFINE3(ipc_req)(sys_arg_t tid, sys_arg_t d0, sys_arg_t d1) +SYSCALL_DEFINE3(ipc_req_proc)(sys_arg_t pid, sys_arg_t d0, sys_arg_t d1) { - struct tcb *r = get_tcb(tid); - /* something like jump_to_callback(t) */ + struct tcb *r = get_tcb(pid); + /* TODO: something like jump_to_callback(t) */ return (struct sys_ret){ d0, d1 }; } -SYSCALL_DEFINE3(ipc_resp)(sys_arg_t tid, sys_arg_t d0, sys_arg_t d1) +SYSCALL_DEFINE3(ipc_req_thread)(sys_arg_t tid, sys_arg_t d0, sys_arg_t d1) { - struct tcb *r = get_tcb(tid); + struct tcb *t = get_tcb(tid); + /* ditto */ + return (struct sys_ret){ d0, d1 }; +} + +SYSCALL_DEFINE2(ipc_resp)(sys_arg_t d0, sys_arg_t d1) +{ + struct tcb *r = cur_tcb(); /* something like return_from_callback(t, r) */ return (struct sys_ret){ d0, d1 }; } diff --git a/common/vmem.c b/common/vmem.c index ff27ed8..b665491 100644 --- a/common/vmem.c +++ b/common/vmem.c @@ -10,10 +10,33 @@ stat_t init_uvmem(struct tcb *t, vm_t base, vm_t top) return init_region(&t->sp_r, base, top); } +static stat_t __free_mapped_region(struct tcb *t, struct mem_region *m) +{ + stat_t status = OK; + pm_t pa = __addr(m->end - m->start); + if (unmap_freed_region(t->b_r, m->start, pa, m->flags, &status)) + return ERR_MISC; + + return status; +} + +stat_t destroy_uvmem(struct tcb *t) +{ + struct mem_region *m = find_first_region(&t->sp_r); + while (m) { + /* free all memory associated with used regions */ + if (is_region_used(m)) + __free_mapped_region(t, m); + } + + /* destroy region tree itself */ + return destroy_region(&t->sp_r); +} + vm_t alloc_uvmem(struct tcb *t, size_t size, vmflags_t flags) { - /* t exists and is the root tcb of the current process */ - hard_assert(t && !t->parent, ERR_INVAL); + /* t exists and is the process tcb of the current process */ + hard_assert(t && is_proc(t), ERR_INVAL); stat_t status = OK; const vm_t v = alloc_region(&t->sp_r, size, &size, flags); @@ -26,7 +49,7 @@ vm_t alloc_uvmem(struct tcb *t, size_t size, vmflags_t flags) vm_t alloc_fixed_uvmem(struct tcb *t, vm_t start, size_t size, vmflags_t flags) { - hard_assert(t && !t->parent, ERR_INVAL); + hard_assert(t && is_proc(t), ERR_INVAL); stat_t status = OK; const vm_t v = alloc_fixed_region(&t->sp_r, start, size, &size, flags); @@ -41,7 +64,7 @@ vm_t alloc_fixed_uvmem(struct tcb *t, vm_t start, size_t size, vmflags_t 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) { - hard_assert(t && t->parent, ERR_INVAL); + hard_assert(t && is_proc(t), ERR_INVAL); stat_t status = OK; const vm_t v = alloc_region(&t->sp_r, size, &size, @@ -86,13 +109,9 @@ stat_t free_uvmem(struct tcb *t, vm_t va) if (!m) return -1; - pm_t pa = __addr(m->end - m->start); - - vmflags_t flags = m->flags; free_region(&t->sp_r, va); - stat_t status = OK; - unmap_freed_region(t->b_r, va, pa, flags, &status); + stat_t status = __free_mapped_region(t, m); if (status == INFO_SEFF) return clone_tcb_maps(t); @@ -107,7 +126,10 @@ stat_t alloc_uvmem_wrapper(struct vm_branch *b, pm_t *offset, vm_t vaddr, return INFO_TRGN; /* try again */ stat_t *status = (stat_t *)data, ret; - ret = *status = map_vpage(b, *offset, vaddr, flags, order); + ret = map_vpage(b, *offset, vaddr, flags, order); + if (status) + *status = ret; + return (ret == INFO_SEFF) ? OK : ret; } @@ -120,7 +142,9 @@ stat_t alloc_shared_wrapper(struct vm_branch *b, pm_t *offset, vm_t vaddr, *offset = alloc_page(MM_O0, *offset); stat_t *status = (stat_t *)data, ret; - ret = *status = map_vpage(b, *offset, vaddr, flags, order); + ret = map_vpage(b, *offset, vaddr, flags, order); + if (status) + *status = ret; return (ret == INFO_SEFF) ? OK : ret; } @@ -137,7 +161,10 @@ stat_t free_uvmem_wrapper(struct vm_branch *b, pm_t *offset, vm_t vaddr, return INFO_TRGN; stat_t *status = (stat_t *)data, ret; - ret = *status = unmap_vpage(b, vaddr); + ret = unmap_vpage(b, vaddr); + if (status) + *status = ret; + /* don't free shared pages, unless they're owned */ if (!__is_set(flags, MR_SHARED) || __is_set(flags, MR_OWNED)) free_page(order, paddr); diff --git a/include/apos/assert.h b/include/apos/assert.h index 7200fd2..f3d9a4d 100644 --- a/include/apos/assert.h +++ b/include/apos/assert.h @@ -8,24 +8,25 @@ #if !defined(DNDEBUG) #define catastrophic_assert(x) \ do { \ - if (x) { \ - err("catastrophic assertion failed: %s\n", QUOTE(x)); \ + if (unlikely(!(x))) { \ + error("catastrophic assertion failed: %s\n", \ + QUOTE(x)); \ while (1) \ ; \ } \ } while (0); #define hard_assert(x, r) \ - do { \ - if (x) { \ + { \ + if (unlikely(!(x))) { \ warn("hard assertion failed: %s\n", QUOTE(x)); \ return r; \ } \ - } while (0); + } #define soft_assert(x) \ do { \ - if (x) { \ + if (unlikely(!(x))) { \ info("soft assertion failed: %s\n", QUOTE(x)); \ } \ } while (0); diff --git a/include/apos/mem_regions.h b/include/apos/mem_regions.h index efe0592..9f09eff 100644 --- a/include/apos/mem_regions.h +++ b/include/apos/mem_regions.h @@ -7,10 +7,12 @@ #include #define mem_container(ptr) container_of(ptr, struct mem_region, sp_n) +#define is_region_used(r) __is_set(r->flags, MR_USED) struct mem_region_root { struct sp_root free_regions; struct sp_root used_regions; + struct mem_region *first; }; struct mem_region { @@ -26,7 +28,7 @@ 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); +stat_t destroy_region(struct mem_region_root *r); vm_t alloc_region(struct mem_region_root *r, size_t size, size_t *actual_size, vmflags_t flags); @@ -35,6 +37,7 @@ vm_t alloc_fixed_region(struct mem_region_root *r, vm_t start, size_t size, 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_first_region(struct mem_region_root *r); 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, vm_t start); diff --git a/include/apos/syscalls.h b/include/apos/syscalls.h index 9346da3..e7b1cd5 100644 --- a/include/apos/syscalls.h +++ b/include/apos/syscalls.h @@ -25,7 +25,8 @@ enum { * structure of the OS, but these following syscalls are probably * required */ SYS_IPC_SERVER, /* inform kernel that process should be treated as a server */ - SYS_IPC_REQ, /* IPC request to server */ + SYS_IPC_REQ_PROC, /* IPC request to server on behalf of process */ + SYS_IPC_REQ_THREAD, /* IPC request to server on behalf of thread */ SYS_IPC_RESP, /* IPC response from server */ /* process management */ diff --git a/include/apos/tcb.h b/include/apos/tcb.h index 11e1b5a..f31bf77 100644 --- a/include/apos/tcb.h +++ b/include/apos/tcb.h @@ -5,14 +5,24 @@ #include #include /* arch-specific data */ +/* process(/main) threads don't have any previous threads */ +#define is_proc(t) (!t->prev) + struct tcb { struct arch_tcbd tcbd; /* mapping data * TODO: should mem_region_root be renamed mem_root or something? feels * kind of clunky */ - struct mem_region_root sp_r; - + union { + /* if we're the main thread, we control the memory regions */ + struct mem_region_root sp_r; + /* if we're not the main thread, we have a pointer to the main + * thread */ + struct tcb *proc; + }; + + id_t pid; id_t tid; vm_t callback; @@ -29,15 +39,18 @@ struct tcb { /* vm root branch */ struct vm_branch *b_r; - struct tcb *parent; + /* linked list of threads in this process */ struct tcb *next; + struct tcb *prev; }; void init_tcbs(); void destroy_tcbs(); -struct tcb *new_thread(); -void destroy_thread(struct tcb *); +struct tcb *create_thread(struct tcb *p); +struct tcb *create_proc(struct tcb *p); +stat_t destroy_thread(struct tcb *t); +stat_t destroy_proc(struct tcb *p); struct tcb *cur_tcb(); void use_tcb(struct tcb *); diff --git a/include/apos/uapi.h b/include/apos/uapi.h index a3b54d5..b3d345f 100644 --- a/include/apos/uapi.h +++ b/include/apos/uapi.h @@ -94,7 +94,8 @@ SYSCALL_DECLARE(free_timer); /* ipc */ SYSCALL_DECLARE(ipc_server); -SYSCALL_DECLARE(ipc_req); +SYSCALL_DECLARE(ipc_req_proc); +SYSCALL_DECLARE(ipc_req_thread); SYSCALL_DECLARE(ipc_resp); /* proc */ diff --git a/include/apos/vmem.h b/include/apos/vmem.h index f1b0d19..036780f 100644 --- a/include/apos/vmem.h +++ b/include/apos/vmem.h @@ -12,7 +12,9 @@ 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 destroy_uvmem(struct tcb *r); stat_t alloc_uvmem_wrapper(struct vm_branch *b, pm_t *offset, vm_t vaddr, vmflags_t flags, enum mm_order order, void *data); diff --git a/include/arch/vmem.h b/include/arch/vmem.h index 74b0aa5..5197c67 100644 --- a/include/arch/vmem.h +++ b/include/arch/vmem.h @@ -16,13 +16,15 @@ stat_t stat_vpage(struct vm_branch *branch, vm_t vaddr, pm_t *paddr, void flush_tlb(); void flush_tlb_all(); -void populate_root_branch(struct vm_branch *b); +stat_t populate_kvmem(struct vm_branch *b); struct vm_branch *init_vmem(void *fdt); #if defined(DEBUG) vm_t setup_kernel_io(struct vm_branch *b, vm_t paddr); #endif -stat_t clone_vmbranch(struct vm_branch *, struct vm_branch *); +struct vm_branch *create_vmem(); +stat_t destroy_vmem(struct vm_branch *); +stat_t clone_uvmem(struct vm_branch *, struct vm_branch *); #endif /* APOS_ARCH_PAGES_H */ -- cgit v1.3