Skip to content

Commit a149b01

Browse files
committed
ipc: cut ep rendezvous cost via handoff and stack MPU skip
- Direct sched handoff on ep_call when the server is waiting; fuse reply_recv when the next caller is already queued - Skip STACK (covered by static URAM) on all arches so stack-only IPC switches avoid MPU/PMP reprogram
1 parent 2bb13e0 commit a149b01

9 files changed

Lines changed: 256 additions & 58 deletions

File tree

arch/arm/mpu_v7m.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,16 +179,45 @@ void ulmk_arch_mpu_configure(uint8_t prs, const ulmk_arch_region_t *regions,
179179
static const ulmk_arch_region_t *g_mpu_regions;
180180
static uint8_t g_mpu_count;
181181
static uint8_t g_mpu_prs = 0xFFu;
182+
static uint8_t g_mpu_dyn; /* non-STACK dynamic slots last programmed */
183+
184+
static uint8_t mpu_dyn_count(const ulmk_arch_region_t *regions, uint8_t count)
185+
{
186+
uint8_t n = 0u;
187+
uint8_t i;
188+
189+
if (!regions)
190+
return 0u;
191+
for (i = 0u; i < count; i++) {
192+
if (regions[i].type != ULMK_REGION_STACK)
193+
n++;
194+
}
195+
return n;
196+
}
182197

183198
void ulmk_arch_mpu_switch(const ulmk_arch_region_t *regions, uint8_t count,
184199
uint8_t prs)
185200
{
186201
uint8_t slot;
187202
uint8_t i;
203+
uint8_t eff;
188204

189205
if (prs == g_mpu_prs && regions == g_mpu_regions && count == g_mpu_count)
190206
return;
191207

208+
eff = (prs != ULMK_ARCH_PRS_KERNEL) ? mpu_dyn_count(regions, count) : 0u;
209+
210+
/*
211+
* STACK is inside the static URAM window. Stack-only address spaces
212+
* share that window — avoid tear-down/reprogram on every IPC switch.
213+
*/
214+
if (prs == g_mpu_prs && eff == 0u && g_mpu_dyn == 0u &&
215+
prs != ULMK_ARCH_PRS_KERNEL) {
216+
g_mpu_regions = regions;
217+
g_mpu_count = count;
218+
return;
219+
}
220+
192221
/*
193222
* Reprogram with the MPU off. A per-thread region's power-of-two
194223
* round-up (log2_cover) can transiently over-cover the kernel .text
@@ -205,11 +234,14 @@ void ulmk_arch_mpu_switch(const ulmk_arch_region_t *regions, uint8_t count,
205234
slot = ULMK_ARCH_MPU_USER_BASE;
206235
if (prs != ULMK_ARCH_PRS_KERNEL && regions) {
207236
for (i = 0u; i < count && slot < ULMK_ARCH_MPU_REGIONS; i++) {
237+
if (regions[i].type == ULMK_REGION_STACK)
238+
continue;
208239
region_program(slot, regions[i].base, regions[i].size,
209240
perm_to_attr(regions[i].perms, false));
210241
slot++;
211242
}
212243
}
244+
eff = (uint8_t)(slot - ULMK_ARCH_MPU_USER_BASE);
213245
for (; slot < ULMK_ARCH_MPU_REGIONS; slot++)
214246
region_disable(slot);
215247

@@ -221,6 +253,7 @@ void ulmk_arch_mpu_switch(const ulmk_arch_region_t *regions, uint8_t count,
221253
g_mpu_prs = prs;
222254
g_mpu_regions = regions;
223255
g_mpu_count = count;
256+
g_mpu_dyn = eff;
224257
}
225258

226259
bool ulmk_arch_mpu_addr_permitted(uintptr_t addr, size_t size, uint32_t perms)

arch/arm/mpu_v8m.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,16 +184,46 @@ static bool covered_by_static(uintptr_t base, uintptr_t size)
184184
static const ulmk_arch_region_t *g_mpu_regions;
185185
static uint8_t g_mpu_count;
186186
static uint8_t g_mpu_prs = 0xFFu;
187+
static uint8_t g_mpu_dyn; /* dynamic slots last programmed */
188+
189+
static uint8_t mpu_dyn_needed(const ulmk_arch_region_t *regions, uint8_t count)
190+
{
191+
uint8_t n = 0u;
192+
uint8_t i;
193+
194+
if (!regions)
195+
return 0u;
196+
for (i = 0u; i < count; i++) {
197+
if (regions[i].type == ULMK_REGION_STACK)
198+
continue;
199+
if (regions[i].size == 0u ||
200+
covered_by_static(regions[i].base, regions[i].size))
201+
continue;
202+
n++;
203+
}
204+
return n;
205+
}
187206

188207
void ulmk_arch_mpu_switch(const ulmk_arch_region_t *regions, uint8_t count,
189208
uint8_t prs)
190209
{
191210
uint8_t slot;
192211
uint8_t i;
212+
uint8_t eff;
193213

194214
if (prs == g_mpu_prs && regions == g_mpu_regions && count == g_mpu_count)
195215
return;
196216

217+
eff = (prs != ULMK_ARCH_PRS_KERNEL) ? mpu_dyn_needed(regions, count) : 0u;
218+
219+
/* No unique dynamic regions — static windows already grant access. */
220+
if (prs == g_mpu_prs && eff == 0u && g_mpu_dyn == 0u &&
221+
prs != ULMK_ARCH_PRS_KERNEL) {
222+
g_mpu_regions = regions;
223+
g_mpu_count = count;
224+
return;
225+
}
226+
197227
/* Reprogram with the MPU off so the update is atomic w.r.t. running code. */
198228
__asm__ volatile("dsb" ::: "memory");
199229
REG32(ULMK_ARCH_MPU_CTRL) = 0u;
@@ -204,6 +234,8 @@ void ulmk_arch_mpu_switch(const ulmk_arch_region_t *regions, uint8_t count,
204234
slot = ULMK_ARCH_MPU_USER_BASE;
205235
if (prs != ULMK_ARCH_PRS_KERNEL && regions) {
206236
for (i = 0u; i < count && slot < ULMK_ARCH_MPU_REGIONS; i++) {
237+
if (regions[i].type == ULMK_REGION_STACK)
238+
continue;
207239
if (regions[i].size == 0u ||
208240
covered_by_static(regions[i].base, regions[i].size))
209241
continue;
@@ -213,6 +245,7 @@ void ulmk_arch_mpu_switch(const ulmk_arch_region_t *regions, uint8_t count,
213245
slot++;
214246
}
215247
}
248+
eff = (uint8_t)(slot - ULMK_ARCH_MPU_USER_BASE);
216249
for (; slot < ULMK_ARCH_MPU_REGIONS; slot++)
217250
region_disable(slot);
218251

@@ -224,6 +257,7 @@ void ulmk_arch_mpu_switch(const ulmk_arch_region_t *regions, uint8_t count,
224257
g_mpu_prs = prs;
225258
g_mpu_regions = regions;
226259
g_mpu_count = count;
260+
g_mpu_dyn = eff;
227261
}
228262

229263
bool ulmk_arch_mpu_addr_permitted(uintptr_t addr, size_t size, uint32_t perms)

arch/riscv/arch.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,11 +430,18 @@ static void pmp_user_layout(const ulmk_arch_region_t *regions, uint8_t count)
430430
pmp_set_napot(ULMK_ARCH_PMP_MMIO, mmio_lo, mmio_hi - mmio_lo,
431431
PMP_R | PMP_W);
432432

433+
/*
434+
* STACK sits inside the static URAM NAPOT window — skip it. Remaining
435+
* dynamic entries (heap / shared / spare) use TOR slots.
436+
*/
433437
slot = ULMK_ARCH_PMP_USER_BASE;
434438
if (regions && count > 0u) {
435439
for (i = 0u; i < count && slot < ULMK_ARCH_PMP_NUM; i++) {
436440
uint8_t perm = 0u;
437441

442+
if (regions[i].type == ULMK_REGION_STACK)
443+
continue;
444+
438445
if (regions[i].perms & ULMK_PERM_READ)
439446
perm |= PMP_R;
440447
if (regions[i].perms & ULMK_PERM_WRITE)
@@ -474,13 +481,40 @@ void ulmk_arch_mpu_configure(uint8_t prs, const ulmk_arch_region_t *regions,
474481
static const ulmk_arch_region_t *g_pmp_regions;
475482
static uint8_t g_pmp_count;
476483
static uint8_t g_pmp_prs = 0xFFu;
484+
static uint8_t g_pmp_dyn; /* non-STACK dynamic slots last programmed */
485+
486+
static uint8_t pmp_dyn_count(const ulmk_arch_region_t *regions, uint8_t count)
487+
{
488+
uint8_t n = 0u;
489+
uint8_t i;
490+
491+
if (!regions)
492+
return 0u;
493+
for (i = 0u; i < count; i++) {
494+
if (regions[i].type != ULMK_REGION_STACK)
495+
n++;
496+
}
497+
return n;
498+
}
477499

478500
void ulmk_arch_mpu_switch(const ulmk_arch_region_t *regions, uint8_t count,
479501
uint8_t prs)
480502
{
503+
uint8_t eff;
504+
481505
if (prs == g_pmp_prs && regions == g_pmp_regions && count == g_pmp_count)
482506
return;
483507

508+
eff = (prs == ULMK_ARCH_PRS_KERNEL) ? 0u : pmp_dyn_count(regions, count);
509+
510+
/* Stack-only AS: static URAM covers stacks — skip full PMP rewrite. */
511+
if (prs == g_pmp_prs && eff == 0u && g_pmp_dyn == 0u &&
512+
prs != ULMK_ARCH_PRS_KERNEL) {
513+
g_pmp_regions = regions;
514+
g_pmp_count = count;
515+
return;
516+
}
517+
484518
if (prs == ULMK_ARCH_PRS_KERNEL)
485519
pmp_kernel_layout();
486520
else
@@ -489,6 +523,7 @@ void ulmk_arch_mpu_switch(const ulmk_arch_region_t *regions, uint8_t count,
489523
g_pmp_prs = prs;
490524
g_pmp_regions = regions;
491525
g_pmp_count = count;
526+
g_pmp_dyn = eff;
492527
}
493528

494529
bool ulmk_arch_mpu_addr_permitted(uintptr_t addr, size_t size, uint32_t perms)

arch/tricore/arch.c

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -702,10 +702,27 @@ static void mpu_write_user_slot(uint8_t idx, const ulmk_arch_region_t *r,
702702
}
703703

704704
/*
705-
* Configure dynamic DPR slots for the given PRS from @regions.
706-
* Preserves the static minimum-isolation enables programmed at boot.
705+
* Dynamic DPR slots for the given PRS from @regions.
706+
* STACK is covered by the static URAM window — skip it so typical IPC
707+
* (stack-only AS) programs zero dynamic slots and can early-exit.
707708
* Regions that do not fit (QEMU: USER_DPR_BASE == NUM_DPR) are ignored.
708709
*/
710+
static uint8_t mpu_dyn_count(const ulmk_arch_region_t *regions, uint8_t count,
711+
uint8_t max_dyn)
712+
{
713+
uint8_t n = 0u;
714+
uint8_t i;
715+
716+
if (!regions || max_dyn == 0u)
717+
return 0u;
718+
719+
for (i = 0u; i < count && n < max_dyn; i++) {
720+
if (regions[i].type != ULMK_REGION_STACK)
721+
n++;
722+
}
723+
return n;
724+
}
725+
709726
static void mpu_program_regions(uint8_t prs, const ulmk_arch_region_t *regions,
710727
uint8_t count)
711728
{
@@ -716,6 +733,7 @@ static void mpu_program_regions(uint8_t prs, const ulmk_arch_region_t *regions,
716733
uint8_t i;
717734
uint8_t prog;
718735
uint8_t max_dyn;
736+
uint8_t eff;
719737
uintptr_t utext_lo;
720738
uintptr_t utext_hi;
721739

@@ -744,29 +762,40 @@ static void mpu_program_regions(uint8_t prs, const ulmk_arch_region_t *regions,
744762
}
745763

746764
max_dyn = (uint8_t)(ULMK_ARCH_MPU_NUM_DPR - ULMK_ARCH_MPU_USER_DPR_BASE);
747-
if (count > max_dyn)
748-
count = max_dyn;
749765
if (!regions)
750766
count = 0u;
767+
eff = mpu_dyn_count(regions, count, max_dyn);
751768

752769
/*
753770
* Unchanged domain (typical self-yield / same thread): no CSFR writes.
754771
*/
755772
if (prs == g_mpu_prs && regions == g_mpu_regions && count == g_mpu_count)
756773
return;
757774

775+
/*
776+
* IPC hot path: both sides stack-only → static URAM already covers
777+
* stacks; no dynamic slots live and none requested.
778+
*/
779+
if (prs == g_mpu_prs && eff == 0u && g_mpu_live == 0u) {
780+
g_mpu_regions = regions;
781+
g_mpu_count = count;
782+
return;
783+
}
784+
758785
mpu_prs1_static_enables(&dpre, &dpwe, &cpre, &cpxe);
759786

760787
/*
761-
* Fast append: mem_map / heap_extend grew the region table by one.
762-
* Only program the new slot and refresh enables.
788+
* Fast append: mem_map grew the table by one non-STACK region and the
789+
* prior layout had no STACK holes (dense slot == region index).
763790
*/
764791
if (prs == g_mpu_prs && regions == g_mpu_regions &&
765-
count == (uint8_t)(g_mpu_count + 1u) && count > 0u) {
792+
count == (uint8_t)(g_mpu_count + 1u) && count > 0u &&
793+
regions[count - 1u].type != ULMK_REGION_STACK &&
794+
mpu_dyn_count(regions, (uint8_t)(count - 1u), max_dyn) ==
795+
(uint8_t)(count - 1u)) {
766796
mpu_write_user_slot((uint8_t)(count - 1u), &regions[count - 1u],
767797
&dpre, &dpwe);
768-
/* Re-apply bits for already-live slots. */
769-
for (i = 0u; i < g_mpu_count; i++) {
798+
for (i = 0u; i < (uint8_t)(count - 1u); i++) {
770799
uint8_t d_slot =
771800
(uint8_t)(ULMK_ARCH_MPU_USER_DPR_BASE + i);
772801

@@ -781,7 +810,13 @@ static void mpu_program_regions(uint8_t prs, const ulmk_arch_region_t *regions,
781810
return;
782811
}
783812

784-
prog = count;
813+
prog = 0u;
814+
for (i = 0u; i < count && prog < max_dyn; i++) {
815+
if (regions[i].type == ULMK_REGION_STACK)
816+
continue;
817+
mpu_write_user_slot(prog, &regions[i], &dpre, &dpwe);
818+
prog++;
819+
}
785820

786821
/* Clear only previously live dynamic slots that are no longer used. */
787822
for (i = prog; i < g_mpu_live; i++) {
@@ -791,9 +826,6 @@ static void mpu_program_regions(uint8_t prs, const ulmk_arch_region_t *regions,
791826
mpu_write_dpr(d_slot, 0u, 0u);
792827
}
793828

794-
for (i = 0u; i < prog; i++)
795-
mpu_write_user_slot(i, &regions[i], &dpre, &dpwe);
796-
797829
mpu_write_enables(prs, dpre, dpwe, cpre, cpxe);
798830

799831
g_mpu_prs = prs;

kernel/include/ulmk_sched.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ void ulmk_sched_set_class(const ulmk_sched_class_t *cls);
5555
void ulmk_sched_start(void);
5656
void ulmk_sched_resched(void);
5757

58+
/*
59+
* ulmk_sched_handoff — switch directly to @next (not in the ready queue).
60+
* Current thread must already be BLOCKED/DEAD/SUSPENDED and dequeued.
61+
* Used by IPC rendezvous to avoid enqueue+pick_next of a known partner.
62+
*/
63+
void ulmk_sched_handoff(ulmk_thread_t *next);
64+
5865
void ulmk_sched_enqueue(ulmk_thread_t *t);
5966
void ulmk_sched_dequeue(ulmk_thread_t *t);
6067
/* Caller already serialised IRQs — no nested irq_save. */

0 commit comments

Comments
 (0)