dedicated kthread interface and enablement of lrud auto-recycler cor/lrud
authorLunaixsky <lunaixsky@qq.com>
Sun, 4 May 2025 18:39:46 +0000 (19:39 +0100)
committerLunaixsky <lunaixsky@qq.com>
Sun, 4 May 2025 18:39:46 +0000 (19:39 +0100)
* fix potential racing issue on async eviction

lunaix-os/includes/lunaix/blkbuf.h
lunaix-os/includes/lunaix/kthread.h [new file with mode: 0644]
lunaix-os/includes/lunaix/process.h
lunaix-os/includes/lunaix/sched.h
lunaix-os/kernel/block/blkbuf.c
lunaix-os/kernel/lrud.c
lunaix-os/kernel/lunad.c
lunaix-os/kernel/process/sched.c
lunaix-os/kernel/process/thread.c

index decf92fce72064ecc697b8d7ed3bcc205297d7d4..0a23e87e26703806b616899b684345d26dce5203 100644 (file)
@@ -24,6 +24,9 @@ struct blk_buf {
     bcobj_t cobj;
     struct llist_header dirty;
     struct blkio_req* breq;
+
+    unsigned int dirty_count;
+    mutex_t lock;
 };
 
 typedef void* bbuf_t;
diff --git a/lunaix-os/includes/lunaix/kthread.h b/lunaix-os/includes/lunaix/kthread.h
new file mode 100644 (file)
index 0000000..25fa4a4
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef __LUNAIX_KTHREAD_H
+#define __LUNAIX_KTHREAD_H
+
+#include <lunaix/types.h>
+#include <lunaix/threads.h>
+
+void
+kthread_spawn(ptr_t entry);
+
+void
+kthread_sleep(int seconds);
+
+#endif /* __LUNAIX_KTHREAD_H */
index 868a9fc290bc97a53ee3bac804b401cf87da54fd..d35c2340822805918165189b854d50a30319197d 100644 (file)
@@ -372,17 +372,6 @@ create_thread(struct proc_info* proc, bool with_ustack);
 void
 start_thread(struct thread* th, ptr_t entry);
 
-static inline void
-spawn_kthread(ptr_t entry) {
-    assert(kernel_process(__current));
-
-    struct thread* th = create_thread(__current, false);
-    
-    assert(th);
-    start_thread(th, entry);
-    detach_thread(th);
-}
-
 void 
 exit_thread(void* val);
 
index 4264307dfa9f20fc8e29e61d047ec4cccbab27c9..94afa72173cd800d035ff85a2e5cf615d8ad2121 100644 (file)
@@ -34,4 +34,11 @@ run(struct thread* thread);
 void
 cleanup_detached_threads();
 
+static inline struct scheduler*
+scheduler()
+{
+    extern struct scheduler sched_ctx;
+    return &sched_ctx;
+}
+
 #endif /* __LUNAIX_SCHEDULER_H */
index bb29150477632511e5e0fcee5875f40c1439cd7e..f72aea70053f3de30017b87d84dbd71a7118bf3f 100644 (file)
@@ -5,7 +5,9 @@
 #include <lunaix/syslog.h>
 #include <asm/muldiv64.h>
 
-LOG_MODULE("blkbuf")  
+LOG_MODULE("blkbuf")
+
+#define MAX_DIRTY_COUNT     16
 
 #define bb_cache_obj(bcache) \
             container_of(bcache, struct blkbuf_cache, cached)
@@ -22,6 +24,18 @@ __tolba(struct blkbuf_cache* cache, unsigned int blk_id)
                     cache->blkdev->blk_size);
 }
 
