mirror of
https://github.com/Mathewnd/Astral.git
synced 2026-10-27 12:48:22 +00:00
scheduler: begin rework
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
#include <kernel/scheduler.h>
|
||||
#include <util.h>
|
||||
|
||||
#define MAX_INTERACTIVITY 100
|
||||
#define SCALING_FACTOR (MAX_INTERACTIVITY / 2)
|
||||
|
||||
#define HISTORY_LIMIT_USEC 5000000
|
||||
|
||||
static void update_interactivity(thread_t *thread) {
|
||||
time_t sleep_time = thread->metrics.sleep_time_avg_us;
|
||||
time_t run_time = thread->metrics.run_time_avg_us;
|
||||
|
||||
if (sleep_time > run_time)
|
||||
thread->metrics.interactivity_score = min((SCALING_FACTOR * run_time) / sleep_time, MAX_INTERACTIVITY);
|
||||
else
|
||||
thread->metrics.interactivity_score = min((SCALING_FACTOR * sleep_time) / run_time + SCALING_FACTOR, MAX_INTERACTIVITY);
|
||||
|
||||
// TODO nice value impacting this
|
||||
}
|
||||
|
||||
static void offset_times(thread_t *thread, time_t run_us, time_t sleep_us) {
|
||||
thread->metrics.run_time_avg_us += run_us;
|
||||
thread->metrics.sleep_time_avg_us += sleep_us;
|
||||
|
||||
time_t total = thread->metrics.run_time_avg_us + thread->metrics.sleep_time_avg_us;
|
||||
if (total > HISTORY_LIMIT_USEC) {
|
||||
if (total > HISTORY_LIMIT_USEC * 2) {
|
||||
// the thread has spent a lot of time sleeping, as this will not be reached normally otherwise
|
||||
// set its sleep time to max and zero the run time before applying normal decay
|
||||
thread->metrics.run_time_avg_us = 0;
|
||||
thread->metrics.sleep_time_avg_us = HISTORY_LIMIT_USEC;
|
||||
}
|
||||
thread->metrics.run_time_avg_us = thread->metrics.run_time_avg_us * 4 / 5;
|
||||
thread->metrics.sleep_time_avg_us = thread->metrics.sleep_time_avg_us * 4 / 5;
|
||||
}
|
||||
|
||||
update_interactivity(thread);
|
||||
}
|
||||
|
||||
void sched_thread_running_callback(thread_t *thread) {
|
||||
thread->metrics.run_start = timekeeper_time();
|
||||
}
|
||||
|
||||
void sched_thread_stopping_callback(thread_t *thread, bool sleeping) {
|
||||
timespec_t now = timekeeper_time();
|
||||
offset_times(thread, timespec_diffus(thread->metrics.run_start, now), 0);
|
||||
|
||||
if (sleeping)
|
||||
thread->metrics.sleep_start = now;
|
||||
}
|
||||
|
||||
// called right before the specified thread gets enqueued after sleeping
|
||||
void sched_thread_wakeup_callback(thread_t *thread) {
|
||||
offset_times(thread, 0, timespec_diffus(thread->metrics.sleep_start, timekeeper_time()));
|
||||
}
|
||||
@@ -80,6 +80,8 @@ static thread_t *runqueuenext(int minprio) {
|
||||
static __attribute__((noreturn)) void switch_thread(thread_t *thread) {
|
||||
interrupt_set(false);
|
||||
thread_t* current = current_thread();
|
||||
|
||||
sched_thread_running_callback(thread);
|
||||
|
||||
current_cpu()->thread = thread;
|
||||
|
||||
@@ -139,6 +141,7 @@ void sched_queue(thread_t *thread) {
|
||||
// TODO yield if higher priority than current thread (or send another CPU an IPI)
|
||||
}
|
||||
|
||||
// no metrics callback here as this will be called from things like threads stopping
|
||||
__attribute__((noreturn)) void sched_stop_current_thread() {
|
||||
interrupt_set(false);
|
||||
|
||||
@@ -256,6 +259,7 @@ static void yield(context_t *context, void *) {
|
||||
ARCH_CONTEXT_THREADSAVE(thread, context);
|
||||
|
||||
thread->flags &= ~THREAD_FLAGS_RUNNING;
|
||||
|
||||
if (sleeping == false)
|
||||
runqueueinsert(thread);
|
||||
|
||||
@@ -267,12 +271,16 @@ static void yield(context_t *context, void *) {
|
||||
}
|
||||
|
||||
spinlock_release(&runqueuelock);
|
||||
sched_thread_running_callback(current_thread());
|
||||
}
|
||||
|
||||
int sched_yield() {
|
||||
bool sleeping = current_thread()->flags & THREAD_FLAGS_SLEEP;
|
||||
bool old = sleeping ? current_thread()->sleepintstatus : interrupt_set(false);
|
||||
|
||||
sched_thread_stopping_callback(current_thread(), sleeping);
|
||||
arch_context_saveandcall(yield, current_cpu()->schedulerstack, NULL);
|
||||
|
||||
__assert(current_cpu()->ipl == IPL_NORMAL);
|
||||
interrupt_set(old);
|
||||
return sleeping ? current_thread()->wakeupreason : 0;
|
||||
@@ -298,6 +306,8 @@ bool sched_wakeup(thread_t *thread, int reason) {
|
||||
thread->flags &= ~(THREAD_FLAGS_SLEEP | THREAD_FLAGS_INTERRUPTIBLE);
|
||||
thread->wakeupreason = reason;
|
||||
|
||||
sched_thread_wakeup_callback(thread);
|
||||
|
||||
// TODO send IPI to idle cores
|
||||
sched_queue(thread);
|
||||
spinlock_release(&thread->sleeplock);
|
||||
@@ -323,6 +333,7 @@ static void dopreempt() {
|
||||
}
|
||||
|
||||
spinlock_release(&runqueuelock);
|
||||
|
||||
switch_thread(next);
|
||||
}
|
||||
|
||||
@@ -334,6 +345,8 @@ static void preempt_dpc(context_t *context, dpcarg_t arg) {
|
||||
if (current->flags & THREAD_FLAGS_PREEMPTED)
|
||||
return;
|
||||
|
||||
sched_thread_stopping_callback(current, false);
|
||||
|
||||
current->flags |= THREAD_FLAGS_PREEMPTED;
|
||||
ARCH_CONTEXT_THREADSAVE(current, context);
|
||||
|
||||
@@ -390,6 +403,7 @@ static void reschedule_yield(context_t *context, void *_cpu) {
|
||||
switch_thread(next);
|
||||
}
|
||||
|
||||
// TODO verify if something like this is really needed
|
||||
void sched_reschedule_on_cpu(cpu_t *cpu, bool target) {
|
||||
cpu_t *old_target = current_thread()->cputarget;
|
||||
current_thread()->cputarget = cpu;
|
||||
@@ -403,6 +417,8 @@ void sched_reschedule_on_cpu(cpu_t *cpu, bool target) {
|
||||
long old_priority = current_thread()->priority;
|
||||
current_thread()->priority = 0;
|
||||
|
||||
sched_thread_stopping_callback(current_thread(), false);
|
||||
|
||||
arch_context_saveandcall(reschedule_yield, current_cpu()->schedulerstack, cpu);
|
||||
|
||||
current_thread()->priority = old_priority;
|
||||
@@ -461,6 +477,8 @@ void sched_init() {
|
||||
|
||||
sched_target_cpu(current_cpu());
|
||||
|
||||
sched_thread_running_callback(current_thread());
|
||||
|
||||
timer_insert(current_cpu()->timer, ¤t_cpu()->schedtimerentry, reschedule_timer_dpc, NULL, QUANTUM_US, true);
|
||||
// XXX move this resume to a more appropriate place
|
||||
timer_resume(current_cpu()->timer);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <kernel/file.h>
|
||||
#include <kernel/interrupt.h>
|
||||
#include <kernel/jobctl.h>
|
||||
#include <kernel/interrupt.h>
|
||||
|
||||
syscallret_t syscall_fork(context_t *ctx) {
|
||||
syscallret_t ret = {
|
||||
@@ -62,6 +63,12 @@ syscallret_t syscall_fork(context_t *ctx) {
|
||||
memcpy(&nproc->signals.pending, &proc->signals.pending, sizeof(sigset_t));
|
||||
memcpy(&nproc->signals.actions, &proc->signals.actions[0], sizeof(sigaction_t) * NSIG);
|
||||
|
||||
// copy thread scheduling metrics over
|
||||
// raised to IPL_DPC to prevent scheduling
|
||||
long ipl = interrupt_raiseipl(IPL_DPC);
|
||||
memcpy(&nthread->metrics, ¤t_thread()->metrics, sizeof(nthread->metrics));
|
||||
interrupt_loweripl(ipl);
|
||||
|
||||
ret.ret = nproc->pid;
|
||||
|
||||
sched_queue(nthread);
|
||||
|
||||
@@ -43,6 +43,8 @@ thread_t *sched_newthread(void *ip, size_t kstacksize, int priority, proc_t *pro
|
||||
SPINLOCK_INIT(thread->sleeplock);
|
||||
SPINLOCK_INIT(thread->signals.lock);
|
||||
EVENT_INITHEADER(&thread->signals.waitpendingevent);
|
||||
memset(&thread->metrics, 0, sizeof(thread->metrics));
|
||||
thread->metrics.sleep_time_avg_us = 1; // to prevent a division by 0 when the thread first gets scheduled
|
||||
|
||||
return thread;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user