Second Extended Filesystem (ext2) and other improvements (#33)
[lunaix-os.git] / lunaix-os / kernel / process / thread.c
index 97e37a2750db85df405abe568d253d3c6aaf84ce..493ed99cc61bd2e6102b571ec9a5b523b3a3661e 100644 (file)
@@ -4,9 +4,8 @@
 #include <lunaix/syscall_utils.h>
 #include <lunaix/mm/mmap.h>
 #include <lunaix/mm/page.h>
-#include <lunaix/mm/vmm.h>
-#include <lunaix/mm/pmm.h>
 #include <lunaix/syslog.h>
+#include <lunaix/kpreempt.h>
 
 #include <usr/lunaix/threads.h>
 
 
 LOG_MODULE("THREAD")
 
-static inline void 
-inject_guardian_page(ptr_t vm_mnt, ptr_t va)
-{
-    vmm_set_mapping(vm_mnt, PG_ALIGN(va), 0, 0, VMAP_GUARDPAGE);
-}
-
 static ptr_t
-__alloc_user_thread_stack(struct proc_info* proc, struct mm_region** stack_region, ptr_t vm_mnt)
+__alloc_user_thread_stack(struct proc_info* proc, 
+                          struct mm_region** stack_region, ptr_t vm_mnt)
 {
-    ptr_t th_stack_top = (proc->thread_count + 1) * USR_STACK_SIZE;
-    th_stack_top = ROUNDUP(USR_STACK_END - th_stack_top, MEM_PAGE);
+    ptr_t th_stack_top = (proc->thread_count + 1) * USR_STACK_SIZE_THREAD;
+    th_stack_top = ROUNDUP(USR_STACK_END - th_stack_top, PAGE_SIZE);
 
     struct mm_region* vmr;
     struct proc_mm* mm = vmspace(proc);
     struct mmap_param param = { .vms_mnt = vm_mnt,
                                 .pvms = mm,
-                                .mlen = USR_STACK_SIZE,
+                                .mlen = USR_STACK_SIZE_THREAD,
                                 .proct = PROT_READ | PROT_WRITE,
                                 .flags = MAP_ANON | MAP_PRIVATE,
                                 .type = REGION_TYPE_STACK };
 
-    int errno = mmap_user((void**)&th_stack_top, &vmr, th_stack_top, NULL, &param);
-
+    int errno;
+    
+    errno = mmap_user((void**)&th_stack_top, &vmr, th_stack_top, NULL, &param);
     if (errno) {
         WARN("failed to create user thread stack: %d", errno);
         return 0;
     }
 
-    // Pre-allocate a page contains stack top, to avoid immediate trap to kernel
-    //  upon thread execution
-    ptr_t pa = pmm_alloc_page(0);
-    ptr_t stack_top = align_stack(th_stack_top + USR_STACK_SIZE - 1);
-    if (likely(pa)) {
-        vmm_set_mapping(vm_mnt, PG_ALIGN(stack_top), 
-                        pa, region_ptattr(vmr), 0);
-    }
-
-    inject_guardian_page(vm_mnt, vmr->start);
+    pte_t* guardp = mkptep_va(vm_mnt, vmr->start);
+    set_pte(guardp, guard_pte);
 
     *stack_region = vmr;
 
+    ptr_t stack_top = align_stack(th_stack_top + USR_STACK_SIZE_THREAD - 1);
     return stack_top;
 }
 
 static ptr_t
 __alloc_kernel_thread_stack(struct proc_info* proc, ptr_t vm_mnt)
 {
-    v_mapping mapping;
-    ptr_t kstack = PG_ALIGN(KSTACK_AREA_END - KSTACK_SIZE);
-    while (kstack >= KSTACK_AREA) {
-        // first page in the kernel stack is guardian page
-        if (!vmm_lookupat(vm_mnt, kstack + MEM_PAGE, &mapping) 
-            || !PG_IS_PRESENT(mapping.flags)) 
-        {
-            break;
+    pfn_t kstack_top = pfn(KSTACK_AREA_END);
+    pfn_t kstack_end = pfn(KSTACK_AREA);
+    pte_t* ptep      = mkptep_pn(vm_mnt, kstack_top);
+    while (ptep_pfn(ptep) > kstack_end) {
+        ptep -= KSTACK_PAGES;
+
+        pte_t pte = pte_at(ptep);
+        if (pte_isnull(pte)) {
+            goto found;
         }
 
-        kstack -= KSTACK_SIZE;
+        ptep--;
     }
 
-    if (kstack < KSTACK_AREA) {
-        WARN("failed to create kernel stack: max stack num reach\n");
-        return 0;
-    }
+    WARN("failed to create kernel stack: max stack num reach\n");
+    return 0;
 
-    ptr_t pa = pmm_alloc_cpage(PN(KSTACK_SIZE) - 1, 0);
+found:;
+    unsigned int po = count_order(KSTACK_PAGES);
+    struct leaflet* leaflet = alloc_leaflet(po);
 
-    if (!pa) {
+    if (!leaflet) {
         WARN("failed to create kernel stack: nomem\n");
         return 0;
     }
 
-    inject_guardian_page(vm_mnt, kstack);
-    for (size_t i = MEM_PAGE, j = 0; i < KSTACK_SIZE; i+=MEM_PAGE, j+=MEM_PAGE) {
-        vmm_set_mapping(vm_mnt, kstack + i, pa + j, PG_PREM_RW, 0);
-    }
+    set_pte(ptep++, guard_pte);
+    ptep_map_leaflet(ptep, mkpte_prot(KERNEL_DATA), leaflet);
 
-    return align_stack(kstack + KSTACK_SIZE - 1);
+    ptep += KSTACK_PAGES;
+    return align_stack(ptep_va(ptep, LFT_SIZE) - 1);
 }
 
 void
-thread_release_mem(struct thread* thread, ptr_t vm_mnt)
+thread_release_mem(struct thread* thread)
 {
-    for (size_t i = 0; i < KSTACK_SIZE; i+=MEM_PAGE) {
-        ptr_t stack_page = PG_ALIGN(thread->kstack - i);
-        vmm_del_mapping(vm_mnt, stack_page);
-    }
+    struct leaflet* leaflet;
+    struct proc_mm* mm = vmspace(thread->process);
+    ptr_t vm_mnt = mm->vm_mnt;
+
+    // Ensure we have mounted
+    assert(vm_mnt);
+
+    pte_t* ptep = mkptep_va(vm_mnt, thread->kstack);
+    leaflet = pte_leaflet(*ptep);
+    
+    ptep -= KSTACK_PAGES;
+    set_pte(ptep, null_pte);
+    ptep_unmap_leaflet(ptep + 1, leaflet);
+
+    leaflet_return(leaflet);
     
     if (thread->ustack) {
         if ((thread->ustack->start & 0xfff)) {
@@ -112,8 +111,12 @@ thread_release_mem(struct thread* thread, ptr_t vm_mnt)
 }
 
 struct thread*
-create_thread(struct proc_info* proc, ptr_t vm_mnt, bool with_ustack)
+create_thread(struct proc_info* proc, bool with_ustack)
 {
+    struct proc_mm* mm = vmspace(proc);
+    assert(mm->vm_mnt);
+
+    ptr_t vm_mnt = mm->vm_mnt;
     struct mm_region* ustack_region = NULL;
     if (with_ustack && 
         !(__alloc_user_thread_stack(proc, &ustack_region, vm_mnt))) 
@@ -134,31 +137,34 @@ create_thread(struct proc_info* proc, ptr_t vm_mnt, bool with_ustack)
     
     th->kstack = kstack;
     th->ustack = ustack_region;
+    
+    if (ustack_region) {
+        th->ustack_top = align_stack(ustack_region->end - 1);
+    }
 
     return th;
 }
 
 void
-start_thread(struct thread* th, ptr_t vm_mnt, ptr_t entry)
+start_thread(struct thread* th, ptr_t entry)
 {
     assert(th && entry);
+    struct proc_mm* mm = vmspace(th->process);
+
+    assert(mm->vm_mnt);
     
-    struct transfer_context transfer;
+    struct hart_transition transition;
     if (!kernel_addr(entry)) {
         assert(th->ustack);
 
-        ptr_t ustack_top = align_stack(th->ustack->end - 1);
-        ustack_top -= 16;   // pre_allocate a 16 byte for inject parameter
-        thread_create_user_transfer(&transfer, th->kstack, ustack_top, entry);
-
-        th->ustack_top = ustack_top;
+        hart_user_transfer(&transition, th->kstack, th->ustack_top, entry);
     } 
     else {
-        thread_create_kernel_transfer(&transfer, th->kstack, entry);
+        hart_kernel_transfer(&transition, th->kstack, entry);
     }
 
-    inject_transfer_context(vm_mnt, &transfer);
-    th->intr_ctx = (isr_param*)transfer.inject;
+    install_hart_transition(mm->vm_mnt, &transition);
+    th->hstate = (struct hart_state*)transition.inject;
 
     commit_thread(th);
 }
@@ -182,21 +188,74 @@ thread_find(struct proc_info* proc, tid_t tid)
     return NULL;
 }
 
-__DEFINE_LXSYSCALL4(int, th_create, tid_t*, tid, struct uthread_info*, thinfo, 
-                                    void*, entry, void*, param)
+void
+thread_stats_update(bool inbound, bool voluntary)
 {
-    struct thread* th = create_thread(__current, VMS_SELF, true);
+    struct thread_stats* stats;
+    time_t now;
+
+    now   = clock_systime();
+    stats = &current_thread->stats;
+
+    stats->at_user = !kernel_context(current_thread->hstate);
+
+    if (!inbound) {
+        if (kernel_process(current_thread->process) || 
+            stats->at_user)
+        {
+            // exiting to user or kernel (kernel thread only), how graceful
+            stats->last_leave = now;
+        }
+        else {
+            // exiting to kernel, effectively reentry
+            stats->last_reentry = now;
+        }
+
+        stats->last_resume = now;
+        return;
+    }
+
+    stats->last_reentry = now;
+
+    if (!stats->at_user)
+    {
+        // entering from kernel, it is a kernel preempt
+        thread_stats_update_kpreempt();
+        return;
+    }
+
+    // entering from user space, a clean entrance.
+
+    if (!voluntary) {
+        stats->entry_count_invol++;
+    }
+    else {
+        stats->entry_count_vol++;
+    }
+
+    thread_stats_reset_kpreempt();
+    stats->last_entry = now;
+}
+
+__DEFINE_LXSYSCALL3(int, th_create, tid_t*, tid, 
+                        struct uthread_param*, thparam, void*, entry)
+{
+    no_preemption();
+
+    struct thread* th = create_thread(__current, true);
     if (!th) {
         return EAGAIN;
     }
 
-    start_thread(th, VMS_SELF, (ptr_t)entry);
+    ptr_t ustack_top;
+
+    ustack_top = th->ustack_top;
+    ustack_top = align_stack(ustack_top - sizeof(*thparam));
 
-    ptr_t ustack_top = th->ustack_top;
-    *((void**)ustack_top) = param;
+    memcpy((void*)ustack_top, thparam, sizeof(*thparam));
 
-    thinfo->th_stack_sz = region_size(th->ustack);
-    thinfo->th_stack_top = (void*)ustack_top;
+    th->ustack_top = ustack_top;
+    start_thread(th, (ptr_t)entry);
     
     if (tid) {
         *tid = th->tid;
@@ -227,14 +286,15 @@ __DEFINE_LXSYSCALL2(int, th_join, tid_t, tid, void**, val_ptr)
     }
 
     while (!proc_terminated(th)) {
-        sched_pass();
+        yield_current();
     }
 
     if (val_ptr) {
         *val_ptr = (void*)th->exit_val;
     }
 
-    destory_thread(VMS_SELF, th);
+    no_preemption();
+    destory_thread(th);
 
     return 0;
 }