+static inline void
+__blkbuf_lock(struct blk_buf* buf)
+{
+    mutex_lock(&buf->lock);
+}
+
+static inline void
+__blkbuf_unlock(struct blk_buf* buf)
+{
+    mutex_unlock(&buf->lock);
+}
+
 static void
 __blkbuf_do_sync(struct bcache* bc, unsigned long tag, void* data)
 {
@@ -105,6 +119,7 @@ __blkbuf_take_slow_lockness(struct blkbuf_cache* bc, unsigned int block_id)
 
     // give dirty a know state
     llist_init_head(&buf->dirty);
+    mutex_init(&buf->lock);
 
     blkio_setread(req);
     blkio_bindctx(req, bc->blkdev->blkio);
@@ -169,6 +184,21 @@ blkbuf_put(bbuf_t buf)
     bcache_return(bbuf->cobj);
 }
 
+
+static void
+__schedule_sync_nolock(struct blk_buf* bbuf, bool wait)
+{
+    struct blkio_req* blkio;
+
+    blkio = bbuf->breq;
+
+    blkio_setwrite(blkio);
+    blkio_commit(blkio, wait ? BLKIO_WAIT : BLKIO_NOWAIT);
+
+    llist_delete(&bbuf->dirty);
+    bbuf->dirty_count = 0;
+}
+
 void
 blkbuf_dirty(bbuf_t buf)
 {
@@ -180,26 +210,21 @@ blkbuf_dirty(bbuf_t buf)
     bbuf = ((struct blk_buf*)buf);
     bc = bcache_holder_embed(bbuf->cobj, struct blkbuf_cache, cached);
     
-    mutex_lock(&bc->lock);
+    __blkbuf_lock(bbuf);
     
-    if (llist_empty(&bbuf->dirty)) {
+    if (llist_empty(&bbuf->dirty)) 
+    {
+        mutex_lock(&bc->lock);
         llist_append(&bc->dirty, &bbuf->dirty);
+        mutex_unlock(&bc->lock);
     }
 
-    mutex_unlock(&bc->lock);
-}
-
-static inline void
-__schedule_sync_event(struct blk_buf* bbuf, bool wait)
-{
-    struct blkio_req* blkio;
-
-    blkio = bbuf->breq;
-
-    blkio_setwrite(blkio);
-    blkio_commit(blkio, wait ? BLKIO_WAIT : BLKIO_NOWAIT);
+    bbuf->dirty_count++;
+    if (bbuf->dirty_count >= MAX_DIRTY_COUNT) {
+        __schedule_sync_nolock(bbuf, false);
+    }
 
-    llist_delete(&bbuf->dirty);
+    __blkbuf_unlock(bbuf);
 }
 
 void
@@ -208,7 +233,9 @@ blkbuf_schedule_sync(bbuf_t buf)
     struct blk_buf* bbuf;
     bbuf = to_blkbuf(buf);
 
-    __schedule_sync_event(bbuf, false);
+    __blkbuf_lock(bbuf);
+    __schedule_sync_nolock(bbuf, false);
+    __blkbuf_unlock(bbuf);
 }
 
 bool
@@ -218,8 +245,11 @@ blkbuf_syncall(struct blkbuf_cache* bc, bool async)
 
     mutex_lock(&bc->lock);
 
