From 764376196a1169edd9ef5b84d970fd5e393c784f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=BE=90=E6=AD=A5=E9=AA=A5?= <xubuji20@mails.ucas.ac.cn>
Date: Tue, 1 Aug 2023 18:05:23 +0800
Subject: [PATCH] fix: do swap before memory is used out

---
 kernel/include/mem.h |  3 +++
 kernel/irq/irq.cpp   |  9 ++++++---
 kernel/mem/mem.cpp   | 20 ++++++++++++++++++--
 3 files changed, 27 insertions(+), 5 deletions(-)

diff --git a/kernel/include/mem.h b/kernel/include/mem.h
index 7e2cb02..312744d 100644
--- a/kernel/include/mem.h
+++ b/kernel/include/mem.h
@@ -62,6 +62,7 @@ private:
     SpinLock m_lock;
     size_t m_max_order;
     Slice<Node *> m_lists;
+    size_t free_mem_size;
 
     void remove_node(Node *node, size_t order);
     void insert_node(Node *node, size_t order);
@@ -74,6 +75,8 @@ protected:
     void __free(void *ptr, size_t len) override;
 
 public:
+    int in_swap_process = 0;
+
     BuddyAllocator() = default;
 
     void init(Slice<byte> buffer);
diff --git a/kernel/irq/irq.cpp b/kernel/irq/irq.cpp
index ff10198..37eca1a 100644
--- a/kernel/irq/irq.cpp
+++ b/kernel/irq/irq.cpp
@@ -56,14 +56,18 @@ void handle_exc_page_fault(Context *c)
         // try kill process
         exit(1);
     }
+    uintptr_t page_aligned_stval = c->stval & ~0xfff;
     if (c->scause == EXC_INST_PAGE_FAULT) {
-        // TODO: swap
-        // assert(0);
+        if (is_swapped_page(hart[hartid].proc, page_aligned_stval)) {
+            do_swap_in(hart[hartid].proc, page_aligned_stval);
+            return;
+        }
         fmt::warn("Inst Page Fault: kill {}({})\n", hart[hartid].proc->pid, hart[hartid].proc->name);
         dump_context(c);
         exit(1);
         return;
     }
+    ensure_user_addr((uintptr_t)c->sepc & ~0xfff, 1);
     uint16_t inst_lo = *((uint16_t *)c->sepc);
     uint16_t inst_hi = *((uint16_t *)c->sepc + 2);
     uint32_t inst = ((uint32_t)inst_hi << 16) | inst_lo; // FIXME: compressed inst on page boundary
@@ -125,7 +129,6 @@ void handle_exc_page_fault(Context *c)
     }
     uint32_t opcode = inst & 0x7f;
     uint32_t amo_func = (inst & 0xf8000000) >> 27;
-    uintptr_t page_aligned_stval = c->stval & ~0xfff;
     if (opcode == 0x3 || (opcode == 0x2f && amo_func == 0x10)) /* load */
     {
         if (is_swapped_page(hart[hartid].proc, page_aligned_stval)) {
diff --git a/kernel/mem/mem.cpp b/kernel/mem/mem.cpp
index c2947b3..f93c41f 100644
--- a/kernel/mem/mem.cpp
+++ b/kernel/mem/mem.cpp
@@ -60,6 +60,7 @@ Optional<void *> BuddyAllocator::__try_alloc(size_t size, size_t alignment) {
             power <<= 1;
             order++;
         }
+        free_mem_size -= PAGE_SIZE << order;
 
         size_t order2 = order;
         size_t power2 = power;
@@ -83,11 +84,16 @@ Optional<void *> BuddyAllocator::__try_alloc(size_t size, size_t alignment) {
 }
 
 Optional<void *> BuddyAllocator::__alloc(size_t size, size_t alignment) {
+    pcb_t *proc = get_current_proc();
+    if (in_swap_process == 0 && free_mem_size <= 8 * LARGE_PAGE_SIZE) {
+        for (size_t npage = 1; npage <= 512; npage++) {
+            if (do_swap_out_proc(proc, 1) == 0) break;
+        }
+    }
     auto result = __try_alloc(size, alignment);
     if (result.has_value()) return result.value();
-    pcb_t *proc = get_current_proc();
     for (size_t npage = 1; npage <= 512; npage++) {
-        do_swap_out_proc(proc, 1);
+        if (do_swap_out_proc(proc, 1) == 0) break;
         result = __try_alloc(size, alignment);
         if (result.has_value()) return result.value();
     }
@@ -96,6 +102,7 @@ Optional<void *> BuddyAllocator::__alloc(size_t size, size_t alignment) {
 
 void BuddyAllocator::__free(void *ptr, size_t len) {
         LockGuard locked(&m_lock);
+        free_mem_size += len;
         size_t order = 0;
         size_t power = UNIT;
         while (power < len) {
@@ -158,6 +165,7 @@ void BuddyAllocator::init(Slice<byte> buffer) {
             free_mem = free_mem.slice(UNIT - align_offset, free_mem.size());
         }
         free_mem = free_mem.slice(0, free_mem.size() - free_mem.size() % UNIT);
+        free_mem_size = free_mem.size();
 
         set(m_lists, static_cast<Node *>(nullptr));
         size_t count = 0;
@@ -377,6 +385,8 @@ void do_swap_read(int blk_num, uintptr_t kvaddr) {
 }
 
 void do_swap_out(pcb_t *owner, uintptr_t vaddr) {
+    buddy_allocator->in_swap_process++;
+    
     assert(owner->user_page_list->m_value.find(vaddr) != owner->user_page_list->m_value.end());
     mcb_t *mcb = owner->user_page_list->m_value[vaddr];
     uintptr_t kvaddr = walk(owner->pgdir->m_value, vaddr).value();
@@ -393,9 +403,13 @@ void do_swap_out(pcb_t *owner, uintptr_t vaddr) {
         remap3(holder.second->pgdir->m_value, holder.first, 0, new_flags);
     }
     buddy_allocator->free(Slice<byte>((byte *)kvaddr, PAGE_SIZE));
+
+    buddy_allocator->in_swap_process--;
 }
 
 void do_swap_in(pcb_t *owner, uintptr_t vaddr) {
+    buddy_allocator->in_swap_process++;
+
     assert(owner->user_page_list->m_value.find(vaddr) != owner->user_page_list->m_value.end());
     mcb_t *mcb = owner->user_page_list->m_value[vaddr];
     assert(mcb->swapped_block > 0);
@@ -410,6 +424,8 @@ void do_swap_in(pcb_t *owner, uintptr_t vaddr) {
 #endif
     mcb->swapped_block = 0;
     mcb->swapped_flags = 0;
+
+    buddy_allocator->in_swap_process--;
 }
 
 // mode > 0, collect $mode pages
-- 
GitLab