Skip to content

Commit 81c696f

Browse files
committed
kernel: add SMP foundation with per-CPU runqueues and affinity
- Percpu state, klocks, and ENABLE_SMP config across arches - Affinity scheduling, lazy secondary ctx, and IPI deferred resched - SMP-safe IPC/notif unlock order; TriCore/RISC-V secondary bring-up
1 parent 25bec93 commit 81c696f

59 files changed

Lines changed: 1898 additions & 236 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,4 @@ build-riscv/
5858

5959
# SDK suite object leftovers
6060
tests/sdk_suite/**/*.o
61+
.hil_tmp/

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ execute_process(
7878
--set "ULMK_CONFIG_MAX_IRQ_BINDINGS=${ULMK_CONFIG_MAX_IRQ_BINDINGS}"
7979
--set "ULMK_CONFIG_DEBUG_PRINTK=${ULMK_CONFIG_DEBUG_PRINTK}"
8080
--set "ULMK_CONFIG_SYSCALL_WCET=${ULMK_CONFIG_SYSCALL_WCET}"
81+
--set "ULMK_CONFIG_ENABLE_SMP=${ULMK_CONFIG_ENABLE_SMP}"
8182
RESULT_VARIABLE _ulmk_gen_rc)
8283
if(NOT _ulmk_gen_rc EQUAL 0)
8384
message(FATAL_ERROR "gen_config.py failed (rc=${_ulmk_gen_rc})")
@@ -97,6 +98,8 @@ add_library(ulmk_kernel STATIC
9798
kernel/syscall/syscall_router.c
9899
kernel/printk/ulmk_printk.c
99100
kernel/thread/thread.c
101+
kernel/percpu/percpu.c
102+
kernel/percpu/klock.c
100103
kernel/sched/sched.c
101104
kernel/sched/fifo_rt.c
102105
kernel/sched/bitmap_rt.c

arch/arm/arch.c

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,3 +503,87 @@ void ulmk_arch_init(ulmk_boot_info_t *info)
503503
ulmk_arch_irq_vectors_init(0u, 0u, 0u);
504504
/* SysTick is armed lazily on the first context switch (see sched_switch). */
505505
}
506+
507+
/* =========================================================================
508+
* SMP — not supported on Cortex-M; stubs keep the arch API complete
509+
* ========================================================================= */
510+
511+
uint32_t ulmk_arch_cpu_id(void)
512+
{
513+
return 0u;
514+
}
515+
516+
void ulmk_arch_spin_lock(ulmk_spinlock_t *lock)
517+
{
518+
#if ULMK_CONFIG_ENABLE_SMP
519+
while (ulmk_arch_atomic_cas(&lock->locked, 0u, 1u) != 0u)
520+
;
521+
#else
522+
(void)lock;
523+
#endif
524+
}
525+
526+
void ulmk_arch_spin_unlock(ulmk_spinlock_t *lock)
527+
{
528+
#if ULMK_CONFIG_ENABLE_SMP
529+
lock->locked = 0u;
530+
#else
531+
(void)lock;
532+
#endif
533+
}
534+
535+
ulmk_arch_irq_key_t ulmk_arch_spin_lock_irqsave(ulmk_spinlock_t *lock)
536+
{
537+
ulmk_arch_irq_key_t key = ulmk_arch_cpu_irq_save();
538+
539+
ulmk_arch_spin_lock(lock);
540+
return key;
541+
}
542+
543+
void ulmk_arch_spin_unlock_irqrestore(ulmk_spinlock_t *lock,
544+
ulmk_arch_irq_key_t key)
545+
{
546+
ulmk_arch_spin_unlock(lock);
547+
ulmk_arch_cpu_irq_restore(key);
548+
}
549+
550+
void ulmk_arch_send_ipi(uint32_t cpu_id)
551+
{
552+
(void)cpu_id;
553+
}
554+
555+
void ulmk_arch_ipi_note_enter(void)
556+
{
557+
}
558+
559+
void ulmk_arch_ipi_clear_self(void)
560+
{
561+
}
562+
563+
void ulmk_arch_ipi_pulse_self(void)
564+
{
565+
}
566+
567+
void ulmk_arch_secondary_init(void)
568+
{
569+
}
570+
571+
void ulmk_arch_secondary_mark_ready(void)
572+
{
573+
}
574+
575+
void ulmk_arch_start_secondary(uint32_t cpu_id, void (*entry)(void))
576+
{
577+
(void)cpu_id;
578+
(void)entry;
579+
}
580+
581+
void ulmk_arch_smp_park(void)
582+
{
583+
for (;;)
584+
ulmk_arch_cpu_halt();
585+
}
586+
587+
void ulmk_arch_smp_mark_ready(void)
588+
{
589+
}

