[kernel] Schedule threads on other CPUs

Now that the other CPUs have been brought up, add support for scheduling
tasks on them. The scheduler now maintains separate ready/blocked lists
per CPU, and CPUs will attempt to balance load via periodic work
stealing.

Other changes as a result of this:
- The device manager no longer creates a local APIC object, but instead
  just gathers relevant info from the APCI tables. Each CPU creates its
  own local APIC object. This also spurred the APIC timer calibration to
  become a static value, as all APICs are assumed to be symmetrical.
- Fixed a bug where the scheduler was popping the current task off of
  its ready list, however the current task is never on the ready list
  (except the idle task was first set up as both current and ready).
  This was causing the lists to get into bad states. Now a task can only
  ever be current or in a ready or blocked list.
- Got rid of the unused static process::s_processes list of all
  processes, instead of trying to synchronize it via locks.
- Added spinlocks for synchronization to the scheduler and logger
  objects.
This commit is contained in:
Justin C. Miller
2021-02-15 12:56:22 -08:00
parent 2a347942bc
commit f0025dbc47
17 changed files with 337 additions and 220 deletions

View File

@@ -3,7 +3,8 @@
/// The task scheduler and related definitions
#include <stdint.h>
#include "objects/thread.h"
#include "kutil/spinlock.h"
#include "kutil/vector.h"
namespace kernel {
namespace args {
@@ -14,6 +15,7 @@ struct cpu_data;
class lapic;
class process;
struct page_table;
struct run_queue;
/// The task scheduler
@@ -39,8 +41,9 @@ public:
static const uint16_t process_quanta = 10;
/// Constructor.
/// \arg apic The local APIC object for this CPU
scheduler(lapic &apic);
/// \arg cpus The number of CPUs to schedule for
scheduler(unsigned cpus);
~scheduler();
/// Create a new process from a program image in memory.
/// \arg program The descriptor of the pogram in memory
@@ -66,15 +69,11 @@ public:
/// Run the scheduler, possibly switching to a new task
void schedule();
/// Get the current TCB.
/// \returns A pointer to the current thread's TCB
inline TCB * current() { return m_current; }
/// Start scheduling a new thread.
/// \arg t The new thread's TCB
void add_thread(TCB *t);
/// Get a reference to the system scheduler
/// Get a reference to the scheduler
/// \returns A reference to the global system scheduler
static scheduler & get() { return *s_instance; }
@@ -82,30 +81,23 @@ private:
friend class process;
static constexpr uint64_t promote_frequency = 10;
static constexpr uint64_t steal_frequency = 10;
/// Create a new process object. This process will have its pid
/// set but nothing else.
/// \arg user True if this thread will enter userspace
/// \returns The new process' main thread
thread * create_process(bool user);
void prune(uint64_t now);
void check_promotions(uint64_t now);
lapic &m_apic;
void prune(run_queue &queue, uint64_t now);
void check_promotions(run_queue &queue, uint64_t now);
void steal_work(cpu_data &cpu);
uint32_t m_next_pid;
uint32_t m_tick_count;
process *m_kernel_process;
tcb_node *m_current;
tcb_list m_runlists[num_priorities];
tcb_list m_blocked;
kutil::vector<run_queue> m_run_queues;
// TODO: lol a real clock
uint64_t m_clock = 0;
uint64_t m_last_promotion;
kutil::spinlock m_steal_lock;
static scheduler *s_instance;
};