From 378e8aa159caea08e869789ae40f73cea1480322 Mon Sep 17 00:00:00 2001
From: Avi Kivity <avi@cloudius-systems.com>
Date: Sun, 3 Mar 2013 18:32:42 +0200
Subject: [PATCH] sched: preemption

Split the core scheduler into a function for calling from interrupts, and
a wrapper for calling it from normal paths.  Call the preemptible path from
interrupt handlers.
---
 arch/x64/exceptions.cc |  4 ++++
 core/sched.cc          | 42 ++++++++++++++++++++++++++++--------------
 include/sched.hh       |  2 ++
 3 files changed, 34 insertions(+), 14 deletions(-)

diff --git a/arch/x64/exceptions.cc b/arch/x64/exceptions.cc
index d0351ce43..8ba6e909a 100644
--- a/arch/x64/exceptions.cc
+++ b/arch/x64/exceptions.cc
@@ -3,6 +3,7 @@
 #include "processor.hh"
 #include "interrupt.hh"
 #include <boost/format.hpp>
+#include "sched.hh"
 #include "debug.hh"
 
 typedef boost::format fmt;
@@ -117,7 +118,10 @@ void interrupt(exception_frame* frame)
     unsigned vector = frame->error_code;
     idt.invoke_interrupt(vector);
     processor::wrmsr(0x80b, 0); // EOI
+    // must call scheduler after EOI, or it may switch contexts and miss the EOI
     current_interrupt_frame = nullptr;
+    // FIXME: layering violation
+    sched::preempt();
 }
 
 #define DUMMY_HANDLER(x) \
diff --git a/core/sched.cc b/core/sched.cc
index 51d4bf891..5656dd398 100644
--- a/core/sched.cc
+++ b/core/sched.cc
@@ -20,8 +20,6 @@ unsigned __thread preempt_counter;
 
 elf::tls_data tls;
 
-// currently the scheduler will poll right after an interrupt, so no
-// need to do anything.
 inter_processor_interrupt wakeup_ipi{[] {}};
 
 }
@@ -44,23 +42,29 @@ void cpu::schedule(bool yield)
 {
     // FIXME: drive by IPI
     handle_incoming_wakeups();
-    thread* p = thread::current();
-    if (p->_status.load() == thread::status::running && !yield) {
-        return;
-    }
 
     with_lock(irq_lock, [this] {
-        assert(!runqueue.empty());
-        auto n = &runqueue.front();
-        runqueue.pop_front();
-        assert(n->_status.load() == thread::status::queued);
-        n->_status.store(thread::status::running);
-        if (n != thread::current()) {
-            n->switch_to();
-        }
+        reschedule_from_interrupt();
     });
 }
 
+void cpu::reschedule_from_interrupt()
+{
+    handle_incoming_wakeups();
+    thread* p = thread::current();
+    if (p->_status == thread::status::running) {
+        p->_status.store(thread::status::queued);
+        runqueue.push_back(*p);
+    }
+    auto n = &runqueue.front();
+    runqueue.pop_front();
+    assert(n->_status.load() == thread::status::queued);
+    n->_status.store(thread::status::running);
+    if (n != thread::current()) {
+        n->switch_to();
+    }
+}
+
 void cpu::do_idle()
 {
     do {
@@ -261,6 +265,9 @@ void thread::wake()
     // FIXME: warrant an interruption
     if (_cpu != current()->tcpu()) {
         wakeup_ipi.send(_cpu);
+    } else if (arch::irq_enabled()) {
+        _cpu->schedule();
+        // We'll also reschedule at the end of an interrupt if needed
     }
 }
 
@@ -355,6 +362,13 @@ void preempt_enable()
     // FIXME: may need to schedule() here if a high prio thread is waiting
 }
 
+void preempt()
+{
+    if (!preempt_counter) {
+        sched::cpu::current()->reschedule_from_interrupt();
+    }
+}
+
 timer_list::callback_dispatch::callback_dispatch()
 {
     clock_event->set_callback(this);
diff --git a/include/sched.hh b/include/sched.hh
index db737a0f3..200413c72 100644
--- a/include/sched.hh
+++ b/include/sched.hh
@@ -266,8 +266,10 @@ struct cpu {
     void do_idle();
     void load_balance();
     unsigned load();
+    void reschedule_from_interrupt();
 };
 
+void preempt();
 void preempt_disable();
 void preempt_enable();
 
-- 
GitLab