From c2644adf3d37b68911df5087f641150d583c65fa Mon Sep 17 00:00:00 2001
From: Jeremy Soller <jackpot51@gmail.com>
Date: Tue, 5 Dec 2017 21:26:45 -0700
Subject: [PATCH] Improve multi_core support

---
 src/acpi/madt.rs                 |  2 +-
 src/arch/x86_64/idt.rs           |  1 +
 src/arch/x86_64/interrupt/ipi.rs | 12 ++++++++++++
 src/arch/x86_64/interrupt/irq.rs | 19 ++++++++++++-------
 src/context/context.rs           | 12 +++++++-----
 src/context/switch.rs            | 20 +++++++-------------
 src/lib.rs                       |  2 +-
 7 files changed, 41 insertions(+), 27 deletions(-)

diff --git a/src/acpi/madt.rs b/src/acpi/madt.rs
index 28f7671..e95c519 100644
--- a/src/acpi/madt.rs
+++ b/src/acpi/madt.rs
@@ -45,7 +45,7 @@ impl Madt {
                 println!("    XAPIC {}: {:>08X}", me, local_apic.address);
             }
 
-            if cfg!(feature = "multi_core"){
+            if cfg!(feature = "multi_core") {
                 let trampoline_frame = Frame::containing_address(PhysicalAddress::new(TRAMPOLINE));
                 let trampoline_page = Page::containing_address(VirtualAddress::new(TRAMPOLINE));
 
diff --git a/src/arch/x86_64/idt.rs b/src/arch/x86_64/idt.rs
index ef50732..f9b9859 100644
--- a/src/arch/x86_64/idt.rs
+++ b/src/arch/x86_64/idt.rs
@@ -60,6 +60,7 @@ pub unsafe fn init() {
 
     // Set IPI handler (null)
     IDT[0x40].set_func(ipi::ipi);
+    IDT[0x41].set_func(ipi::pit);
 
     // Set syscall function
     IDT[0x80].set_func(syscall::syscall);
diff --git a/src/arch/x86_64/interrupt/ipi.rs b/src/arch/x86_64/interrupt/ipi.rs
index 325d9d8..2f7177c 100644
--- a/src/arch/x86_64/interrupt/ipi.rs
+++ b/src/arch/x86_64/interrupt/ipi.rs
@@ -1,5 +1,17 @@
+use core::sync::atomic::Ordering;
+
+use context;
 use device::local_apic::LOCAL_APIC;
+use super::irq::PIT_TICKS;
 
 interrupt!(ipi, {
     LOCAL_APIC.eoi();
 });
+
+interrupt!(pit, {
+    LOCAL_APIC.eoi();
+
+    if PIT_TICKS.fetch_add(1, Ordering::SeqCst) >= 10 {
+        let _ = context::switch();
+    }
+});
diff --git a/src/arch/x86_64/interrupt/irq.rs b/src/arch/x86_64/interrupt/irq.rs
index 6cc5a15..b463fd3 100644
--- a/src/arch/x86_64/interrupt/irq.rs
+++ b/src/arch/x86_64/interrupt/irq.rs
@@ -1,10 +1,10 @@
-use context::timeout;
-use device::pic;
-use device::serial::{COM1, COM2};
 use core::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
 
-use time;
 use context;
+use context::timeout;
+use device::{local_apic, pic};
+use device::serial::{COM1, COM2};
+use time;
 
 //resets to 0 in context::switch()
 pub static PIT_TICKS: AtomicUsize = ATOMIC_USIZE_INIT;
@@ -39,6 +39,11 @@ pub unsafe fn acknowledge(irq: usize) {
 }
 
 interrupt!(pit, {
+    // Wake up other CPUs
+    if cfg!(feature = "multi_core") {
+        local_apic::LOCAL_APIC.set_icr(3 << 18 | 1 << 14 | 0x41);
+    }
+
     // Saves CPU time by not sending IRQ event irq_trigger(0);
 
     const PIT_RATE: u64 = 2250286;
@@ -52,12 +57,12 @@ interrupt!(pit, {
 
     pic::MASTER.ack();
 
+    // Any better way of doing this?
+    timeout::trigger();
+
     if PIT_TICKS.fetch_add(1, Ordering::SeqCst) >= 10 {
         let _ = context::switch();
     }
-
-    // Any better way of doing this?
-    timeout::trigger();
 });
 
 interrupt!(keyboard, {
diff --git a/src/context/context.rs b/src/context/context.rs
index b6b8d0e..6449869 100644
--- a/src/context/context.rs
+++ b/src/context/context.rs
@@ -231,11 +231,13 @@ impl Context {
     pub fn unblock(&mut self) -> bool {
         if self.status == Status::Blocked {
             self.status = Status::Runnable;
-            if let Some(cpu_id) = self.cpu_id {
-                if cpu_id != ::cpu_id() {
-                    // Send IPI if not on current CPU
-                    // TODO: Make this more architecture independent
-                    unsafe { device::local_apic::LOCAL_APIC.ipi(cpu_id) };
+            if cfg!(feature = "multi_core") {
+                if let Some(cpu_id) = self.cpu_id {
+                    if cpu_id != ::cpu_id() {
+                        // Send IPI if not on current CPU
+                        // TODO: Make this more architecture independent
+                        unsafe { device::local_apic::LOCAL_APIC.set_icr(3 << 18 | 1 << 14 | 0x40) };
+                    }
                 }
             }
             true
diff --git a/src/context/switch.rs b/src/context/switch.rs
index 0702474..859d573 100644
--- a/src/context/switch.rs
+++ b/src/context/switch.rs
@@ -42,14 +42,14 @@ pub unsafe fn switch() -> bool {
         }
 
         let check_context = |context: &mut Context| -> bool {
+            // Take ownership if not already owned
             if context.cpu_id == None {
                 context.cpu_id = Some(cpu_id);
                 // println!("{}: take {} {}", cpu_id, context.id, ::core::str::from_utf8_unchecked(&context.name.lock()));
             }
 
+            // Restore from signal
             if context.ksig_restore {
-                println!("Restore from ksig");
-
                 let ksig = context.ksig.take().expect("context::switch: ksig not set with ksig_restore");
                 context.arch = ksig.0;
                 if let Some(ref mut kfx) = context.kfx {
@@ -64,16 +64,15 @@ pub unsafe fn switch() -> bool {
                 }
                 context.ksig_restore = false;
 
-                //TODO: Interrupt
-                if context.status == Status::Blocked {
-                    context.unblock();
-                }
+                context.unblock();
             }
 
+            // Unblock when there are pending signals
             if context.status == Status::Blocked && !context.pending.is_empty() {
                 context.unblock();
             }
 
+            // Wake from sleep
             if context.status == Status::Blocked && context.wake.is_some() {
                 let wake = context.wake.expect("context::switch: wake not set");
 
@@ -84,13 +83,8 @@ pub unsafe fn switch() -> bool {
                 }
             }
 
-            if context.cpu_id == Some(cpu_id) {
-                if context.status == Status::Runnable && !context.running {
-                    return true;
-                }
-            }
-
-            false
+            // Switch to context if it needs to run, is not currently running, and is owned by the current CPU
+            !context.running && context.status == Status::Runnable && context.cpu_id == Some(cpu_id)
         };
 
         for (pid, context_lock) in contexts.iter() {
diff --git a/src/lib.rs b/src/lib.rs
index c6f40ee..3bf770d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -184,7 +184,7 @@ pub fn kmain(cpus: usize, env: &[u8]) -> ! {
 pub fn kmain_ap(id: usize) -> ! {
     CPU_ID.store(id, Ordering::SeqCst);
 
-    if cfg!(feature = "multi_core"){
+    if cfg!(feature = "multi_core") {
         context::init();
 
         let pid = syscall::getpid();
-- 
GitLab