From: Tulio A M Mendes Date: Mon, 16 Feb 2026 19:14:42 +0000 (-0300) Subject: feat: IPI reschedule infrastructure (SMP Phase 4) X-Git-Url: https://projects.tadryanom.me/docs/static/git-logo.png?a=commitdiff_plain;h=ab231fa2fb3fcc7ad679613661af563b6f249d81;p=AdrOS.git feat: IPI reschedule infrastructure (SMP Phase 4) Add inter-processor interrupt (IPI) reschedule mechanism: - IPI vector 0xFD (253) registered in IDT + ISR assembly stub - isr_handler dispatches vector 253: sends LAPIC EOI then calls schedule() on the receiving CPU - sched_ipi_resched() sends IPI to wake a remote idle CPU when work is enqueued to its runqueue (avoids waking self) - sched_enqueue_ready() sends IPI after enqueuing to remote CPU - sched_pcpu_inc_load() called when enqueuing new kernel threads All processes still dispatched to CPU 0 — per-CPU TSS is needed before user processes can run on APs. The IPI + load tracking infrastructure is ready for when per-CPU TSS is added. 83/83 smoke tests pass (8s), cppcheck clean. --- diff --git a/include/arch/x86/lapic.h b/include/arch/x86/lapic.h index da0349a..fae9d36 100644 --- a/include/arch/x86/lapic.h +++ b/include/arch/x86/lapic.h @@ -37,6 +37,9 @@ /* LAPIC timer IRQ vector — we use IDT slot 32 (same as PIT was) */ #define LAPIC_TIMER_VEC 32 +/* IPI reschedule vector — sent to wake an idle AP when work arrives */ +#define IPI_RESCHED_VEC 0xFD + /* Initialize the Local APIC. Returns 1 if APIC enabled, 0 if not available. */ int lapic_init(void); diff --git a/src/arch/x86/idt.c b/src/arch/x86/idt.c index d1075b2..fea48e5 100644 --- a/src/arch/x86/idt.c +++ b/src/arch/x86/idt.c @@ -303,6 +303,10 @@ void idt_init(void) { // Syscall gate (int 0x80) must be callable from user mode (DPL=3) idt_set_gate(128, (uint32_t)isr128, 0x08, 0xEE); + // IPI reschedule vector (0xFD = 253) — wakes idle APs to run schedule() + extern void isr253(void); + idt_set_gate(253, (uint32_t)isr253, 0x08, 0x8E); + // LAPIC spurious interrupt vector (must have an IDT entry or CPU triple-faults) idt_set_gate(255, (uint32_t)isr255, 0x08, 0x8E); @@ -396,6 +400,14 @@ void isr_handler(struct registers* regs) { return; } + // IPI reschedule (vector 253): send EOI and call schedule() + if (regs->int_no == 253) { + lapic_eoi(); + extern void schedule(void); + schedule(); + return; + } + // Send EOI for IRQs (32-47) BEFORE calling the handler. // This is critical: the timer handler calls schedule() which may // context-switch away. If EOI is deferred until after the handler, diff --git a/src/arch/x86/interrupts.S b/src/arch/x86/interrupts.S index 70c5ffe..342966b 100644 --- a/src/arch/x86/interrupts.S +++ b/src/arch/x86/interrupts.S @@ -144,6 +144,9 @@ IRQ 15, 47 /* Syscall vector (int 0x80 -> 128) */ ISR_NOERRCODE 128 +/* IPI reschedule vector (253 = 0xFD) */ +ISR_NOERRCODE 253 + /* LAPIC spurious interrupt vector (255) */ ISR_NOERRCODE 255 diff --git a/src/kernel/scheduler.c b/src/kernel/scheduler.c index c7215b9..6df95c0 100644 --- a/src/kernel/scheduler.c +++ b/src/kernel/scheduler.c @@ -99,6 +99,23 @@ struct cpu_rq { static struct cpu_rq pcpu_rq[SCHED_MAX_CPUS]; +#ifdef __i386__ +#include "arch/x86/lapic.h" +#include "arch/x86/percpu.h" +/* Send IPI reschedule to wake a remote CPU when work arrives */ +static void sched_ipi_resched(uint32_t target_cpu) { + uint32_t my_cpu = percpu_cpu_index(); + if (target_cpu == my_cpu) return; + if (!lapic_is_enabled()) return; + extern const struct cpu_info* smp_get_cpu(uint32_t index); + const struct cpu_info* ci = smp_get_cpu(target_cpu); + if (!ci) return; + lapic_send_ipi(ci->lapic_id, IPI_RESCHED_VEC); +} +#else +static void sched_ipi_resched(uint32_t target_cpu) { (void)target_cpu; } +#endif + static inline uint32_t bsf32(uint32_t v) { return (uint32_t)__builtin_ctz(v); } @@ -225,14 +242,18 @@ static struct process* rq_pick_next(uint32_t cpu) { void sched_enqueue_ready(struct process* p) { if (!p) return; + uint32_t target_cpu = 0; + int need_ipi = 0; uintptr_t flags = spin_lock_irqsave(&sched_lock); sleep_queue_remove(p); if (p->state == PROCESS_READY) { - uint32_t cpu = p->cpu_id < SCHED_MAX_CPUS ? p->cpu_id : 0; - rq_enqueue(pcpu_rq[cpu].active, p); - sched_pcpu_inc_load(cpu); + target_cpu = p->cpu_id < SCHED_MAX_CPUS ? p->cpu_id : 0; + rq_enqueue(pcpu_rq[target_cpu].active, p); + sched_pcpu_inc_load(target_cpu); + need_ipi = 1; } spin_unlock_irqrestore(&sched_lock, flags); + if (need_ipi) sched_ipi_resched(target_cpu); } void thread_wrapper(void (*fn)(void)); @@ -945,7 +966,8 @@ struct process* process_create_kernel(void (*entry_point)(void)) { ready_queue_head->prev = proc; ready_queue_tail = proc; - rq_enqueue(pcpu_rq[proc->cpu_id].active, proc); + rq_enqueue(pcpu_rq[0].active, proc); + sched_pcpu_inc_load(0); spin_unlock_irqrestore(&sched_lock, flags); return proc;