From d7343e7b5568c9a32b1c0600c32b5ed4a7c76166 Mon Sep 17 00:00:00 2001 From: Matheus Date: Mon, 17 Aug 2026 21:47:00 -0300 Subject: [PATCH] scheduler: rework load balancing locking --- kernel-src/sys/scheduler/queue.c | 45 ++++++++++++++++---------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/kernel-src/sys/scheduler/queue.c b/kernel-src/sys/scheduler/queue.c index d34537ef..41ecb710 100644 --- a/kernel-src/sys/scheduler/queue.c +++ b/kernel-src/sys/scheduler/queue.c @@ -303,34 +303,37 @@ void sched_load_balancer(context_t *, dpcarg_t) { interrupt_set(false); cpu_t *most_loaded = NULL; cpu_t *least_loaded = NULL; + size_t most_loaded_count = 0; + size_t least_loaded_count = 0; // find the least loaded and most loaded cpus - for (int i = 0; i < arch_smp_cpusawake; ++i) { - cpu_t *old_most_loaded = NULL; - cpu_t *old_least_loaded = NULL; - + for (size_t i = 0; i < arch_smp_cpusawake; ++i) { spinlock_acquire(&smp_cpus[i]->sched_lock); - if ((most_loaded == NULL || most_loaded->thread_count < smp_cpus[i]->thread_count) && smp_cpus[i]->stealable_thread_count) { - old_most_loaded = most_loaded; + size_t thread_count = smp_cpus[i]->thread_count; + size_t stealable_thread_count = smp_cpus[i]->stealable_thread_count; + spinlock_release(&smp_cpus[i]->sched_lock); + + if ((most_loaded == NULL || most_loaded_count < thread_count) && stealable_thread_count) { most_loaded = smp_cpus[i]; + most_loaded_count = thread_count; } - if (least_loaded == NULL || least_loaded->thread_count > smp_cpus[i]->thread_count) { - old_least_loaded = least_loaded; + if (least_loaded == NULL || least_loaded_count > thread_count) { least_loaded = smp_cpus[i]; + least_loaded_count = thread_count; } - - if (old_most_loaded) - spinlock_release(&old_most_loaded->sched_lock); - - if (old_least_loaded && old_least_loaded != most_loaded && old_least_loaded != old_most_loaded) - spinlock_release(&old_least_loaded->sched_lock); - - if (least_loaded != smp_cpus[i] && most_loaded != smp_cpus[i]) - spinlock_release(&smp_cpus[i]->sched_lock); } - if (most_loaded == NULL || most_loaded->thread_count <= 1 || most_loaded->thread_count == least_loaded->thread_count) + if (most_loaded == NULL || most_loaded == least_loaded) + return; + + cpu_t *first = most_loaded->internal_id < least_loaded->internal_id ? most_loaded : least_loaded; + cpu_t *second = first == most_loaded ? least_loaded : most_loaded; + spinlock_acquire(&first->sched_lock); + spinlock_acquire(&second->sched_lock); + + // the queues may have changed since the scan, so only move work if it still reduces the imbalance + if (most_loaded->stealable_thread_count == 0 || most_loaded->thread_count <= 1 || most_loaded->thread_count <= least_loaded->thread_count) goto leave; thread_t *thread = sched_steal_work_from_cpu(most_loaded); @@ -342,8 +345,6 @@ void sched_load_balancer(context_t *, dpcarg_t) { sched_preempt_cpu(least_loaded); leave: - if (most_loaded) - spinlock_release(&most_loaded->sched_lock); - if (most_loaded != least_loaded) - spinlock_release(&least_loaded->sched_lock); + spinlock_release(&second->sched_lock); + spinlock_release(&first->sched_lock); }