diff --git a/kernel-src/include/kernel/scheduler.h b/kernel-src/include/kernel/scheduler.h index e1178249..270c8cf8 100644 --- a/kernel-src/include/kernel/scheduler.h +++ b/kernel-src/include/kernel/scheduler.h @@ -167,6 +167,7 @@ thread_t *sched_newthread(void *ip, size_t kstacksize, int priority, proc_t *pro void sched_destroyproc(proc_t *); void sched_destroythread(thread_t *); void sched_targetcpu(struct cpu_t *cpu); +void sched_reschedule_on_cpu(struct cpu_t *cpu, bool target); void sched_sleepus(size_t us); void sched_apentry(); void sched_inactiveproc(proc_t *proc); diff --git a/kernel-src/sys/itimer.c b/kernel-src/sys/itimer.c index dc90294a..ef707043 100644 --- a/kernel-src/sys/itimer.c +++ b/kernel-src/sys/itimer.c @@ -30,16 +30,11 @@ void itimer_pause(itimer_t *itimer, uintmax_t *remainingus, uintmax_t *repeatus) bool intstatus = interrupt_set(false); spinlock_acquire(&itimer->lock); + while (itimer->cpu && itimer->cpu != current_cpu()) { // schedule on the itimer cpu - sched_targetcpu(itimer->cpu); spinlock_release(&itimer->lock); - // allow interrupts to be handled (such as a tlb shootdown) - // as the other cpu could be busy waiting for this one - interrupt_set(true); - interrupt_set(false); - sched_yield(); - sched_targetcpu(NULL); + sched_reschedule_on_cpu(itimer->cpu, false); spinlock_acquire(&itimer->lock); } diff --git a/kernel-src/sys/scheduler.c b/kernel-src/sys/scheduler.c index 612647ae..1943a829 100644 --- a/kernel-src/sys/scheduler.c +++ b/kernel-src/sys/scheduler.c @@ -13,6 +13,7 @@ #include #include #include +#include #define QUANTUM_US 100000 #define SCHEDULER_STACK_SIZE PAGE_SIZE * 16 @@ -714,6 +715,52 @@ void sched_targetcpu(cpu_t *cpu) { interrupt_set(intstatus); } +// yields the current thread and sends an reschedule ipi to a specific cpu +static void reschedule_yield(context_t *context, void *_cpu) { + thread_t *thread = current_thread(); + cpu_t *cpu = _cpu; + + spinlock_acquire(&runqueuelock); + + thread_t *next = runqueuenext(0x0fffffff); + + ARCH_CONTEXT_THREADSAVE(thread, context); + + thread->flags &= ~SCHED_THREAD_FLAGS_RUNNING; + runqueueinsert(thread); + + if (next == NULL) + next = current_cpu()->idlethread; + + arch_smp_sendipi(cpu, cpu->reschedule_isr, ARCH_SMP_IPI_TARGET, false); + + spinlock_release(&runqueuelock); + switchthread(next); +} + +void sched_reschedule_on_cpu(cpu_t *cpu, bool target) { + cpu_t *old_target = current_thread()->cputarget; + current_thread()->cputarget = cpu; + + bool status = interrupt_set(false); + + // already on the cpu + if (cpu == current_cpu()) + goto leave; + + long old_priority = current_thread()->priority; + current_thread()->priority = 0; + + arch_context_saveandcall(reschedule_yield, current_cpu()->schedulerstack, cpu); + + current_thread()->priority = old_priority; + + leave: + interrupt_set(status); + if (target == false) + current_thread()->cputarget = old_target; +} + static void timeout(context_t *, dpcarg_t arg) { thread_t *thread = arg; sched_wakeup(thread, 0);