arch/arm/include/arch_config.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818

1919
#include <ulmk/platform.h>
2020

21+
#ifndef ULMK_ARCH_NUM_CPU
22+
#define ULMK_ARCH_NUM_CPU 1
23+
#endif
24+
2125
/* Sub-profile: 0 = ARMv7-M, 1 = ARMv8-M mainline (board overrides). */
2226
#ifndef ULMK_ARCH_ARMV8M
2327
#define ULMK_ARCH_ARMV8M 0

arch/arm/include/ulmk_arch.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,36 @@ typedef struct {
5656
#define ULMK_REGION_PERIPH 4
5757
#define ULMK_REGION_SHARED 5
5858

59+
typedef struct {
60+
volatile uint32_t locked;
61+
} ulmk_spinlock_t;
62+
63+
#define ULMK_SPINLOCK_INIT { 0u }
64+
5965
ulmk_arch_irq_key_t ulmk_arch_cpu_irq_save(void);
6066
void ulmk_arch_cpu_irq_restore(ulmk_arch_irq_key_t key);
6167
void ulmk_arch_cpu_irq_enable(void);
6268
void ulmk_arch_cpu_irq_disable(void);
6369
void ulmk_arch_cpu_idle(void);
6470
void ulmk_arch_cpu_halt(void);
6571
uint32_t ulmk_arch_cpu_clz(uint32_t val);
72+
uint32_t ulmk_arch_cpu_id(void);
73+
74+
void ulmk_arch_spin_lock(ulmk_spinlock_t *lock);
75+
void ulmk_arch_spin_unlock(ulmk_spinlock_t *lock);
76+
ulmk_arch_irq_key_t ulmk_arch_spin_lock_irqsave(ulmk_spinlock_t *lock);
77+
void ulmk_arch_spin_unlock_irqrestore(ulmk_spinlock_t *lock,
78+
ulmk_arch_irq_key_t key);
79+
80+
void ulmk_arch_send_ipi(uint32_t cpu_id);
81+
void ulmk_arch_ipi_clear_self(void);
82+
void ulmk_arch_ipi_note_enter(void);
83+
void ulmk_arch_ipi_pulse_self(void);
84+
void ulmk_arch_secondary_init(void);
85+
void ulmk_arch_secondary_mark_ready(void);
86+
void ulmk_arch_start_secondary(uint32_t cpu_id, void (*entry)(void));
87+
void ulmk_arch_smp_mark_ready(void);
88+
void ulmk_arch_smp_park(void);
6689

6790
void ulmk_arch_cycle_enable(void);
6891
uint32_t ulmk_arch_cycle_read(void);
@@ -130,7 +153,9 @@ void ulmk_arch_trap_dump(uint8_t trap_class, uint8_t tin);
130153
void ulmk_printk_char_out(char c);
131154

132155
void ulmk_kern_irq_dispatch(uint8_t srpn);
156+
void ulmk_kern_ipi_resched(void);
133157
void ulmk_kern_sched_dispatch(bool from_isr);
158+
void ulmk_kern_secondary_main(void);
134159
uint32_t ulmk_kern_syscall_ret_resolve(uint32_t ret);
135160
uint32_t ulmk_kern_trap_syscall(uint8_t tin, uint32_t args[4]);
136161
void ulmk_kern_trap_recoverable(void);

arch/riscv/arch.c

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ struct riscv_trap_frame {
4242
uint32_t regs[TF_SIZE / 4u];
4343
};
4444

45-
uintptr_t g_trap_sp;
45+
/* Indexed by mhartid — trap.S stores the frame pointer per hart. */
46+
uintptr_t g_trap_sp[ULMK_ARCH_NUM_CPU];
4647

4748
static inline uint32_t read_mstatus(void)
4849
{
@@ -478,10 +479,23 @@ void ulmk_arch_mpu_configure(uint8_t prs, const ulmk_arch_region_t *regions,
478479
(void)count;
479480
}
480481

481-
static const ulmk_arch_region_t *g_pmp_regions;
482-
static uint8_t g_pmp_count;
483-
static uint8_t g_pmp_prs = 0xFFu;
484-
static uint8_t g_pmp_dyn; /* non-STACK dynamic slots last programmed */
482+
/*
483+
* PMP CSRs are per-hart. The "last programmed" cache must not be global or
484+
* one CPU's switch causes another's mpu_switch to skip a real rewrite.
485+
*/
486+
struct pmp_cpu_cache {
487+
const ulmk_arch_region_t *regions;
488+
uint8_t count;
489+
uint8_t prs;
490+
uint8_t dyn;
491+
};
492+
493+
static struct pmp_cpu_cache g_pmp_cache[ULMK_ARCH_NUM_CPU] = {
494+
[0] = { .prs = 0xFFu },
495+
#if ULMK_ARCH_NUM_CPU > 1
496+
[1] = { .prs = 0xFFu },
497+
#endif
498+
};
485499

486500
static uint8_t pmp_dyn_count(const ulmk_arch_region_t *regions, uint8_t count)
487501
{
@@ -500,30 +514,44 @@ static uint8_t pmp_dyn_count(const ulmk_arch_region_t *regions, uint8_t count)
500514
void ulmk_arch_mpu_switch(const ulmk_arch_region_t *regions, uint8_t count,
501515
uint8_t prs)
502516
{
503-
uint8_t eff;
517+
struct pmp_cpu_cache *c;
518+
uint32_t cpu;
519+
uint8_t eff;
504520

505-
if (prs == g_pmp_prs && regions == g_pmp_regions && count == g_pmp_count)
521+
cpu = ulmk_arch_cpu_id();
522+
if (cpu >= (uint32_t)ULMK_ARCH_NUM_CPU)
523+
cpu = 0u;
524+
c = &g_pmp_cache[cpu];
525+
526+
/*
527+
* On SMP only skip when this hart already has the exact same layout.
528+
* The stack-only fast path was UP-friendly but races badly when another
529+
* hart's view of "already programmed" is assumed.
530+
*/
531+
if (prs == c->prs && regions == c->regions && count == c->count)
506532
return;
507533

508534
eff = (prs == ULMK_ARCH_PRS_KERNEL) ? 0u : pmp_dyn_count(regions, count);
509535

536+
#if !ULMK_CONFIG_ENABLE_SMP
510537
/* Stack-only AS: static URAM covers stacks — skip full PMP rewrite. */
511-
if (prs == g_pmp_prs && eff == 0u && g_pmp_dyn == 0u &&
538+
if (prs == c->prs && eff == 0u && c->dyn == 0u &&
512539
prs != ULMK_ARCH_PRS_KERNEL) {
513-
g_pmp_regions = regions;
514-
g_pmp_count = count;
540+
c->regions = regions;
541+
c->count = count;
515542
return;
516543
}
544+
#endif
517545

518546
if (prs == ULMK_ARCH_PRS_KERNEL)
519547
pmp_kernel_layout();
520548
else
521549
pmp_user_layout(regions, count);
522550

523-
g_pmp_prs = prs;
524-
g_pmp_regions = regions;
525-
g_pmp_count = count;
526-
g_pmp_dyn = eff;
551+
c->prs = prs;
552+
c->regions = regions;
553+
c->count = count;
554+
c->dyn = eff;
527555
}
528556

529557
bool ulmk_arch_mpu_addr_permitted(uintptr_t addr, size_t size, uint32_t perms)
@@ -717,5 +745,9 @@ void ulmk_arch_init(ulmk_boot_info_t *info)
717745

718746
ulmk_arch_irq_vectors_init((uintptr_t)_trap_handler, 0u, 0u);
719747
ulmk_arch_mpu_init();
748+
#if ULMK_CONFIG_ENABLE_SMP
749+
/* Accept CLINT MSIP reschedule IPIs on every hart. */
750+
__asm__ volatile("csrs mie, %0" :: "r"(1u << 3));
751+
#endif
720752
(void)user_mstatus_init;
721753
}

arch/riscv/include/arch_config.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212

1313
#include <ulmk/platform.h>
1414

15+
#ifndef ULMK_ARCH_NUM_CPU
16+
#define ULMK_ARCH_NUM_CPU 1
17+
#endif
18+
1519
#ifndef ULMK_ARCH_HAVE_FPU
1620
#define ULMK_ARCH_HAVE_FPU 0
1721
#endif

arch/riscv/include/ulmk_arch.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ typedef struct {
1919

2020
typedef uint32_t ulmk_arch_irq_key_t;
2121

22+
typedef struct {
23+
volatile uint32_t locked;
24+
} ulmk_spinlock_t;
25+
26+
#define ULMK_SPINLOCK_INIT { 0u }
27+
2228
typedef struct {
2329
uintptr_t base;
2430
size_t size;
@@ -40,6 +46,23 @@ void ulmk_arch_cpu_irq_disable(void);
4046
void ulmk_arch_cpu_idle(void);
4147
void ulmk_arch_cpu_halt(void);
4248
uint32_t ulmk_arch_cpu_clz(uint32_t val);
49+
uint32_t ulmk_arch_cpu_id(void);
50+
51+
void ulmk_arch_spin_lock(ulmk_spinlock_t *lock);
52+
void ulmk_arch_spin_unlock(ulmk_spinlock_t *lock);
53+
ulmk_arch_irq_key_t ulmk_arch_spin_lock_irqsave(ulmk_spinlock_t *lock);
54+
void ulmk_arch_spin_unlock_irqrestore(ulmk_spinlock_t *lock,
55+
ulmk_arch_irq_key_t key);
56+
57+
void ulmk_arch_send_ipi(uint32_t cpu_id);
58+
void ulmk_arch_ipi_clear_self(void);
59+
void ulmk_arch_ipi_note_enter(void);
60+
void ulmk_arch_ipi_pulse_self(void);
61+
void ulmk_arch_secondary_init(void);
62+
void ulmk_arch_secondary_mark_ready(void);
63+
void ulmk_arch_start_secondary(uint32_t cpu_id, void (*entry)(void));
64+
void ulmk_arch_smp_mark_ready(void);
65+
void ulmk_arch_smp_park(void);
4366

4467
void ulmk_arch_cycle_enable(void);
4568
uint32_t ulmk_arch_cycle_read(void);
@@ -107,7 +130,9 @@ void ulmk_arch_trap_dump(uint8_t trap_class, uint8_t tin);
107130
void ulmk_printk_char_out(char c);
108131

109132
void ulmk_kern_irq_dispatch(uint8_t srpn);
133+
void ulmk_kern_ipi_resched(void);
110134
void ulmk_kern_sched_dispatch(bool from_isr);
135+
void ulmk_kern_secondary_main(void);
111136
uint32_t ulmk_kern_syscall_ret_resolve(uint32_t ret);
112137
uint32_t ulmk_kern_trap_syscall(uint8_t tin, uint32_t args[4]);
113138
void ulmk_kern_trap_recoverable(void);

arch/riscv/irq_clint.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <stdint.h>
1010
#include <ulmk/microkernel.h>
11+
#include <ulmk/config.h>
1112
#include <ulmk_arch.h>
1213
#include <arch_config.h>
1314
#include "irq_internal.h"
@@ -97,6 +98,17 @@ void riscv_clint_dispatch(uint32_t mcause)
9798
is_soft = mcause == MCAUSE_MSOFT;
9899
is_timer = mcause == MCAUSE_MTIMER;
99100

101+
#if ULMK_CONFIG_ENABLE_SMP
102+
/*
103+
* Reschedule IPI: clear this hart's MSIP; kernel marks needs_resched.
104+
* Bound SRPN soft IRQs (tests) still run below.
105+
*/
106+
if (is_soft) {
107+
ulmk_arch_ipi_clear_self();
108+
ulmk_kern_ipi_resched();
109+
}
110+
#endif
111+
100112
for (srpn = 1u; srpn < 256u; srpn++) {
101113
if (g_src_type[srpn] != IRQ_SRC_CLINT)
102114
continue;

0 commit comments

Comments
 (0)