dedicated kthread interface and enablement of lrud auto-recycler
[lunaix-os.git] / lunaix-os / kernel / process / thread.c
index ef207aa0be82ccc9404f60210ebb58675d6aa768..f4bbac04cb0a2024c6c869e972f26757028e1f77 100644 (file)
@@ -5,11 +5,12 @@
 #include <lunaix/mm/mmap.h>
 #include <lunaix/mm/page.h>
 #include <lunaix/syslog.h>
+#include <lunaix/kpreempt.h>
 
 #include <usr/lunaix/threads.h>
 
-#include <sys/abi.h>
-#include <sys/mm/mm_defs.h>
+#include <asm/abi.h>
+#include <asm/mm_defs.h>
 
 LOG_MODULE("THREAD")
 
@@ -49,16 +50,18 @@ __alloc_user_thread_stack(struct proc_info* proc,
 static ptr_t
 __alloc_kernel_thread_stack(struct proc_info* proc, ptr_t vm_mnt)
 {
-    pfn_t kstack_top = leaf_count(KSTACK_AREA_END);
+    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 + 1;
+        ptep -= KSTACK_PAGES;
 
-        pte_t pte = pte_at(ptep + 1);
+        pte_t pte = pte_at(ptep);
         if (pte_isnull(pte)) {
             goto found;
         }
+
+        ptep--;
     }
 
     WARN("failed to create kernel stack: max stack num reach\n");
@@ -73,13 +76,42 @@ found:;
         return 0;
     }
 
-    set_pte(ptep, guard_pte);
-    ptep_map_leaflet(ptep + 1, mkpte_prot(KERNEL_DATA), leaflet);
+    set_pte(ptep++, guard_pte);
+    ptep_map_leaflet(ptep, mkpte_prot(KERNEL_DATA), leaflet);
 
     ptep += KSTACK_PAGES;
     return align_stack(ptep_va(ptep, LFT_SIZE) - 1);
 }
 
+static int
+__thread_putsleep(int seconds)
+{
+    if (!seconds) {
+        return 0;
+    }
+
+    struct scheduler* sched;
+    time_t systime;
+    struct haybed* bed;
+
+    sched = scheduler();
+    systime = clock_systime() / 1000;
+    bed = &current_thread->sleep;
+
+    if (bed->wakeup_time) {
+        return (bed->wakeup_time - systime);
+    }
+
+    bed->wakeup_time = systime + seconds;
+
+    if (llist_empty(&bed->sleepers)) {
+        llist_append(&sched->sleepers, &bed->sleepers);
+    }
+
+    block_current_thread();
+    return seconds;
+}
+
 void
 thread_release_mem(struct thread* thread)
 {
@@ -93,7 +125,7 @@ thread_release_mem(struct thread* thread)
     pte_t* ptep = mkptep_va(vm_mnt, thread->kstack);
     leaflet = pte_leaflet(*ptep);
     
-    ptep -= KSTACK_PAGES - 1;
+    ptep -= KSTACK_PAGES;
     set_pte(ptep, null_pte);
     ptep_unmap_leaflet(ptep + 1, leaflet);
 
@@ -185,9 +217,79 @@ thread_find(struct proc_info* proc, tid_t tid)
     return NULL;
 }
 
+void
+thread_stats_update(bool inbound, bool voluntary)
+{
+    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;
+}
+
+void
+kthread_spawn(ptr_t entry)
+{
+    assert(kernel_process(__current));
+
+    struct thread* th = create_thread(__current, false);
+    
+    assert(th);
+    start_thread(th, entry);
+    detach_thread(th);
+}
+
+void
+kthread_sleep(int seconds)
+{
+    if (__thread_putsleep(seconds))
+        yield_current();
+}
+
 __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;
@@ -232,13 +334,14 @@ __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;
     }
 
+    no_preemption();
     destory_thread(th);
 
     return 0;
@@ -277,3 +380,16 @@ __DEFINE_LXSYSCALL2(int, th_kill, tid_t, tid, int, signum)
     
     return 0;
 }
+
+__DEFINE_LXSYSCALL1(unsigned int, sleep, unsigned int, seconds)
+{
+    int sec;
+
+    sec = __thread_putsleep(seconds);
+    store_retval(seconds);
+    
+    if (sec)
+        schedule();
+
+    return 0;
+}
\ No newline at end of file