-    llist_for_each(pos, n, &bc->dirty, dirty) {
-        __schedule_sync_event(pos, !async);
+    llist_for_each(pos, n, &bc->dirty, dirty) 
+    {
+        __blkbuf_lock(pos);
+        __schedule_sync_nolock(pos, !async);
+        __blkbuf_unlock(pos);
     }
 
     mutex_unlock(&bc->lock);
index 0395e42da500b12fc04f54e46ea12ef77840fb71..137cfa632ba2c84a37e5559f34562f10ae8c6576 100644 (file)
@@ -3,16 +3,18 @@
 #include <lunaix/spike.h>
 #include <lunaix/fs/twimap.h>
 #include <lunaix/fs/twifs.h>
+#include <lunaix/kthread.h>
+#include <lunaix/owloysius.h>
 
 #include <klibc/string.h>
 
 static struct llist_header zone_lead = { .next = &zone_lead, .prev = &zone_lead };
 
 DEFINE_SPINLOCK_OPS(struct lru_zone*, lock);
-
+static DEFINE_SPINLOCK(zone_list_lock);
 
 static void
-__do_evict_lockless(struct lru_zone* zone, struct llist_header* elem)
+__do_evict_nolock(struct lru_zone* zone, struct llist_header* elem)
 {
     llist_delete(elem);
     if (!zone->try_evict(container_of(elem, struct lru_node, lru_nodes))) {
@@ -28,12 +30,16 @@ __do_evict_lockless(struct lru_zone* zone, struct llist_header* elem)
 }
 
 static void
-__lru_evict_all_lockness(struct lru_zone* zone)
+__lru_evict_all_nolock(struct lru_zone* zone)
 {
-    struct llist_header* tail = zone->lead_node.prev;
-    while (tail != &zone->lead_node) {
-        __do_evict_lockless(zone, tail);
+    struct llist_header* tail, *curr;
+
+    tail = zone->lead_node.prev;
+    while (tail != &zone->lead_node) 
+    {
+        curr = tail;
         tail = tail->prev;
+        __do_evict_nolock(zone, curr);
     }
 }
 
@@ -49,9 +55,12 @@ lru_new_zone(const char* name, evict_cb try_evict_cb)
 
     strncpy(zone->name, name, sizeof(zone->name) - 1);
     llist_init_head(&zone->lead_node);
-    llist_append(&zone_lead, &zone->zones);
     spinlock_init(&zone->lock);
 
+    spinlock_acquire(&zone_list_lock);
+    llist_append(&zone_lead, &zone->zones);
+    spinlock_release(&zone_list_lock);
+
     return zone;
 }
 
@@ -60,10 +69,12 @@ lru_free_zone(struct lru_zone* zone)
 {
     lock(zone);
 
-    __lru_evict_all_lockness(zone);
+    __lru_evict_all_nolock(zone);
 
-    if (llist_empty(&zone->lead_node)) {
+    if (llist_empty(&zone->lead_node)) 
+    {
         llist_delete(&zone->zones);
+        unlock(zone);
         vfree(zone);
         return;
     }
@@ -107,24 +118,31 @@ lru_evict_one(struct lru_zone* zone)
 
     struct llist_header* tail = zone->lead_node.prev;
     if (tail == &zone->lead_node) {
-        return;
+        goto done;
     }
 
-    __do_evict_lockless(zone, tail);
+    __do_evict_nolock(zone, tail);
 
+done:
     unlock(zone);
 }
 
 void
 lru_evict_half(struct lru_zone* zone)
 {
+    int target;
+    struct llist_header *tail, *curr;
+    
     lock(zone);
 
-    int target = (int)(zone->objects / 2);
-    struct llist_header* tail = zone->lead_node.prev;
+    target = (int)(zone->objects / 2);
+    tail = zone->lead_node.prev;
+
     while (tail != &zone->lead_node && target > 0) {
-        __do_evict_lockless(zone, tail);
+        curr = tail;
         tail = tail->prev;
+
+        __do_evict_nolock(zone, curr);
         target--;
     }
 
@@ -138,7 +156,7 @@ lru_evict_all(struct lru_zone* zone)
 {
     lock(zone);
     
-    __lru_evict_all_lockness(zone);
+    __lru_evict_all_nolock(zone);
 
     zone->evict_stats.n_full++;
 
@@ -148,16 +166,48 @@ lru_evict_all(struct lru_zone* zone)
 void
 lru_remove(struct lru_zone* zone, struct lru_node* node)
 {
-    lock(zone);
+    if (llist_empty(&node->lru_nodes)) 
+        return;
 
-    if (node->lru_nodes.next && node->lru_nodes.prev) {
-        llist_delete(&node->lru_nodes);
-    }
+    lock(zone);
+    
+    llist_delete(&node->lru_nodes);
     zone->objects--;
 
     unlock(zone);
 }
 
+
+static void
+__lru_pool_daemon()
+{
+    struct lru_zone *pos, *n;
+
+    while (true)
+    {
+        spinlock_acquire(&zone_list_lock);
+        
+        // TODO add a watermark check before doing eviction
+        llist_for_each(pos, n, &zone_lead, zones) {
+            lru_evict_half(pos);
+        }
+
+        spinlock_release(&zone_list_lock);
+
+        kthread_sleep(10);
+    }
+}
+
+static void
+__lru_pool_init()
+{
+    // TODO make sure other are thread-safe first
+
+    // kthread_spawn((ptr_t)__lru_pool_daemon);
+}
+owloysius_fetch_init(__lru_pool_init, on_postboot)
+
+
 static void
 __twimap_read_lru_pool(struct twimap* map)
 {
index b808022ab1f8f4ee10d1c34587bbd90a050b0a45..25ce7d21d0014c14a7479195216ac86d7b0839a5 100644 (file)
@@ -10,6 +10,7 @@
 #include <lunaix/sched.h>
 #include <lunaix/kpreempt.h>
 #include <lunaix/kcmd.h>
+#include <lunaix/kthread.h>
 
 #include <klibc/string.h>
 
@@ -84,6 +85,17 @@ lunad_do_usr() {
     }
 }
 
+static void
+__thread_cleaner()
+{
+    while (true)
+    {
+        cleanup_detached_threads();
+        kthread_sleep(30);
+    }
+}
+
+
 /**
  * @brief LunaixOS的内核进程,该进程永远为可执行。
  *
@@ -95,23 +107,11 @@ lunad_do_usr() {
 void
 lunad_main()
 {
-    spawn_kthread((ptr_t)init_platform);
-
-    /*
-        NOTE Kernel preemption after this point.
+    kthread_spawn((ptr_t)init_platform);
+    kthread_spawn((ptr_t)__thread_cleaner);
 
-        More specifically, it is not a real kernel preemption (as in preemption
-        happened at any point of kernel, except those marked explicitly).
-        In Lunaix, things are designed in an non-preemptive fashion, we implement 
-        kernel preemption the other way around: only selected kernel functions which,
-        of course, with great care of preemptive assumption, will goes into kernel 
-        thread (which is preemptive!)
-    */
-
-    set_preemption();
     while (1)
     {
-        cleanup_detached_threads();
         yield_current();
     }
 }
index 112741c553de299db541357d39baca3487d9f373..ccf0e45aac1935ace616b2800188521f1e2263df 100644 (file)
@@ -224,33 +224,6 @@ done:
     fail("unexpected return from scheduler");
 }
 
-__DEFINE_LXSYSCALL1(unsigned int, sleep, unsigned int, seconds)
-{
-    if (!seconds) {
-        return 0;
-    }
-
-    time_t systime = clock_systime() / 1000;
-    struct haybed* 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_ctx.sleepers, &bed->sleepers);
-    }
-
-    store_retval(seconds);
-
-    block_current_thread();
-    schedule();
-
-    return 0;
-}
-
 __DEFINE_LXSYSCALL1(unsigned int, alarm, unsigned int, seconds)
 {
     struct haybed* bed = &current_thread->sleep;
@@ -396,7 +369,7 @@ alloc_process()
     proc->root = vfs_sysroot;
 
     proc->sigreg = vzalloc(sizeof(struct sigregistry));
-    proc->fdtable = vzalloc(sizeof(struct v_fdtable));
+    proc->fdtable = fdtable_create();
 
     proc->mm = procvm_create(proc);
     
@@ -632,4 +605,5 @@ orphaned_proc(pid_t pid)
     // 如果其父进程的状态是terminated 或 destroy中的一种
     // 或者其父进程是在该进程之后创建的,那么该进程为孤儿进程
     return proc_terminated(parent) || parent->created > proc->created;
-}
\ No newline at end of file
+}
+
index 78cce8b53b89c8ff323b729af407289cc8a558c3..f4bbac04cb0a2024c6c869e972f26757028e1f77 100644 (file)
@@ -83,6 +83,35 @@ found:;
     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)
 {
@@ -237,6 +266,25 @@ thread_stats_update(bool inbound, bool voluntary)
     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)
 {
@@ -332,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