]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
feat: IPI reschedule infrastructure (SMP Phase 4)
authorTulio A M Mendes <[email protected]>
Mon, 16 Feb 2026 19:14:42 +0000 (16:14 -0300)
committerTulio A M Mendes <[email protected]>
Mon, 16 Feb 2026 19:14:42 +0000 (16:14 -0300)
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.

include/arch/x86/lapic.h
src/arch/x86/idt.c
src/arch/x86/interrupts.S
src/kernel/scheduler.c

index 80e3b6c5e50975d2ef5f237e8cba1f879308a92e..01d23819a1df51f2af31a0a5fa11ebfbc4c6919c 100644 (file)
@@ -46,6 +46,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);
 
index b2df21963ee181b2c652410ac974a4b3c47b6c8a..e7cff5ac3a7de627591c741f8064c7709728daf7 100644 (file)
@@ -312,6 +312,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);
 
@@ -405,6 +409,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,
index bc52cb6ab2db7fc8fb390e12511df14239636321..f84229c06273d71dc6f542dc0c5ebf6ab62c7b94 100644 (file)
@@ -153,6 +153,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
 
index 675ace31daa13bad3cd60697cc289c3dbb8f9a0a..e1699f70f22e3de1a6652df416f70905f2674b78 100644 (file)
@@ -108,6 +108,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);
 }
@@ -234,14 +251,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));
@@ -954,7 +975,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;