]> 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 da0349a8b1bf1f1a984cc05a30c26870da45a64f..fae9d369b91331a570135115152d730cde64f62c 100644 (file)
@@ -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);
 
index d1075b25790da7e554d5199d1ca0d8fbd74fc1b7..fea48e59e8a3fbdc1230cbbaa76b8bd2da8a9c23 100644 (file)
@@ -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,
index 70c5ffe1324b2d15632023f61c49aff70378e3f7..342966b10d226258aca83bf0caca70654aaf8b50 100644 (file)
@@ -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
 
index c7215b964e7047583f094e2ac7ce9ab77228c591..6df95c08542e8daeba537e80842ed5d788a721cd 100644 (file)
@@ -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;