Jelajahi Sumber

feat: set ap cpus online in scheduler!

There are still some bugs waiting to be fixed. The biggest of all is
that multiple processes call `exit` at the same time will cause
deadlock. This is because we hold `current.inner`, then `init.inner` and
then `child.inner`. And if our children are doing `exit` simultaneously,
they might hold their `current.inner` first and spin on `init.inner`.

This causes the deadlock.
greatbridf 3 minggu lalu
induk
melakukan
68afbfce01

+ 0 - 3
include/kernel/async/lock.hpp

@@ -26,9 +26,6 @@ class mutex {
     mutex(const mutex&) = delete;
     ~mutex();
 
-    void lock();
-    void unlock();
-
     lock_context_t lock_irq();
     void unlock_irq(lock_context_t state);
 };

+ 22 - 5
src/kernel/arch/x86_64/init.rs

@@ -4,11 +4,14 @@ use crate::{
         arch::interrupt::APIC_BASE,
         mem::{paging::Page, phys::PhysPtr as _},
         smp,
+        task::{ProcessList, Scheduler, Thread},
     },
     println_debug, println_info,
     sync::preempt,
 };
+use alloc::{format, sync::Arc};
 use arch::{
+    interrupt,
     task::pause,
     x86_64::{gdt::GDT, task::TSS},
 };
@@ -71,14 +74,28 @@ pub static BOOT_STACK: AtomicUsize = AtomicUsize::new(0);
 pub static CPU_COUNT: AtomicUsize = AtomicUsize::new(0);
 
 #[no_mangle]
-pub unsafe extern "C" fn ap_entry(_stack_start: u64) {
+pub unsafe extern "C" fn ap_entry(stack_start: u64) {
     init_cpu();
 
-    // TODO!!!!!: Set up idle task.
+    let idle_process = ProcessList::get()
+        .try_find_process(0)
+        .expect("Idle process must exist");
 
-    // TODO!!!!!: Free the stack before switching to idle task.
+    let idle_thread_name = format!("[kernel idle#AP{}]", 0);
+    let idle_thread = Thread::new_for_init(Arc::from(idle_thread_name.as_bytes()), &idle_process);
+    ProcessList::get().add_thread(&idle_thread);
+    Scheduler::set_idle(idle_thread.clone());
+    Scheduler::set_current(idle_thread);
 
-    loop {}
+    preempt::disable();
+    interrupt::enable();
+
+    // TODO!!!!!: Free the stack after having switched to idle task.
+    arch::task::context_switch_light(
+        stack_start as *mut _, // We will never come back
+        unsafe { Scheduler::idle_task().get_sp_ptr() },
+    );
+    arch::task::freeze()
 }
 
 pub unsafe fn bootstrap_cpus() {
@@ -94,7 +111,7 @@ pub unsafe fn bootstrap_cpus() {
         pause();
     }
 
-    while CPU_COUNT.load(Ordering::Acquire) != 3 {
+    while CPU_COUNT.load(Ordering::Acquire) != 4 {
         if BOOT_STACK.load(Ordering::Acquire) == 0 {
             let page = Page::alloc_many(9);
             let stack_start = page.as_cached().as_ptr::<()>() as usize;

+ 21 - 8
src/kernel/task/scheduler.rs

@@ -1,6 +1,6 @@
 use core::{
     ptr::NonNull,
-    sync::atomic::{compiler_fence, Ordering},
+    sync::atomic::{compiler_fence, fence, Ordering},
 };
 
 use crate::{prelude::*, sync::preempt};
@@ -58,7 +58,7 @@ impl Scheduler {
         BorrowedArc::from_raw(IDLE_TASK.get().unwrap().as_ptr())
     }
 
-    pub(super) unsafe fn set_idle(thread: Arc<Thread>) {
+    pub unsafe fn set_idle(thread: Arc<Thread>) {
         thread.prepare_kernel_stack(|kstack| {
             let mut writer = kstack.get_writer();
             writer.flags = 0x200;
@@ -71,10 +71,13 @@ impl Scheduler {
         assert!(old.is_none(), "Idle task is already set");
     }
 
-    pub(super) unsafe fn set_current(thread: Arc<Thread>) {
+    pub unsafe fn set_current(thread: Arc<Thread>) {
+        assert_eq!(thread.oncpu.swap(true, Ordering::AcqRel), false);
         let old = CURRENT.swap(NonNull::new(Arc::into_raw(thread) as *mut _));
+
         if let Some(thread_pointer) = old {
-            Arc::from_raw(thread_pointer.as_ptr());
+            let thread = Arc::from_raw(thread_pointer.as_ptr());
+            thread.oncpu.store(false, Ordering::Release);
         }
     }
 
@@ -94,8 +97,12 @@ impl Scheduler {
         let mut state = thread.state.lock();
         assert_eq!(*state, ThreadState::USleep);
 
-        *state = ThreadState::Ready;
-        self.enqueue(&thread);
+        if thread.oncpu.load(Ordering::Acquire) {
+            *state = ThreadState::Running;
+        } else {
+            *state = ThreadState::Ready;
+            self.enqueue(&thread);
+        }
     }
 
     pub fn isleep(&mut self, thread: &Arc<Thread>) {
@@ -112,8 +119,12 @@ impl Scheduler {
         match *state {
             ThreadState::Ready | ThreadState::Running | ThreadState::USleep => return,
             ThreadState::ISleep => {
-                *state = ThreadState::Ready;
-                self.enqueue(&thread);
+                if thread.oncpu.load(Ordering::Acquire) {
+                    *state = ThreadState::Running;
+                } else {
+                    *state = ThreadState::Ready;
+                    self.enqueue(&thread);
+                }
             }
             state => panic!("Invalid transition from state {:?} to `Ready`", state),
         }
@@ -229,6 +240,8 @@ extern "C" fn idle_task() {
         //
         // The other cpu should see the changes of kernel stack of the target thread
         // made in this cpu.
+        fence(Ordering::SeqCst);
         context_switch_light(&Scheduler::idle_task(), &Thread::current());
+        fence(Ordering::SeqCst);
     }
 }

+ 12 - 11
src/kernel/task/thread.rs

@@ -1,15 +1,12 @@
 use core::{
     cell::RefCell,
     cmp,
-    sync::atomic::{self, AtomicU32},
+    sync::atomic::{self, AtomicBool, AtomicU32},
 };
 
 use crate::{
     kernel::{
-        arch::user::TLS,
-        mem::{phys::PhysPtr, MMList},
-        terminal::Terminal,
-        user::dataflow::CheckedUserPointer,
+        arch::user::TLS, mem::MMList, terminal::Terminal, user::dataflow::CheckedUserPointer,
         vfs::FsContext,
     },
     prelude::*,
@@ -20,7 +17,7 @@ use alloc::{
     collections::{btree_map::BTreeMap, vec_deque::VecDeque},
     sync::{Arc, Weak},
 };
-use bindings::{ECHILD, EINTR, EINVAL, EPERM, ESRCH};
+use bindings::{ECHILD, EINTR, EPERM, ESRCH};
 use lazy_static::lazy_static;
 
 use crate::kernel::vfs::filearray::FileArray;
@@ -209,6 +206,8 @@ pub struct Thread {
     /// Thread state for scheduler use.
     pub state: Spin<ThreadState>,
 
+    pub oncpu: AtomicBool,
+
     /// Kernel stack
     /// Never access this directly.
     ///
@@ -389,15 +388,15 @@ impl Drop for ProcessGroup {
 }
 
 lazy_static! {
-    static ref GLOBAL_PROC_LIST: ProcessList = {
+    static ref GLOBAL_PROC_LIST: ProcessList = unsafe {
         let init_process = Process::new_for_init(1, None);
         let init_thread = Thread::new_for_init(b"[kernel kinit]".as_slice().into(), &init_process);
-        unsafe { Scheduler::set_current(init_thread.clone()) };
+        Scheduler::set_current(init_thread.clone());
 
         let idle_process = Process::new_for_init(0, None);
         let idle_thread =
             Thread::new_for_init(b"[kernel idle#BS]".as_slice().into(), &idle_process);
-        unsafe { Scheduler::set_idle(idle_thread.clone()) };
+        Scheduler::set_idle(idle_thread.clone());
 
         let init_session_weak = Arc::downgrade(&init_process.inner.lock().session);
         let init_pgroup_weak = Arc::downgrade(&init_process.inner.lock().pgroup);
@@ -591,7 +590,7 @@ impl Process {
         process
     }
 
-    fn new_for_init(pid: u32, parent: Option<Arc<Self>>) -> Arc<Self> {
+    unsafe fn new_for_init(pid: u32, parent: Option<Arc<Self>>) -> Arc<Self> {
         let process = Arc::new_cyclic(|weak| {
             let session = Session::new(pid, weak.clone());
             let pgroup = ProcessGroup::new_for_init(pid, weak.clone(), Arc::downgrade(&session));
@@ -798,7 +797,7 @@ impl UserDescriptorFlags {
 }
 
 impl Thread {
-    fn new_for_init(name: Arc<[u8]>, process: &Arc<Process>) -> Arc<Self> {
+    pub unsafe fn new_for_init(name: Arc<[u8]>, process: &Arc<Process>) -> Arc<Self> {
         let thread = Arc::new(Self {
             tid: process.pid,
             process: process.clone(),
@@ -807,6 +806,7 @@ impl Thread {
             signal_list: SignalList::new(),
             kstack: RefCell::new(KernelStack::new()),
             state: Spin::new(ThreadState::Preparing),
+            oncpu: AtomicBool::new(false),
             inner: Spin::new(ThreadInner {
                 name,
                 tls: None,
@@ -836,6 +836,7 @@ impl Thread {
             signal_list,
             kstack: RefCell::new(KernelStack::new()),
             state: Spin::new(ThreadState::Preparing),
+            oncpu: AtomicBool::new(false),
             inner: Spin::new(ThreadInner {
                 name: other_inner.name.clone(),
                 tls: other_inner.tls.clone(),