From 4239f0c20a2337c7444bf4f87938ba266f1d04e2 Mon Sep 17 00:00:00 2001 From: Zeev Belinsky Date: Sun, 21 Jun 2026 13:09:10 -0700 Subject: [PATCH 1/3] [WIP] Explore BESTWAIT hardware steering for scheduler optimization This commit explores using the BESTWAIT register for hardware-assisted scheduling decisions in the reference implementation. The goal is to investigate whether BESTWAIT can improve scheduler performance or enable different implementation strategies. ## What was attempted - Added BESTWAIT register support to hw.h/hw.spec - Implemented test infrastructure (H2K_bestwait test suite) - Modified check_sanity.ref.c to explore BESTWAIT-based scheduling - Updated futex_pi and setup reference implementations - Attempted to implement opt version of check_sanity with BESTWAIT BESTWAIT is a global priority register that: - Holds the priority of the highest-priority task waiting to run - Triggers a reschedule interrupt when ANY thread's effective priority becomes worse than the BESTWAIT value - Does NOT deliver interrupts to specific threads; it's a global signal ## Critical blockers preventing full implementation 1. **Cannot access per-thread STID.PRIO from software** - The STID register (per-thread state) is not accessible from other threads - This prevents reading the effective priority of other threads - Blocks the core comparison logic needed for proper BESTWAIT usage 2. **Resched function cannot implement priority handoff** - The H2K_dosched() function needs to compare thread priorities to determine which thread should handle the reschedule interrupt - Without access to STID.PRIO, we cannot implement the required logic - This is the blocker for steering interrupts correctly ## Performance implications - Current check_sanity.ref implementation helps performance - Attempted opt version also shows NO improvement over current implementation - The reference implementation remains exploratory ## Why only in ref? The opt version was attempted but provides no performance benefit. The ref version is kept to document the exploration and architectural constraints for future scheduler work. ## Next steps - Investigate if STID.PRIO can be exposed via privileged interface - Consider alternative scheduling implementation accommodating the bestwait reg usage Signed-off-by: Zeev Belinsky --- kernel/futex/futex_pi/futex_pi.ref.c | 9 +- kernel/init/setup/setup.ref.c | 1 + kernel/sched/check_sanity/check_sanity.ref.c | 25 ++- .../test/tests/H2K_bestwait/Makefile | 7 + .../test/tests/H2K_bestwait/Makefile.inc | 5 + .../test/tests/H2K_bestwait/test.c | 208 ++++++++++++++++++ .../tests/H2K_check_sanity/scenarios/test.c | 19 ++ kernel/util/hw/hw.h | 31 +++ kernel/util/hw/hw.spec | 59 +++++ kernel/util/max/max.h | 5 + scripts/testlist.v61 | 1 + 11 files changed, 359 insertions(+), 11 deletions(-) create mode 100644 kernel/sched/check_sanity/test/tests/H2K_bestwait/Makefile create mode 100644 kernel/sched/check_sanity/test/tests/H2K_bestwait/Makefile.inc create mode 100644 kernel/sched/check_sanity/test/tests/H2K_bestwait/test.c diff --git a/kernel/futex/futex_pi/futex_pi.ref.c b/kernel/futex/futex_pi/futex_pi.ref.c index ae7aa0ed7..2f64a81e2 100644 --- a/kernel/futex/futex_pi/futex_pi.ref.c +++ b/kernel/futex/futex_pi/futex_pi.ref.c @@ -42,6 +42,9 @@ static inline void H2K_futex_pi_raise(u32_t prio, H2K_id_t destid) H2K_ready_insert(dest); } else if (dest->status == H2K_STATUS_RUNNING) { H2K_runlist_set_thread_prio(dest, prio); + /* Sync hardware STID.PRIO so BESTWAIT sees the boosted priority + * immediately -- avoids spurious preemption of the holder. */ + set_thread_stid_prio(dest->hthread, prio); if (H2K_gp->priomask & (1<hthread)) { H2K_raise_lowprio(); } @@ -130,8 +133,10 @@ s32_t H2K_futex_unlock_pi(u32_t *lock, H2K_thread_context *me) H2K_atomic_swap(lock,H2K_id_from_context(ret).raw+1); } H2K_safemem_unlock(); - /* Restore my priority */ + /* Restore priority in software and hardware atomically -- BESTWAIT + * comparator reads STID.PRIO; without the hardware sync the stale + * boosted value hides me from the comparator causing a priority inversion. */ H2K_runlist_set_thread_prio(me, me->base_prio); + set_thread_stid_prio(me->hthread, me->base_prio); return (s32_t)H2K_check_sanity_unlock(0); } - diff --git a/kernel/init/setup/setup.ref.c b/kernel/init/setup/setup.ref.c index 17abac72f..931c66c02 100644 --- a/kernel/init/setup/setup.ref.c +++ b/kernel/init/setup/setup.ref.c @@ -97,6 +97,7 @@ IN_SECTION(".text.init.setup") static H2K_vmblock_t *H2K_init_setup(u32_t multic H2K_lowprio_init(); H2K_futex_init(); H2K_intconfig_init(ssbase); + H2K_set_schedcfg(SCHEDCFG_EN | SCHEDCFG_INTNO(RESCHED_INT)); H2K_thread_init(); H2K_asid_table_init(); diff --git a/kernel/sched/check_sanity/check_sanity.ref.c b/kernel/sched/check_sanity/check_sanity.ref.c index 5690a2968..578720caa 100644 --- a/kernel/sched/check_sanity/check_sanity.ref.c +++ b/kernel/sched/check_sanity/check_sanity.ref.c @@ -11,18 +11,25 @@ #include #include #include +#include u64_t H2K_check_sanity(const u64_t retval) { - if (H2K_gp->priomask == 0) { - H2K_lowprio_notify(); - } - if (H2K_runlist_worst_prio() IS_WORSE_THAN H2K_ready_best_prio()) { - resched_int(); - } else if (H2K_gp->wait_mask && H2K_ready_any_valid()) { - resched_int(); - } - return(retval); + if (H2K_gp->priomask == 0) { + H2K_lowprio_notify(); + } + + u32_t best = H2K_ready_best_prio(); + H2K_set_bestwait(best); // This is blind for waiting thread- with prio -1, ie 255, and ready thread with prio 255- This won't fire an interrupt + + if (H2K_get_bestwait() == BESTWAIT_MASK) { //bestwait fired + return(retval); + } + + if (H2K_gp->wait_mask && best < MAX_PRIOS) { //We reach here only if ready thread's prio is 255. If it was 254 bestwait see waiting's prio 255 > 254 and fires. + resched_int(); + } + return(retval); } u64_t H2K_check_sanity_unlock(const u64_t retval) diff --git a/kernel/sched/check_sanity/test/tests/H2K_bestwait/Makefile b/kernel/sched/check_sanity/test/tests/H2K_bestwait/Makefile new file mode 100644 index 000000000..71b74048c --- /dev/null +++ b/kernel/sched/check_sanity/test/tests/H2K_bestwait/Makefile @@ -0,0 +1,7 @@ +STANDALONE=1 + +OBJS+=test.o +EXEC=test.elf + +include Makefile.inc +OSLIB= diff --git a/kernel/sched/check_sanity/test/tests/H2K_bestwait/Makefile.inc b/kernel/sched/check_sanity/test/tests/H2K_bestwait/Makefile.inc new file mode 100644 index 000000000..81cc0e9a7 --- /dev/null +++ b/kernel/sched/check_sanity/test/tests/H2K_bestwait/Makefile.inc @@ -0,0 +1,5 @@ +# Need to define how to get back to the main H2 dir +H2DIR=${UPDIR}../../../../../.. + +# Everything else defined here +include ${H2DIR}/scripts/Makefile.inc.test diff --git a/kernel/sched/check_sanity/test/tests/H2K_bestwait/test.c b/kernel/sched/check_sanity/test/tests/H2K_bestwait/test.c new file mode 100644 index 000000000..766758964 --- /dev/null +++ b/kernel/sched/check_sanity/test/tests/H2K_bestwait/test.c @@ -0,0 +1,208 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: BSD-3-Clause-Clear + */ + +/* + * bestwait_sim_check + * ================== + * Demonstrates that the simulator (hexagon-sim + devsim_v81.cfg) does NOT model + * the BESTWAIT/SCHEDCFG hardware reschedule-interrupt comparator described in + * the V85 spec (80-V9418-400) section 6.5. + * + * Per the spec: + * - BESTWAIT holds the priority of the best task waiting to run. + * - When the effective priority of ANY hardware thread (STID.PRIO) is worse + * than BESTWAIT, the hardware raises the reschedule interrupt (the interrupt + * number programmed into SCHEDCFG.INTNO) and resets BESTWAIT to 0x1FF. + * - IPEND reflects pending interrupts; "the hardware automatically sets bits + * in this register when an interrupt is received or delivered." + * + * This test arranges the exact trigger condition and checks: + * PART A: the BESTWAIT and SCHEDCFG registers can be read/written (storage). + * PART B: with the feature enabled and this thread's STID.PRIO made WORSE than + * BESTWAIT, the hardware raises RESCHED_INT and resets BESTWAIT. + * PART C: negative case - it does NOT fire when no thread is worse. + * PART D: it does NOT fire when SCHEDCFG.EN=0 (the feature must be enabled, + * which the boot path H2K_init_setup normally does). + * + * Result observed on the current sim: ALL PASS - the comparator IS modeled. + * (An earlier conclusion that it was not modeled was a measurement error: with + * interrupts enabled the posted RESCHED_INT is immediately taken and its IPEND + * bit auto-clears, so it reads back as 0. This test disables interrupts first.) + * + * Interrupts are kept globally disabled (clear_gie) so that, if the interrupt + * is posted, it stays pending in IPEND for us to observe rather than being + * taken (which would auto-clear the IPEND bit per spec 6.1). + */ + +#include +#include +#include +#include +#include + +/* STID.PRIORITY is bits [23:16]; 0 = highest priority, 0xFF = lowest. */ +#define STID_PRIO_SHIFT 16 + +static int failures; + +static void check(const char *what, int ok) +{ + printf(" [%s] %s\n", ok ? "PASS" : "FAIL", what); + if (!ok) + failures++; +} + +int main() +{ + u32_t bw, sc, ipend; + + /* Keep interrupts globally disabled so a posted RESCHED_INT remains pending + * in IPEND (taken interrupts auto-clear their IPEND bit; see spec 6.1). */ + H2K_clear_gie(); + H2K_clear_ipend(0xffffffff); + + puts("PART A: BESTWAIT / SCHEDCFG behave as readable/writable registers"); + + H2K_set_bestwait(0x123); + asm volatile ("isync"); + bw = H2K_get_bestwait(); + printf(" BESTWAIT: wrote 0x123, read 0x%x\n", bw); + check("BESTWAIT round-trips", bw == 0x123); + + sc = SCHEDCFG_EN | SCHEDCFG_INTNO(RESCHED_INT); + H2K_set_schedcfg(sc); + asm volatile ("isync"); + printf(" SCHEDCFG: wrote 0x%x, read 0x%x\n", sc, H2K_get_schedcfg()); + check("SCHEDCFG round-trips", H2K_get_schedcfg() == sc); + + puts("PART B: hardware comparator should raise RESCHED_INT when a thread's " + "STID.PRIO is worse than BESTWAIT"); + + /* Make THIS thread look like the worst-priority running thread (prio 200). */ + asm volatile ("stid = %0; isync" : : "r"(200u << STID_PRIO_SHIFT)); + + /* Enable the feature and arm BESTWAIT with a BETTER priority (50). + * 200 (us) is worse than 50 (bestwait) -> the spec says fire. */ + H2K_set_schedcfg(SCHEDCFG_EN | SCHEDCFG_INTNO(RESCHED_INT)); + asm volatile ("isync"); + H2K_set_bestwait(50); + asm volatile ("isync"); + + ipend = H2K_get_ipend(); + bw = H2K_get_bestwait(); + printf(" armed: STID.PRIO=200, BESTWAIT=50, SCHEDCFG.EN=1, INTNO=%d\n", + RESCHED_INT); + printf(" IPEND=0x%x (RESCHED bit %d), BESTWAIT now 0x%x\n", + ipend, (ipend & RESCHED_INT_INTMASK) ? 1 : 0, bw); + + check("hardware posted RESCHED_INT into IPEND", + (ipend & RESCHED_INT_INTMASK) != 0); + check("hardware reset BESTWAIT to 0x1FF after firing", bw == 0x1ff); + + puts("PART C: negative case - comparator must NOT fire when no thread is " + "worse than BESTWAIT"); + + /* Make this thread a GOOD priority (10) and arm BESTWAIT WORSE (50). + * 10 (us) is better than 50 (bestwait) -> nothing is worse -> no fire. */ + H2K_clear_ipend(0xffffffff); + asm volatile ("stid = %0; isync" : : "r"(10u << STID_PRIO_SHIFT)); + H2K_set_bestwait(50); + asm volatile ("isync"); + ipend = H2K_get_ipend(); + bw = H2K_get_bestwait(); + printf(" armed: STID.PRIO=10, BESTWAIT=50\n"); + printf(" IPEND=0x%x (RESCHED bit %d), BESTWAIT now 0x%x\n", + ipend, (ipend & RESCHED_INT_INTMASK) ? 1 : 0, bw); + check("comparator did NOT post RESCHED_INT (no thread worse)", + (ipend & RESCHED_INT_INTMASK) == 0); + check("BESTWAIT retained its value (not auto-reset)", bw == 50); + + puts("PART D: does the feature require SCHEDCFG.EN? (mimics the scenario " + "unit test, which never calls H2K_init_setup and so never enables it)"); + + /* Clear only the EN bit (keep INTNO) to confirm EN specifically gates + * the feature -- not just any SCHEDCFG write. */ + H2K_set_schedcfg(~SCHEDCFG_EN | SCHEDCFG_INTNO(RESCHED_INT)); + asm volatile ("isync"); + H2K_clear_ipend(0xffffffff); + asm volatile ("stid = %0; isync" : : "r"(200u << STID_PRIO_SHIFT)); + H2K_set_bestwait(50); + asm volatile ("isync"); + ipend = H2K_get_ipend(); + bw = H2K_get_bestwait(); + printf(" SCHEDCFG=~EN|INTNO (EN=0, INTNO kept), STID.PRIO=200, BESTWAIT=50\n"); + printf(" IPEND=0x%x (RESCHED bit %d), BESTWAIT now 0x%x\n", + ipend, (ipend & RESCHED_INT_INTMASK) ? 1 : 0, bw); + printf(" OBSERVATION: with EN=0, comparator fires=%d " + "(if 0, SCHEDCFG.EN is REQUIRED)\n", + (ipend & RESCHED_INT_INTMASK) ? 1 : 0); + check("comparator does NOT fire when SCHEDCFG.EN=0", + (ipend & RESCHED_INT_INTMASK) == 0); + + puts("PART E: hardware steering delivers RESCHED to a qualified thread whose " + "RESCHED imask bit is clear (the boot/hw-steering model)"); + + /* Model the hw-steering setup: feature enabled, this thread's RESCHED imask + * bit cleared via iassignw (every thread qualified), STID.PRIO made worse + * than BESTWAIT. The hardware should steer RESCHED_INT to this thread and + * post it in IPEND. Re-enable interrupts briefly is NOT done here (we keep + * GIE off so the posted bit stays observable in IPEND). */ + H2K_set_schedcfg(SCHEDCFG_EN | SCHEDCFG_INTNO(RESCHED_INT)); + asm volatile ("isync"); + iassignw(RESCHED_INT, 0); /* clear RESCHED imask bit on all threads */ + H2K_clear_ipend(0xffffffff); + asm volatile ("stid = %0; isync" : : "r"(200u << STID_PRIO_SHIFT)); + H2K_set_bestwait(50); + asm volatile ("isync"); + ipend = H2K_get_ipend(); + printf(" steered: RESCHED imask clear, STID.PRIO=200, BESTWAIT=50 -> " + "IPEND=0x%x (RESCHED %d)\n", + ipend, (ipend & RESCHED_INT_INTMASK) ? 1 : 0); + check("RESCHED steered to this qualified thread", + (ipend & RESCHED_INT_INTMASK) != 0); + + puts("PART F: the real 255-gap scenario -- simulate a sleeping hthread.\n" + " A sleeping hthread has stid=-1 (STID.PRIO=255) AND the sleep path\n" + " opened its imask. The only ready thread has prio=255. check_sanity\n" + " writes BESTWAIT=255. Hardware: 255>255=false -- does NOT fire.\n" + " Software backstop (wait_mask && best backstop fires resched_int() to wake the sleeping hthread."); + + /* Simulate a sleeping hthread by faking wait_mask (we can't actually park + * a second hthread in this single-threaded test, but we can verify the + * hardware comparator side: STID.PRIO=255, BESTWAIT=255 does not fire, + * i.e. the software backstop is the only thing that covers this case. */ + H2K_set_schedcfg(SCHEDCFG_EN | SCHEDCFG_INTNO(RESCHED_INT)); + asm volatile ("isync"); + iassignw(RESCHED_INT, 0); + H2K_clear_ipend(0xffffffff); + asm volatile ("stid = %0; isync" : : "r"(255u << STID_PRIO_SHIFT)); + H2K_set_bestwait(255); + asm volatile ("isync"); + ipend = H2K_get_ipend(); + printf(" STID.PRIO=255, BESTWAIT=255 (no wait_mask): IPEND=0x%x (RESCHED %d)\n", + ipend, (ipend & RESCHED_INT_INTMASK) ? 1 : 0); + check("hardware does NOT fire at 255==255 (the gap)", + (ipend & RESCHED_INT_INTMASK) == 0); + + /* Now verify the software backstop fires when wait_mask is set AND best<256. + * best=255 < MAX_PRIOS(256) is true, so the condition triggers resched_int(). */ + H2K_clear_ipend(0xffffffff); + swi(RESCHED_INT_INTMASK); /* simulate what the software backstop does */ + ipend = H2K_get_ipend(); + printf(" after software resched_int(): IPEND=0x%x (RESCHED %d)\n", + ipend, (ipend & RESCHED_INT_INTMASK) ? 1 : 0); + check("software backstop covers the 255==255 gap", + (ipend & RESCHED_INT_INTMASK) != 0); + + if (failures == 0) { + puts("TEST PASSED"); + return 0; + } + + /* If PART B fails, the demonstration is that the sim is storage-only. */ + puts("TEST FAILED"); + return 1; +} diff --git a/kernel/sched/check_sanity/test/tests/H2K_check_sanity/scenarios/test.c b/kernel/sched/check_sanity/test/tests/H2K_check_sanity/scenarios/test.c index d0a292b2b..a36cd8c85 100644 --- a/kernel/sched/check_sanity/test/tests/H2K_check_sanity/scenarios/test.c +++ b/kernel/sched/check_sanity/test/tests/H2K_check_sanity/scenarios/test.c @@ -100,11 +100,13 @@ static void setup(struct scenario *scenario) int hthread; int prio; int i; + int worst_running_prio = 0; /* 0 == best; track max over RUNNING threads */ H2K_clear_ipend(0xffffffff); H2K_readylist_init(); H2K_runlist_init(); H2K_lowprio_init(); + H2K_set_schedcfg(SCHEDCFG_EN | SCHEDCFG_INTNO(RESCHED_INT)); for (i = 0; i < num_threads; i++) { status = scenario->thread[i].status; hthread = scenario->thread[i].hthread; @@ -114,10 +116,27 @@ static void setup(struct scenario *scenario) threads[i].prio = prio; if (status == RUNNING) { H2K_runlist_push(&threads[i]); + if (prio > worst_running_prio) + worst_running_prio = prio; } else if (status == READY) { H2K_ready_append(&threads[i]); } } + /* The BESTWAIT comparator reads each hw thread's REAL STID.PRIO (bits + * [23:16]), not the software runlist_prios[] array that H2K_runlist_push + * fills. This test runs on a single hw thread, so drive that thread's real + * STID.PRIO to the scenario's worst running priority -- the only running + * priority that affects the decision (hardware fires iff some thread's + * STID.PRIO is worse than BESTWAIT, i.e. worst_running > best_ready). + * + * The comparator is level-sensitive, so disarm BESTWAIT (0x1FF) BEFORE + * writing STID, otherwise a stale BESTWAIT left by the previous scenario + * would fire spuriously the instant we install a worse STID.PRIO. Clear + * IPEND last so setup() leaves a clean slate; only H2K_check_sanity (under + * test) should arm BESTWAIT and raise the interrupt. */ + H2K_set_bestwait(BESTWAIT_MASK); + asm volatile ("stid = %0;" : : "r"((u32_t)worst_running_prio << 16)); + H2K_clear_ipend(0xffffffff); H2K_kg.priomask = scenario->priomask; H2K_kg.wait_mask = scenario->wait_mask; } diff --git a/kernel/util/hw/hw.h b/kernel/util/hw/hw.h index bdcc6b5cb..a63da5206 100644 --- a/kernel/util/hw/hw.h +++ b/kernel/util/hw/hw.h @@ -56,6 +56,13 @@ static inline u32_t get_imask(u32_t thread) return imask; } +/* Write STID.PRIO for a named hardware thread (cross-thread, Monitor only). + * Mirrors setimask -- uses predicate register to name the target thread. */ +static inline void set_thread_stid_prio(u32_t thread, u32_t prio) +{ + asm volatile (" p0 = %0\n setprio(p0,%1)" : : "r"(thread),"r"(prio):"p0"); +} + static inline void iassignw(u32_t intno, u32_t threadmask) { asm(" iassignw(%0)" : : "r"(Q6_R_combine_RlRl(intno,threadmask))); @@ -501,6 +508,30 @@ static inline void H2K_dmcfgwr(u32_t index, u32_t data) { asm volatile ("dmcfgwr(%0, %1);" : : "r" (index), "r" (data)); } + +static inline u32_t H2K_get_bestwait() +{ + u32_t ret; + asm volatile ("%0 = bestwait; \n" : "=r" (ret)); + return(ret); +} + +static inline void H2K_set_bestwait(u32_t priority_mask) +{ + asm volatile (" bestwait = %0; \n" : : "r" (priority_mask)); +} + +static inline u32_t H2K_get_schedcfg() +{ + u32_t ret; + asm volatile ("%0 = schedcfg; \n" : "=r" (ret)); + return(ret); +} + +static inline void H2K_set_schedcfg(u32_t val) +{ + asm volatile (" schedcfg = %0; \n" : : "r" (val)); +} #endif void H2K_start_threads(unsigned int mask); diff --git a/kernel/util/hw/hw.spec b/kernel/util/hw/hw.spec index 60920d84f..a3c4ef84d 100644 --- a/kernel/util/hw/hw.spec +++ b/kernel/util/hw/hw.spec @@ -248,4 +248,63 @@ Description This function retrieves clears the contents of the IPEND status register. +H2K_get_bestwait +---------------- + +.. c:function:: static inline u32_t H2K_get_bestwait() + +Description +~~~~~~~~~~~ + +This function retrieves the contents of the BESTWAIT register. The hardware +resets BESTWAIT to 0x1FF after it raises the reschedule interrupt, so reading it +back indicates whether the hardware has already fired since it was last +programmed. See the hardware reschedule interrupt. + + +H2K_set_bestwait +---------------- + +.. c:function:: static inline void H2K_set_bestwait(u32_t priority_mask) + + :param priority_mask: Priority value to program into BESTWAIT + +Description +~~~~~~~~~~~ + +This function programs the BESTWAIT register with the priority of the highest +priority task waiting to run. The hardware raises the reschedule interrupt when +any hardware thread's effective STID.PRIO is worse than this value. See the +hardware reschedule interrupt in conjunction with the SCHEDCFG register. + + +H2K_get_schedcfg +---------------- + +.. c:function:: static inline u32_t H2K_get_schedcfg() + +Description +~~~~~~~~~~~ + +This function retrieves the contents of the SCHEDCFG register, which configures +the hardware reschedule interrupt feature (SCHEDCFG.EN enables it, SCHEDCFG.INTNO +selects the interrupt raised). + + +H2K_set_schedcfg +---------------- + +.. c:function:: static inline void H2K_set_schedcfg(u32_t val) + + :param val: Value to write to the SCHEDCFG register + +Description +~~~~~~~~~~~ + +This function writes the SCHEDCFG register, used to enable the hardware +reschedule interrupt feature and select which interrupt it raises. See the +hardware reschedule interrupt in conjunction with the BESTWAIT register. + + + diff --git a/kernel/util/max/max.h b/kernel/util/max/max.h index 3a27fab04..71a3a7f8c 100644 --- a/kernel/util/max/max.h +++ b/kernel/util/max/max.h @@ -79,6 +79,11 @@ #define MAX_PRIO ((MAX_PRIOS) - 1) #define BEST_PRIO 0 +#define BESTWAIT_MASK 0x1ff + +#define SCHEDCFG_EN (1u << 8) +#define SCHEDCFG_INTNO(n) ((n) & 0xf) + #if ARCHV <= 3 #define ASID_BITS 5 #else diff --git a/scripts/testlist.v61 b/scripts/testlist.v61 index b489fe7cf..1ecef6cb7 100644 --- a/scripts/testlist.v61 +++ b/scripts/testlist.v61 @@ -34,6 +34,7 @@ ./kernel/power/hvx/test ./kernel/power/apcr/simple_test ./kernel/sched/check_sanity/test/tests/H2K_check_sanity/scenarios +./kernel/sched/check_sanity/test/tests/H2K_bestwait ./kernel/sched/dosched/test/test ./kernel/futex/futex/test/tests/badaccess ./kernel/futex/futex/test/tests/find_match From 6262329b9019c23392e9fe63e9cfd24bafc2ec04 Mon Sep 17 00:00:00 2001 From: Zeev Belinsky Date: Mon, 13 Jul 2026 15:56:53 -0700 Subject: [PATCH 2/3] sched: switch to BESTWAIT hardware steering, retire software lowprio Replace the software low-priority steering (priomask / lowprio_imask / H2K_runlist_worst_prio) with the hardware BESTWAIT + SCHEDCFG[8] priority comparator. check_sanity now arms BESTWAIT with the best ready priority and lets hardware post RESCHED to the worst-priority (STID.PRIO) hardware thread; a software resched_int() backstop covers the 255==255 case the comparator cannot fire on- will be removed in the upcoming changes. Boot opens IMASK (imask=0) so every thread is a steering candidate, and setup drops H2K_lowprio_init. runlist_worst_prio/_hthread, lowprio_notify/raise, highprio/lowprio_imask, and the priomask bookkeeping in dosched/check_sanity are removed. The runlist[]/runlist_prios[] mirror arrays are no longer read by the scheduler. MAX_PRIO is split into WAITING_PRIO (255, the idle/sentinel priority) and MAX_READY_PRIO (254, the cap for runnable threads) to match the comparator's strict-greater semantics- will be applied in upcoming changes. runlist and lowprio unit tests are disabled in the testlist accordingly. Known scheduler-callsite races (documented in-tree, not yet root-caused): - dosched go-to-sleep: change_imask(hthread,0) is issued before H2K_switch. It is logically redundant with the imask=0 the switch sleep path already does, but removing it loses wakeups -- the thread must become a valid RESCHED steering target (IMASK[RESCHED]=0) early enough for a producer's set_bestwait to steer to it. Without it the futex/pi test hangs. - dosched H2K_runlist_push(new), and the H2K_runlist_remove(me) in yield and resched: these now write to throwaway wip_dummy_runlist[] arrays that nothing reads. The writes are retained only because removing them hangs the futex_pi test: the extra stores shift interrupt-arrival timing on the scheduler hot path enough to keep a wakeup/steering window from being lost. This is a timing shim over a real lost-wakeup race in the ready-append -> set_bestwait -> steer sequence, not a data dependency; every other callsite is fine without them. - The H2K_runlist_remove(me) calls in yield.ref.c and resched.ref.c are the same case: dropping their dummy-array writes also hangs the futex_pi test, for the same timing reason as the dosched push above. - The whole H2K_runlist mechanism (push/remove/set_thread_prio and the runlist[]/runlist_prios[] arrays) could be removed entirely across all callsites once this timing sensitivity is fixed: the arrays are write-only now (nothing reads them), so they carry no data dependency -- only the incidental instruction-timing that currently masks the race. v81/ref: 135 tests pass, including kernel/futex/futex/test/tests/pi. Other test_variants havent been tested. Signed-off-by: Zeev Belinsky --- .../checker_runlist/checker_runlist.check.c | 2 +- kernel/data/globals/globals.h | 2 + kernel/data/readylist/readylist.h | 2 +- kernel/data/runlist/runlist.h | 39 ++-------- kernel/event/intpool/intpool.ref.c | 4 - kernel/event/intpool/test/test.c | 21 +----- kernel/event/popup/popup.ref.c | 8 -- kernel/event/popup/test/test.c | 13 +--- .../futex/futex/test/tests/multi_wake/main.c | 2 +- .../futex/futex_classic/futex_classic.ref.c | 1 - kernel/futex/futex_pi/futex_pi.ref.c | 4 - kernel/init/boot/boot.ref.S | 3 +- kernel/init/setup/setup.ref.c | 5 +- kernel/init/setup/test/test.c | 20 +---- kernel/sched/check_sanity/check_sanity.ref.c | 4 - .../test/tests/H2K_bestwait/test.c | 75 +++++++------------ .../H2K_check_sanity/scenarios/scenarios.py | 36 ++------- .../tests/H2K_check_sanity/scenarios/test.c | 25 ------- kernel/sched/dosched/dosched.ref.c | 24 ++---- kernel/sched/dosched/test/test/harness.c | 69 +---------------- kernel/sched/lowprio/lowprio.h | 34 ++++----- kernel/sched/lowprio/lowprio.ref.c | 2 +- kernel/sched/resched/resched.ref.c | 3 +- kernel/sched/resched/test/test.c | 5 +- kernel/sched/yield/test/test.c | 4 +- kernel/sched/yield/yield.ref.c | 4 +- kernel/thread/create/create.ref.c | 2 +- kernel/thread/create/test/test.c | 3 +- kernel/thread/stop/stop.ref.c | 1 - kernel/thread/stop/test/test.c | 16 +--- kernel/traps/config/config.ref.c | 2 +- kernel/traps/config/test/test.c | 2 +- kernel/traps/hwconfig/hwconfig.ref.c | 3 - kernel/traps/prio/prio.ref.c | 21 +----- kernel/traps/prio/test/test.c | 10 ++- kernel/util/hw/hw.h | 25 +------ kernel/util/max/max.h | 3 +- kernel/util/stmode/test/test.c | 2 +- kernel/vm/vmfuncs/test/test.c | 4 - kernel/vm/vmfuncs/vmfuncs.ref.c | 1 - scripts/testlist.v61 | 4 +- 41 files changed, 104 insertions(+), 406 deletions(-) diff --git a/kernel/checkers/checker_runlist/checker_runlist.check.c b/kernel/checkers/checker_runlist/checker_runlist.check.c index d34cea562..499bbe69f 100644 --- a/kernel/checkers/checker_runlist/checker_runlist.check.c +++ b/kernel/checkers/checker_runlist/checker_runlist.check.c @@ -14,7 +14,7 @@ s32_t checker_runlist() { u32_t i; for (i = 0; i < H2K_gp->hthreads; i++) { - if (0 <= H2K_gp->runlist_prios[i] && H2K_gp->runlist_prios[i] <= MAX_PRIO) { + if (0 <= H2K_gp->runlist_prios[i] && H2K_gp->runlist_prios[i] <= MAX_READY_PRIO) { if (H2K_gp->runlist_prios[i] != H2K_gp->runlist[i]->prio) FAIL("runlist_prios does not match priority of scheduled thread"); } else { if (H2K_gp->runlist[i]) FAIL("Priority out of range but readylist non-null"); diff --git a/kernel/data/globals/globals.h b/kernel/data/globals/globals.h index c034daf3b..e95a3adb8 100644 --- a/kernel/data/globals/globals.h +++ b/kernel/data/globals/globals.h @@ -146,6 +146,8 @@ typedef struct { #endif H2K_thread_context *runlist[MAX_HTHREADS]; s16_t runlist_prios[(MAX_HTHREADS+7)/8*8] __attribute__((aligned(8))); + H2K_thread_context *wip_dummy_runlist[MAX_HTHREADS]; + s16_t wip_dummy_runlist_prios[(MAX_HTHREADS+7)/8*8] __attribute__((aligned(8))); H2K_vmblock_t *vmblocks[H2K_ID_MAX_VMS]; u32_t phys_offset; u32_t build_id; diff --git a/kernel/data/readylist/readylist.h b/kernel/data/readylist/readylist.h index 5b6d8ca37..145f3d806 100644 --- a/kernel/data/readylist/readylist.h +++ b/kernel/data/readylist/readylist.h @@ -41,7 +41,7 @@ static inline u32_t H2K_ready_any_valid() /* Check whether a thread at a given priority is ready */ static inline u32_t H2K_ready_prio_valid(u32_t prio) { - if (prio > MAX_PRIO) return 0; + if (prio > WAITING_PRIO) return 0; return (H2K_gp->ready_valids[prio >> 6] & (1ULL << (prio & 0x3f))) != 0; } diff --git a/kernel/data/runlist/runlist.h b/kernel/data/runlist/runlist.h index 8725971c8..ace6dc5d7 100644 --- a/kernel/data/runlist/runlist.h +++ b/kernel/data/runlist/runlist.h @@ -11,55 +11,28 @@ #include #include #include +#include static inline void H2K_runlist_push(H2K_thread_context *newthread) { u32_t hthread = newthread->hthread; u32_t prio = newthread->prio; newthread->status = H2K_STATUS_RUNNING; - H2K_gp->runlist[hthread] = newthread; - H2K_gp->runlist_prios[hthread] = (s16_t)prio; -} - -static inline u32_t H2K_runlist_worst_prio() -{ - s32_t worst_prio = -1; - s32_t hthread = -1; - s32_t i; - for (i = 0; i < H2K_gp->hthreads; i++) { - if (H2K_gp->runlist_prios[i] IS_WORSE_THAN worst_prio) { - worst_prio = H2K_gp->runlist_prios[i]; - hthread = i; - } - } - return hthread == -1 ? MAX_PRIOS : (u32_t)worst_prio; -} - -static inline u32_t H2K_runlist_worst_prio_hthread() -{ - s32_t worst_prio = -1; - s32_t hthread = -1; - s32_t i; - for (i = 0; i < H2K_gp->hthreads; i++) { - if (H2K_gp->runlist_prios[i] IS_WORSE_THAN worst_prio) { - worst_prio = H2K_gp->runlist_prios[i]; - hthread = i; - } - } - return (u32_t)hthread; + H2K_gp->wip_dummy_runlist[hthread] = newthread; + H2K_gp->wip_dummy_runlist_prios[hthread] = (s16_t)prio; } static inline void H2K_runlist_remove(H2K_thread_context *thread) { u32_t hthread = thread->hthread; - H2K_gp->runlist[hthread] = NULL; - H2K_gp->runlist_prios[hthread] = -1; + H2K_gp->wip_dummy_runlist[hthread] = NULL; + H2K_gp->wip_dummy_runlist_prios[hthread] = -1; } static inline void H2K_runlist_set_thread_prio(H2K_thread_context *thread, u32_t new_prio) { thread->prio = (u8_t)new_prio; - H2K_gp->runlist_prios[thread->hthread] = (s16_t)new_prio; + H2K_gp->wip_dummy_runlist_prios[thread->hthread] = (s16_t)new_prio; } void H2K_runlist_init(void) IN_SECTION(".text.init.runlist"); diff --git a/kernel/event/intpool/intpool.ref.c b/kernel/event/intpool/intpool.ref.c index 15826fe27..1d1a77d05 100644 --- a/kernel/event/intpool/intpool.ref.c +++ b/kernel/event/intpool/intpool.ref.c @@ -35,13 +35,10 @@ void H2K_intpool_int(u32_t intnum, H2K_thread_context *me, u32_t hwtnum, H2K_vmb H2K_ring_remove(&vmblock->intpool,woken); woken->r00 = intnum; if (me != NULL) { - H2K_runlist_remove(me); H2K_ready_append(me); } else { H2K_gp->wait_mask = (u32_t)Q6_R_clrbit_RR(H2K_gp->wait_mask,hwtnum); } - H2K_gp->priomask = (u32_t)Q6_R_clrbit_RR(H2K_gp->priomask,hwtnum); - highprio_imask(hwtnum); H2K_runlist_push(woken); H2K_switch(me,woken); } @@ -88,7 +85,6 @@ int H2K_intpool_wait(u32_t int_ack_num, H2K_thread_context *me) } } /* FIXME: check pending intpool interrupts */ - H2K_runlist_remove(me); H2K_ring_append(&vmblock->intpool,me); me->status = H2K_STATUS_INTBLOCKED; /* OR INTPOOL_BLOCKED? */ me->r00 = (u32_t)-1; diff --git a/kernel/event/intpool/test/test.c b/kernel/event/intpool/test/test.c index a58c1f700..44e6fb6e1 100644 --- a/kernel/event/intpool/test/test.c +++ b/kernel/event/intpool/test/test.c @@ -129,10 +129,7 @@ int TH_call_intpool_wait(int i, H2K_thread_context *current) */ void TH_check_waiting(int i, H2K_thread_context *thread) { - //if (H2K_gp->inthandlers[i].handler != H2K_intpool_int) FAIL("Wrong handler"); - //if (H2K_gp->inthandlers[i].param != thread) FAIL("Wrong thread"); if (thread->status != H2K_STATUS_INTBLOCKED) FAIL("Wrong status"); - if (H2K_gp->runlist[thread->hthread] == thread) FAIL("Thread still scheduled"); } /* @@ -140,14 +137,9 @@ void TH_check_waiting(int i, H2K_thread_context *thread) */ void TH_check_running(int i, H2K_thread_context *thread) { -#if 0 - printf("Checking i=%d thread=%p inthandlers[i].handler=%p inthandlers[i].param=%p\n", - i,thread,H2K_gp->inthandlers[i].handler,H2K_gp->inthandlers[i].param); -#endif if (H2K_gp->inthandlers[i].handler != NULL) FAIL("Handler set"); if (H2K_gp->inthandlers[i].param == thread) FAIL("Handler thread set"); if (thread->status != H2K_STATUS_RUNNING) FAIL("Wrong status"); - if (H2K_gp->runlist[thread->hthread] != thread) FAIL("Thread not scheduled"); } void TH_check_old(H2K_thread_context *thread) @@ -184,26 +176,19 @@ void TH_intpool_int(int i, H2K_thread_context *interrupted, int hthread, H2K_vmb void TH_set_idle(int hthread) { H2K_gp->wait_mask = 1<priomask = 1<wait_mask &= ~(1<priomask &= ~(1<wait_mask) FAIL("wait_mask still set"); - if ((1<<(hthread)) & H2K_gp->priomask) FAIL("priomask still set"); - if ((get_imask(thread->hthread)) == 0) { + if ((get_imask(thread->hthread)) != 0) { printf("ht=%x tht=%x imask=%x\n",hthread, thread->hthread, get_imask(thread->hthread)); - FAIL("IMASK clear for hthread"); + FAIL("IMASK set for hthread"); } } @@ -255,8 +240,6 @@ int main() H2K_gp->l2_ack_base = fakeint+(0x200/sizeof(u32_t)); #endif H2K_readylist_init(); - H2K_runlist_init(); - H2K_lowprio_init(); a.prio = b.prio = c.prio = MAX_PRIOS - 30; a.vmblock = b.vmblock = c.vmblock = vmblock; diff --git a/kernel/event/popup/popup.ref.c b/kernel/event/popup/popup.ref.c index a7a2e74e6..ecfe1e260 100644 --- a/kernel/event/popup/popup.ref.c +++ b/kernel/event/popup/popup.ref.c @@ -27,17 +27,10 @@ void H2K_popup_int(u32_t intnum, H2K_thread_context *me, u32_t hwtnum, H2K_threa return; } if (me != NULL) { - H2K_runlist_remove(me); H2K_ready_append(me); } else { H2K_gp->wait_mask = (u32_t)Q6_R_clrbit_RR(H2K_gp->wait_mask,hwtnum); } - /* Assume woken is better than interrupted thread. - * If not, check_sanity will detect. - * Let check_sanity find the new lowprio thread also... - */ - H2K_gp->priomask = (u32_t)Q6_R_clrbit_RR(H2K_gp->priomask,hwtnum); - highprio_imask(hwtnum); H2K_runlist_push(woken); H2K_switch(me,woken); } @@ -62,7 +55,6 @@ int H2K_popup_wait(u32_t intnum, H2K_thread_context *me) } H2K_gp->inthandlers[intnum].param = me; H2K_gp->inthandlers[intnum].handler = H2K_popup_int; - H2K_runlist_remove(me); me->status = H2K_STATUS_INTBLOCKED; me->r0100 = intnum; H2K_intcontrol_enable(intnum); diff --git a/kernel/event/popup/test/test.c b/kernel/event/popup/test/test.c index 50f079e7d..e87a6e66c 100644 --- a/kernel/event/popup/test/test.c +++ b/kernel/event/popup/test/test.c @@ -130,7 +130,6 @@ void TH_check_waiting(int i, H2K_thread_context *thread) if (H2K_gp->inthandlers[i].handler != H2K_popup_int) FAIL("Wrong handler"); if (H2K_gp->inthandlers[i].param != thread) FAIL("Wrong thread"); if (thread->status != H2K_STATUS_INTBLOCKED) FAIL("Wrong status"); - if (H2K_gp->runlist[thread->hthread] == thread) FAIL("Thread still scheduled"); } /* @@ -145,7 +144,6 @@ void TH_check_running(int i, H2K_thread_context *thread) if (H2K_gp->inthandlers[i].handler != NULL) FAIL("Handler set"); if (H2K_gp->inthandlers[i].param == thread) FAIL("Handler thread set"); if (thread->status != H2K_STATUS_RUNNING) FAIL("Wrong status"); - if (H2K_gp->runlist[thread->hthread] != thread) FAIL("Thread not scheduled"); } void TH_check_old(H2K_thread_context *thread) @@ -190,9 +188,6 @@ void TH_popup_int(int i, H2K_thread_context *interrupted, int hthread, H2K_threa void TH_set_idle(int hthread) { H2K_gp->wait_mask = 1<priomask = 1<wait_mask &= ~(1<priomask &= ~(1<wait_mask) FAIL("wait_mask still set"); - if ((1<<(hthread)) & H2K_gp->priomask) FAIL("priomask still set"); - if ((get_imask(thread->hthread)) == 0) FAIL("IMASK clear for hthread"); + if ((get_imask(thread->hthread)) != 0) FAIL("IMASK set for hthread"); } /* @@ -262,8 +253,6 @@ int main() H2K_gp->l2_ack_base = fakeint+(0x200/sizeof(u32_t)); #endif H2K_readylist_init(); - H2K_runlist_init(); - H2K_lowprio_init(); a.prio = b.prio = c.prio = MAX_PRIOS - 30; /* First, test wait */ diff --git a/kernel/futex/futex/test/tests/multi_wake/main.c b/kernel/futex/futex/test/tests/multi_wake/main.c index 535c63d34..d9c74bf58 100644 --- a/kernel/futex/futex/test/tests/multi_wake/main.c +++ b/kernel/futex/futex/test/tests/multi_wake/main.c @@ -221,7 +221,7 @@ void vmmain(void *unused) /* Figure out the max priority that can be woken up */ k = nr_to_wake; - for (j=0; j> 16) == j) { k--; diff --git a/kernel/futex/futex_classic/futex_classic.ref.c b/kernel/futex/futex_classic/futex_classic.ref.c index 2be881dac..ea11bd614 100644 --- a/kernel/futex/futex_classic/futex_classic.ref.c +++ b/kernel/futex/futex_classic/futex_classic.ref.c @@ -42,7 +42,6 @@ s32_t H2K_futex_wait(u32_t *lock, u32_t val, H2K_thread_context *me) return -1; } me->futex_ptr = pa; - H2K_runlist_remove(me); me->r0100 = 0; me->status = H2K_STATUS_BLOCKED; H2K_futex_hash_add_ring(&H2K_gp->futexhash[FUTEX_HASHVAL(pa)],me); diff --git a/kernel/futex/futex_pi/futex_pi.ref.c b/kernel/futex/futex_pi/futex_pi.ref.c index 2f64a81e2..2ac273ceb 100644 --- a/kernel/futex/futex_pi/futex_pi.ref.c +++ b/kernel/futex/futex_pi/futex_pi.ref.c @@ -45,9 +45,6 @@ static inline void H2K_futex_pi_raise(u32_t prio, H2K_id_t destid) /* Sync hardware STID.PRIO so BESTWAIT sees the boosted priority * immediately -- avoids spurious preemption of the holder. */ set_thread_stid_prio(dest->hthread, prio); - if (H2K_gp->priomask & (1<hthread)) { - H2K_raise_lowprio(); - } /* Need to update lowprio */ } else if (dest->status == H2K_STATUS_INTBLOCKED) { /* Waiting on interrupt, but we want it to have high priority when it starts up */ @@ -95,7 +92,6 @@ s32_t H2K_futex_lock_pi(u32_t *lock, H2K_thread_context *me) } H2K_futex_pi_raise(me->prio,x.dest); me->futex_ptr = pa; - H2K_runlist_remove(me); me->r0100 = 0; me->status = H2K_STATUS_BLOCKED; H2K_futex_hash_add_ring(&H2K_gp->futexhash[FUTEX_HASHVAL(pa)],me); diff --git a/kernel/init/boot/boot.ref.S b/kernel/init/boot/boot.ref.S index 2ed83d785..ebc68ef25 100644 --- a/kernel/init/boot/boot.ref.S +++ b/kernel/init/boot/boot.ref.S @@ -348,10 +348,11 @@ boot_continuation: SETCONST(H2K_GP,H2K_kg) /* Set up rising edge triggered */ - TMP = #-1 + TMP = #0 /* Set imask to -1, since we are not lowest priority at boot */ /* (other idle threads) */ imask = TMP + TMP = #-1 #if ARCHV < 65 iel = TMP iahl = TMP diff --git a/kernel/init/setup/setup.ref.c b/kernel/init/setup/setup.ref.c index 931c66c02..c86d19d10 100644 --- a/kernel/init/setup/setup.ref.c +++ b/kernel/init/setup/setup.ref.c @@ -92,9 +92,8 @@ IN_SECTION(".text.init.setup") static H2K_vmblock_t *H2K_init_setup(u32_t multic H2K_l2cache_init(); H2K_tcm_copy(last_tlb_index); H2K_trace_init(); - H2K_runlist_init(); H2K_readylist_init(); - H2K_lowprio_init(); + H2K_gp->wait_mask = 0; H2K_futex_init(); H2K_intconfig_init(ssbase); H2K_set_schedcfg(SCHEDCFG_EN | SCHEDCFG_INTNO(RESCHED_INT)); @@ -227,7 +226,7 @@ IN_SECTION(".text.init.boot") void H2K_thread_boot(u32_t multicore_shift, u32_t #ifdef CLUSTER_SCHED H2K_cluster_config(); #endif - H2K_runlist_push(boot); + H2K_runlist_push(boot); //removing this will make kernel/time/timer/test_h2 to fail H2K_mutex_unlock_tlb(); H2K_switch(NULL,boot); } diff --git a/kernel/init/setup/test/test.c b/kernel/init/setup/test/test.c index 57d213337..5ecc8363d 100644 --- a/kernel/init/setup/test/test.c +++ b/kernel/init/setup/test/test.c @@ -30,9 +30,7 @@ u32_t TH_init_seen; u32_t TH_switch_seen; enum { - runlist_init = 0, - readylist_init, - lowprio_init, + readylist_init = 0, futex_init, intconfig_init, kg_init, @@ -89,9 +87,9 @@ u32_t H2K_trap_config(u32_t configtype, void *ptr, u32_t val2, u32_t val3, u32_t #define HELPER_FUNC(X) void H2K_##X() { TH_init_seen |= 1<< X; } -HELPER_FUNC(runlist_init) + HELPER_FUNC(readylist_init) -HELPER_FUNC(lowprio_init) + HELPER_FUNC(futex_init) //HELPER_FUNC(intconfig_init) void H2K_intconfig_init(u32_t ssbase) { TH_init_seen |= 1<< intconfig_init; } @@ -144,16 +142,12 @@ void H2K_thread_boot(); int main() { u32_t i; - u32_t found_thread; __asm__ __volatile(GLOBAL_REG_STR " = %0 " : : "r"(&H2K_kg)); H2K_gp->logbuf_enable = 0; H2K_gp->hthreads = get_hthreads(); - for (i = 0; i < H2K_gp->hthreads; i++) { - H2K_gp->runlist[i] = 0; - } TH_init_seen = 0; TH_switch_seen = 0; if (setjmp(env) == 0) { @@ -166,14 +160,6 @@ int main() } if (boot->continuation != (H2K_interrupt_restore)) FAIL("Incorrect continuation"); if (boot->trapmask != 0xffffffffU) FAIL("boot thread trapmask"); - found_thread = 0; - for (i = 0; i < H2K_gp->hthreads; i++) { - if (H2K_gp->runlist[i] == boot) { - if (H2K_gp->runlist_prios[i] != 0) FAIL("Didn't push into runlist (0)"); - found_thread = 1; - } - } - if (!found_thread) FAIL("Didn't push into runlist (1)"); puts("TEST PASSED\n"); exit(0); } diff --git a/kernel/sched/check_sanity/check_sanity.ref.c b/kernel/sched/check_sanity/check_sanity.ref.c index 578720caa..a22ecb30c 100644 --- a/kernel/sched/check_sanity/check_sanity.ref.c +++ b/kernel/sched/check_sanity/check_sanity.ref.c @@ -15,10 +15,6 @@ u64_t H2K_check_sanity(const u64_t retval) { - if (H2K_gp->priomask == 0) { - H2K_lowprio_notify(); - } - u32_t best = H2K_ready_best_prio(); H2K_set_bestwait(best); // This is blind for waiting thread- with prio -1, ie 255, and ready thread with prio 255- This won't fire an interrupt diff --git a/kernel/sched/check_sanity/test/tests/H2K_bestwait/test.c b/kernel/sched/check_sanity/test/tests/H2K_bestwait/test.c index 766758964..205142c0c 100644 --- a/kernel/sched/check_sanity/test/tests/H2K_bestwait/test.c +++ b/kernel/sched/check_sanity/test/tests/H2K_bestwait/test.c @@ -64,12 +64,14 @@ int main() H2K_clear_ipend(0xffffffff); puts("PART A: BESTWAIT / SCHEDCFG behave as readable/writable registers"); - - H2K_set_bestwait(0x123); + u32_t garbage_value = 0x123; + H2K_set_bestwait(garbage_value); asm volatile ("isync"); bw = H2K_get_bestwait(); - printf(" BESTWAIT: wrote 0x123, read 0x%x\n", bw); - check("BESTWAIT round-trips", bw == 0x123); + printf(" BESTWAIT: wrote 0x%x, read 0x%x\n", garbage_value, bw); + check("BESTWAIT round-trips", bw == garbage_value); + + H2K_set_bestwait(BESTWAIT_MASK); sc = SCHEDCFG_EN | SCHEDCFG_INTNO(RESCHED_INT); H2K_set_schedcfg(sc); @@ -84,22 +86,23 @@ int main() asm volatile ("stid = %0; isync" : : "r"(200u << STID_PRIO_SHIFT)); /* Enable the feature and arm BESTWAIT with a BETTER priority (50). - * 200 (us) is worse than 50 (bestwait) -> the spec says fire. */ + * 200 (us) is worse than 50 (bestwait) -> the reg fires. */ H2K_set_schedcfg(SCHEDCFG_EN | SCHEDCFG_INTNO(RESCHED_INT)); asm volatile ("isync"); - H2K_set_bestwait(50); + u32_t best_ready = 50; + H2K_set_bestwait(best_ready); asm volatile ("isync"); ipend = H2K_get_ipend(); bw = H2K_get_bestwait(); - printf(" armed: STID.PRIO=200, BESTWAIT=50, SCHEDCFG.EN=1, INTNO=%d\n", - RESCHED_INT); + printf(" armed: STID.PRIO=200, BESTWAIT=0x%x, SCHEDCFG.EN=1, INTNO=%d\n", + best_ready, RESCHED_INT); printf(" IPEND=0x%x (RESCHED bit %d), BESTWAIT now 0x%x\n", ipend, (ipend & RESCHED_INT_INTMASK) ? 1 : 0, bw); check("hardware posted RESCHED_INT into IPEND", (ipend & RESCHED_INT_INTMASK) != 0); - check("hardware reset BESTWAIT to 0x1FF after firing", bw == 0x1ff); + check("hardware reset BESTWAIT to 0x1ff after firing", bw == BESTWAIT_MASK); puts("PART C: negative case - comparator must NOT fire when no thread is " "worse than BESTWAIT"); @@ -108,16 +111,17 @@ int main() * 10 (us) is better than 50 (bestwait) -> nothing is worse -> no fire. */ H2K_clear_ipend(0xffffffff); asm volatile ("stid = %0; isync" : : "r"(10u << STID_PRIO_SHIFT)); - H2K_set_bestwait(50); + best_ready = 50; + H2K_set_bestwait(best_ready); asm volatile ("isync"); ipend = H2K_get_ipend(); bw = H2K_get_bestwait(); - printf(" armed: STID.PRIO=10, BESTWAIT=50\n"); + printf(" armed: STID.PRIO=10, BESTWAIT=0x%x\n", best_ready); printf(" IPEND=0x%x (RESCHED bit %d), BESTWAIT now 0x%x\n", ipend, (ipend & RESCHED_INT_INTMASK) ? 1 : 0, bw); check("comparator did NOT post RESCHED_INT (no thread worse)", (ipend & RESCHED_INT_INTMASK) == 0); - check("BESTWAIT retained its value (not auto-reset)", bw == 50); + check("BESTWAIT retained its value (not auto-reset)", bw == best_ready); puts("PART D: does the feature require SCHEDCFG.EN? (mimics the scenario " "unit test, which never calls H2K_init_setup and so never enables it)"); @@ -128,7 +132,8 @@ int main() asm volatile ("isync"); H2K_clear_ipend(0xffffffff); asm volatile ("stid = %0; isync" : : "r"(200u << STID_PRIO_SHIFT)); - H2K_set_bestwait(50); + best_ready = 50; + H2K_set_bestwait(best_ready); asm volatile ("isync"); ipend = H2K_get_ipend(); bw = H2K_get_bestwait(); @@ -140,6 +145,7 @@ int main() (ipend & RESCHED_INT_INTMASK) ? 1 : 0); check("comparator does NOT fire when SCHEDCFG.EN=0", (ipend & RESCHED_INT_INTMASK) == 0); + check("Bestwait retains its value (not auto-reset) when EN=0", bw == best_ready); puts("PART E: hardware steering delivers RESCHED to a qualified thread whose " "RESCHED imask bit is clear (the boot/hw-steering model)"); @@ -154,48 +160,17 @@ int main() iassignw(RESCHED_INT, 0); /* clear RESCHED imask bit on all threads */ H2K_clear_ipend(0xffffffff); asm volatile ("stid = %0; isync" : : "r"(200u << STID_PRIO_SHIFT)); - H2K_set_bestwait(50); + best_ready = 50; + H2K_set_bestwait(best_ready); asm volatile ("isync"); ipend = H2K_get_ipend(); - printf(" steered: RESCHED imask clear, STID.PRIO=200, BESTWAIT=50 -> " + bw = H2K_get_bestwait(); + printf(" steered: RESCHED imask clear, STID.PRIO=200, BESTWAIT=0x%x -> " "IPEND=0x%x (RESCHED %d)\n", - ipend, (ipend & RESCHED_INT_INTMASK) ? 1 : 0); + best_ready, ipend, (ipend & RESCHED_INT_INTMASK) ? 1 : 0); check("RESCHED steered to this qualified thread", (ipend & RESCHED_INT_INTMASK) != 0); - - puts("PART F: the real 255-gap scenario -- simulate a sleeping hthread.\n" - " A sleeping hthread has stid=-1 (STID.PRIO=255) AND the sleep path\n" - " opened its imask. The only ready thread has prio=255. check_sanity\n" - " writes BESTWAIT=255. Hardware: 255>255=false -- does NOT fire.\n" - " Software backstop (wait_mask && best backstop fires resched_int() to wake the sleeping hthread."); - - /* Simulate a sleeping hthread by faking wait_mask (we can't actually park - * a second hthread in this single-threaded test, but we can verify the - * hardware comparator side: STID.PRIO=255, BESTWAIT=255 does not fire, - * i.e. the software backstop is the only thing that covers this case. */ - H2K_set_schedcfg(SCHEDCFG_EN | SCHEDCFG_INTNO(RESCHED_INT)); - asm volatile ("isync"); - iassignw(RESCHED_INT, 0); - H2K_clear_ipend(0xffffffff); - asm volatile ("stid = %0; isync" : : "r"(255u << STID_PRIO_SHIFT)); - H2K_set_bestwait(255); - asm volatile ("isync"); - ipend = H2K_get_ipend(); - printf(" STID.PRIO=255, BESTWAIT=255 (no wait_mask): IPEND=0x%x (RESCHED %d)\n", - ipend, (ipend & RESCHED_INT_INTMASK) ? 1 : 0); - check("hardware does NOT fire at 255==255 (the gap)", - (ipend & RESCHED_INT_INTMASK) == 0); - - /* Now verify the software backstop fires when wait_mask is set AND best<256. - * best=255 < MAX_PRIOS(256) is true, so the condition triggers resched_int(). */ - H2K_clear_ipend(0xffffffff); - swi(RESCHED_INT_INTMASK); /* simulate what the software backstop does */ - ipend = H2K_get_ipend(); - printf(" after software resched_int(): IPEND=0x%x (RESCHED %d)\n", - ipend, (ipend & RESCHED_INT_INTMASK) ? 1 : 0); - check("software backstop covers the 255==255 gap", - (ipend & RESCHED_INT_INTMASK) != 0); + check("Bestwait retains its value (not auto-reset)", bw == BESTWAIT_MASK); if (failures == 0) { puts("TEST PASSED"); diff --git a/kernel/sched/check_sanity/test/tests/H2K_check_sanity/scenarios/scenarios.py b/kernel/sched/check_sanity/test/tests/H2K_check_sanity/scenarios/scenarios.py index d8961a235..e420ecab8 100644 --- a/kernel/sched/check_sanity/test/tests/H2K_check_sanity/scenarios/scenarios.py +++ b/kernel/sched/check_sanity/test/tests/H2K_check_sanity/scenarios/scenarios.py @@ -19,14 +19,8 @@ # MAX_HTHREADS RUNNING threads, and each RUNNING thread must have a valid # hthread. No two RUNNING threads may have the same hthread. # -# In addition to thread states, we generate a priomask and a wait_mask to be set -# before entering H2K_check_sanity. # -# We also generate an expected output priomask and whether the resched interrupt -# should be raised. The output priomask does not have to exactly match the -# expected output priomask. If the input priomask is zero, then at least one -# bit of the output priomask should be set that is also set in the expected -# output priomask. +# We also generate whether the resched interrupt should be raised. with open('scenarios.h', 'w') as outfile: random.seed(0) @@ -55,7 +49,7 @@ available_threads.remove(hthread) prio = random.randrange(MAX_PRIOS) # We record the hthread and prio of each RUNNING thread in order to later compute - # the worst RUNNING prio and a valid priomask. + # the worst RUNNING prio. running_hthreads.append(hthread) running_prios.append(prio) # We then print out the state of the RUNNING thread. @@ -64,14 +58,12 @@ else: outfile.write('\t ') print('{ RUNNING, %2d, %8d },' % (hthread, prio), file=outfile) - # Next we determine the hthread and prio of each READY thread. We record the ready_valids - # and best READY prio in order to later compute whether the resched interrupt should be raised. - ready_valids = 0 + # Next we determine the hthread and prio of each READY thread. We record the READY prio + # and best in order to later compute whether the resched interrupt should be raised. best_ready_prio = MAX_PRIOS for thread_id in range(num_ready_threads): hthread = random.randrange(-5, 20) prio = random.randrange(MAX_PRIOS) - ready_valids |= 1 << prio if prio < best_ready_prio: best_ready_prio = prio # We then print out the state of the READY thread. @@ -91,24 +83,9 @@ if running_prios[j] == worst_running_prio: worst_prio_running_threads.append(running_hthreads[j]) non_running_threads.remove(running_hthreads[j]) - # Then we compute a possible valid input priomask and the expected output priomask. - priomask = 0 - expected_priomask = 0 - for worst_prio_running_thread in worst_prio_running_threads: - if random.choice((True, False)): - priomask |= 1 << worst_prio_running_thread - expected_priomask |= 1 << worst_prio_running_thread - # Then we compute a possible valid wait_mask. - wait_mask = 0 - for non_running_thread in non_running_threads: - if random.choice((True, False)): - wait_mask |= 1 << non_running_thread # We then compute whether the resched interrupt should be raised. - should_resched = (worst_running_prio > best_ready_prio or (wait_mask != 0 and ready_valids != 0)) + should_resched = (worst_running_prio > best_ready_prio) # Finally, we print out the remaining input and output state. - print('\t 0x%x, // priomask' % priomask, file=outfile) - print('\t 0x%x, // wait_mask' % wait_mask, file=outfile) - print('\t 0x%x, // expected_priomask' % expected_priomask, file=outfile) print('\t %d, // should_resched' % should_resched, file=outfile) print('\t %d, // hthreads' % MAX_HTHREADS, file=outfile) print('\t},', file=outfile) @@ -119,8 +96,5 @@ for i in range(1, NUM_THREADS_IN_SCENARIOS): print('\t { BLOCKED, %2d, %8d },' % (i, i), file=outfile) print(""" }, - 0x0, // priomask - 0x0, // wait_mask - 0x1, // expected_priomask 0, // should_resched }""", file=outfile) diff --git a/kernel/sched/check_sanity/test/tests/H2K_check_sanity/scenarios/test.c b/kernel/sched/check_sanity/test/tests/H2K_check_sanity/scenarios/test.c index a36cd8c85..50e60e63c 100644 --- a/kernel/sched/check_sanity/test/tests/H2K_check_sanity/scenarios/test.c +++ b/kernel/sched/check_sanity/test/tests/H2K_check_sanity/scenarios/test.c @@ -30,9 +30,6 @@ struct thread_state { struct scenario { struct thread_state thread[NUM_THREADS_IN_SCENARIOS]; - int priomask; - int wait_mask; - int expected_priomask; int should_resched; int hthreads; }; @@ -71,9 +68,6 @@ static void print_scenario(struct scenario *scenario) prio = scenario->thread[i].prio; printf("t%-5d %s %-7d %d\n", i, status_string[status], hthread, prio); } - printf("priomask = 0x%x\n", scenario->priomask); - printf("wait_mask = 0x%x\n", scenario->wait_mask); - printf("expected_priomask = 0x%x\n", scenario->expected_priomask); printf("should_resched = %d\n", scenario->should_resched); } @@ -104,8 +98,6 @@ static void setup(struct scenario *scenario) H2K_clear_ipend(0xffffffff); H2K_readylist_init(); - H2K_runlist_init(); - H2K_lowprio_init(); H2K_set_schedcfg(SCHEDCFG_EN | SCHEDCFG_INTNO(RESCHED_INT)); for (i = 0; i < num_threads; i++) { status = scenario->thread[i].status; @@ -137,28 +129,11 @@ static void setup(struct scenario *scenario) H2K_set_bestwait(BESTWAIT_MASK); asm volatile ("stid = %0;" : : "r"((u32_t)worst_running_prio << 16)); H2K_clear_ipend(0xffffffff); - H2K_kg.priomask = scenario->priomask; - H2K_kg.wait_mask = scenario->wait_mask; } /* Checks to see that H2K_check_sanity did its job, given the scenario. */ static void check(struct scenario *scenario) { - int i; - if (scenario->priomask == 0) { - if ((H2K_kg.priomask & scenario->expected_priomask) == 0) { - FAIL("priomask not set for worst priority thread", scenario); - } - for (i = 0; i < H2K_kg.hthreads; i++) { - if (H2K_kg.priomask & 1 << i && get_imask(i) != 0) { - FAIL("failed to clear imask", scenario); - } - } - } else { - if (H2K_kg.priomask != scenario->priomask) { - FAIL("mucked with non-zero priomask", scenario); - } - } if (scenario->should_resched && !resched_requested()) { FAIL("failed to raise resched interrupt", scenario); } diff --git a/kernel/sched/dosched/dosched.ref.c b/kernel/sched/dosched/dosched.ref.c index 92562c7c4..4c596b89c 100644 --- a/kernel/sched/dosched/dosched.ref.c +++ b/kernel/sched/dosched/dosched.ref.c @@ -18,8 +18,9 @@ void H2K_dosched(H2K_thread_context *me,u32_t hthread) H2K_thread_context *new; new = H2K_ready_getbest(hthread); if (new == NULL) { + change_imask(hthread,0); //This is not needed actually. + // Thing is that if we dont put this here-it is not working. /* GO TO SLEEP */ - H2K_raise_lowprio(); /* FIXME: temporary ugly hack for broken 8.7+ compiler -- investigate * whether this is still needed and if a proper direct call can be * restored */ @@ -30,23 +31,12 @@ void H2K_dosched(H2K_thread_context *me,u32_t hthread) // H2K_switch(me,NULL); /* EJP: should never get here! */ } - if ((H2K_gp->wait_mask == 0) && (new->prio IS_WORSE_THAN H2K_runlist_worst_prio())) { - /* If no threads are waiting and this new priority is worse than everyone else... */ - if ((H2K_gp->priomask & (1<priomask |= 1<priomask & (1<priomask = Q6_R_clrbit_RR(H2K_gp->priomask,hthread); - highprio_imask(hthread); - } - } new->hthread = (u8_t)hthread; - H2K_runlist_push(new); + H2K_runlist_push(new); // This callsite hides inside it a race. + // If we delete the writes to the "dummy" arrays H2K_runlist_push- futex_pi test hangs. + // THere is no point in writing to the dummies, just for this callsit, + // All other callsites across the code are fine without H2K_runlist_push writing + // to the dummies, but this preserved for futex_pi test to pass. //if (new->vmstatus & H2K_VMSTATUS_VMWORK) H2K_vm_do_work(new); H2K_switch(me,new); /* EJP: should never get here! */ diff --git a/kernel/sched/dosched/test/test/harness.c b/kernel/sched/dosched/test/test/harness.c index cc1094e2b..a58509819 100644 --- a/kernel/sched/dosched/test/test/harness.c +++ b/kernel/sched/dosched/test/test/harness.c @@ -55,9 +55,8 @@ typedef void (* testsetup_t)(phase_t phase); void TB_reset() { TB_saw_switch = 0; - H2K_runlist_init(); H2K_readylist_init(); - H2K_lowprio_init(); + H2K_gp->wait_mask = 0; } void TB_setup_common() @@ -91,92 +90,54 @@ void TB_do_call() void TB_fiddle_prio_low_to_low(phase_t phase) { if (phase == SETUP) { - /* Setup for call */ - if (H2K_gp->wait_mask == 0) { - H2K_gp->priomask = H2K_gp->wait_mask; - } else { - H2K_gp->priomask = 0x1; - } H2K_runlist_push(&h); H2K_ready_append(&m); TB_me = &l; } else { /* Check expected values */ if (TB_to != &m) FAIL("Unexpected thread scheduled"); - if (H2K_gp->wait_mask == 0) { - if ((H2K_gp->priomask & 1) == 0) FAIL("low_to_low did not switch to lowprio"); - } } } void TB_fiddle_prio_low_to_high(phase_t phase) { if (phase == SETUP) { - /* Setup for call */ - if (H2K_gp->wait_mask == 0) { - H2K_gp->priomask = H2K_gp->wait_mask; - } else { - H2K_gp->priomask = 0x1; - } H2K_runlist_push(&m); H2K_ready_append(&h); TB_me = &l; } else { /* Check expected values */ if (TB_to != &h) FAIL("Unexpected thread scheduled"); - if ((H2K_gp->priomask & 1) == 1) FAIL("low_to_high did not switch from lowprio"); } } void TB_fiddle_prio_high_to_low(phase_t phase) { if (phase == SETUP) { - /* Setup for call */ - if (H2K_gp->wait_mask == 0) { - H2K_gp->priomask = H2K_gp->wait_mask; - } else { - H2K_gp->priomask = 0x2; - } H2K_runlist_push(&m); H2K_ready_append(&l); TB_me = &h; } else { /* Check expected values */ if (TB_to != &l) FAIL("Unexpected thread scheduled"); - if (H2K_gp->wait_mask == 0) { - if ((H2K_gp->priomask & 1) == 0) FAIL("high_to_low did not switch to lowprio"); - } } } void TB_fiddle_prio_high_to_high(phase_t phase) { if (phase == SETUP) { - /* Setup for call */ - if (H2K_gp->wait_mask == 0) { - H2K_gp->priomask = H2K_gp->wait_mask; - } else { - H2K_gp->priomask = 0x2; - } H2K_runlist_push(&l); H2K_ready_append(&m); H2K_ready_append(&h); TB_me = &h; } else { if (TB_to != &h) FAIL("Unexpected thread scheduled"); - if ((H2K_gp->priomask & 1) != 0) FAIL( " Unexpected switch to lowprio"); } } void TB_fiddle_prio_high_to_wait(phase_t phase) { if (phase == SETUP) { - /* Setup for call */ - if (H2K_gp->wait_mask == 0) { - H2K_gp->priomask = H2K_gp->wait_mask; - } else { - H2K_gp->priomask = 0x2; - } TB_me = &h; } else { /* Check expected values */ @@ -187,12 +148,6 @@ void TB_fiddle_prio_high_to_wait(phase_t phase) void TB_fiddle_prio_low_to_wait(phase_t phase) { if (phase == SETUP) { - /* Setup for call */ - if (H2K_gp->wait_mask == 0) { - H2K_gp->priomask = H2K_gp->wait_mask; - } else { - H2K_gp->priomask = 0x1; - } TB_me = &l; } else { /* Check expected values */ @@ -207,20 +162,9 @@ void TB_fiddle_prio_wait_to_high(phase_t phase) TB_me = NULL; H2K_runlist_push(&l); H2K_ready_append(&h); - if (H2K_gp->wait_mask == 0) { - H2K_gp->priomask = 0; - H2K_lowprio_notify(); - } else { - H2K_gp->priomask = H2K_gp->wait_mask; - } } else { /* Check expected values */ if (H2K_ready_any_valid()) FAIL("didn't find ready thread"); - if (H2K_gp->runlist[h.hthread] != &h) FAIL("Didn't insert h thread"); - if (H2K_gp->runlist_prios[h.hthread] != h.prio) FAIL("Didn't insert h thread"); - if (H2K_gp->wait_mask == 0) { - if (H2K_gp->priomask == 0x1) FAIL("set myself to lowprio?"); - } } } @@ -232,20 +176,9 @@ void TB_fiddle_prio_wait_to_low(phase_t phase) TB_me = NULL; H2K_runlist_push(&h); H2K_ready_append(&l); - if (H2K_gp->wait_mask == 0) { - H2K_gp->priomask = 0; - H2K_lowprio_notify(); - } else { - H2K_gp->priomask = H2K_gp->wait_mask; - } } else { /* Check expected values */ if (H2K_ready_any_valid()) FAIL("didn't find ready thread"); - if (H2K_gp->runlist[l.hthread] != &l) FAIL("Didn't insert l thread"); - if (H2K_gp->runlist_prios[l.hthread] != l.prio) FAIL("Didn't insert l thread"); - if (H2K_gp->wait_mask == 0) { - if (H2K_gp->priomask != 0x1) FAIL("Didn't set myself to lowprio"); - } } } diff --git a/kernel/sched/lowprio/lowprio.h b/kernel/sched/lowprio/lowprio.h index a0629f459..430c0ee3d 100644 --- a/kernel/sched/lowprio/lowprio.h +++ b/kernel/sched/lowprio/lowprio.h @@ -13,24 +13,24 @@ /* Notify a low priority thread that it is the new lowest priority if necessary */ /* H2K_gp->priomask should be non-zero */ -static inline void H2K_lowprio_notify() -{ - u32_t hthread; - hthread = H2K_runlist_worst_prio_hthread(); - H2K_gp->priomask |= 1<priomask |= 1<priomask; - if (H2K_gp->wait_mask) return; // just a sanity check... should be an error - H2K_gp->priomask = 0; - change_imask((u32_t)Q6_R_ct0_R(mask),(u32_t)-1); -} +// /* Notify the current lowprio thread that he is no longer the worst */ +// /* Note that if threads are waiting, this should not be called, you are +// * going to get into trouble if you raise the priority of a waiting thread */ +// static inline void H2K_raise_lowprio() +// { +// u32_t mask = H2K_gp->priomask; +// if (H2K_gp->wait_mask) return; // just a sanity check... should be an error +// H2K_gp->priomask = 0; +// change_imask((u32_t)Q6_R_ct0_R(mask),(u32_t)-1); +// } void H2K_lowprio_init() IN_SECTION(".text.init.lowprio"); diff --git a/kernel/sched/lowprio/lowprio.ref.c b/kernel/sched/lowprio/lowprio.ref.c index fce38ae75..5b1b2ac6a 100644 --- a/kernel/sched/lowprio/lowprio.ref.c +++ b/kernel/sched/lowprio/lowprio.ref.c @@ -8,7 +8,7 @@ void H2K_lowprio_init() { #ifdef TESTING - H2K_gp->priomask = H2K_gp->wait_mask = 0; + H2K_gp->wait_mask = 0; #endif } diff --git a/kernel/sched/resched/resched.ref.c b/kernel/sched/resched/resched.ref.c index 9043433c7..6437a28df 100644 --- a/kernel/sched/resched/resched.ref.c +++ b/kernel/sched/resched/resched.ref.c @@ -16,7 +16,8 @@ static inline void resched(u32_t unused, H2K_thread_context *me, u32_t hwtnum) { if (me != NULL) { - H2K_runlist_remove(me); + H2K_runlist_remove(me); // This is kept for the same reason described + // in kernel/sched/dosched/dosched.ref.c at the H2K_rnulist_push callsite. H2K_ready_append(me); } else { /* Interrupted WAIT mode */ diff --git a/kernel/sched/resched/test/test.c b/kernel/sched/resched/test/test.c index db6d86982..dbaa365d5 100644 --- a/kernel/sched/resched/test/test.c +++ b/kernel/sched/resched/test/test.c @@ -68,8 +68,7 @@ int main() H2K_gp->cluster_sched = 1; #endif H2K_readylist_init(); - H2K_runlist_init(); - H2K_lowprio_init(); + H2K_gp->wait_mask = 0; a.prio = b.prio = c.prio = MAX_PRIOS - 30; a.hthread = 0; b.hthread = 1; @@ -88,7 +87,6 @@ int main() H2K_runlist_push(&c); TH_resched(0,TB_in,0); if (TB_saw_dosched == 0) FAIL("Did not do a resched"); - if (H2K_gp->runlist[c.hthread] != &c) FAIL("Unexpected thread in runlist"); if (H2K_gp->ready[MAX_PRIOS - 30] != &a) FAIL("Unexpected thread in readylist"); H2K_gp->wait_mask = 0; TB_saw_dosched = 0; @@ -103,7 +101,6 @@ int main() H2K_runlist_push(&b); TH_resched_cluster(0,TB_in,1); if (TB_saw_dosched == 0) FAIL("Did not do a resched"); - if (H2K_gp->runlist[b.hthread] == &b) FAIL("Unexpected thread in runlist"); if (H2K_gp->ready[MAX_PRIOS - 30]->next->next != &b) FAIL("Unexpected thread in readylist"); puts("TEST PASSED\n"); return 0; diff --git a/kernel/sched/yield/test/test.c b/kernel/sched/yield/test/test.c index dcfc79eaa..75263a4e9 100644 --- a/kernel/sched/yield/test/test.c +++ b/kernel/sched/yield/test/test.c @@ -51,7 +51,7 @@ int main() __asm__ __volatile(GLOBAL_REG_STR " = %0 " : : "r"(&H2K_kg)); H2K_readylist_init(); H2K_runlist_init(); - H2K_lowprio_init(); + H2K_gp->wait_mask = 0; a.prio = b.prio = c.prio = d.prio = 2; a.hthread = 0; b.hthread = 2; @@ -73,7 +73,7 @@ int main() BKL_UNLOCK(); H2K_readylist_init(); H2K_runlist_init(); - H2K_lowprio_init(); + H2K_gp->wait_mask = 0; H2K_runlist_push(&a); H2K_runlist_push(&c); H2K_runlist_push(&d); diff --git a/kernel/sched/yield/yield.ref.c b/kernel/sched/yield/yield.ref.c index c12f2d7fb..5ed47dbe4 100644 --- a/kernel/sched/yield/yield.ref.c +++ b/kernel/sched/yield/yield.ref.c @@ -22,7 +22,9 @@ void H2K_sched_yield(H2K_thread_context *me) BKL_UNLOCK(&H2K_bkl); return; } - H2K_runlist_remove(me); + H2K_runlist_remove(me); // This is kept for the same reason described + // in kernel/sched/dosched/dosched.ref.c at the H2K_rnulist_push callsite. + H2K_ready_append(me); H2K_dosched(me,me->hthread); } diff --git a/kernel/thread/create/create.ref.c b/kernel/thread/create/create.ref.c index fa716d75b..939d04c44 100644 --- a/kernel/thread/create/create.ref.c +++ b/kernel/thread/create/create.ref.c @@ -55,7 +55,7 @@ IN_SECTION(".text.misc.create") s32_t H2K_thread_create_no_squash(u32_t pc, u32_ extra = H2K_gp->asid_table[me->ssr_asid].fields.extra; } - if (prio > MAX_PRIO) return -1; // bad prio + if (prio > MAX_READY_PRIO) return -1; // bad prio if (prio < bestprio) return -1; // priority better than allowed if ((sp & 7) != 0) return -1; // bad stack pointer alignment diff --git a/kernel/thread/create/test/test.c b/kernel/thread/create/test/test.c index 528d4a881..a663c11f6 100644 --- a/kernel/thread/create/test/test.c +++ b/kernel/thread/create/test/test.c @@ -106,9 +106,8 @@ int main() u32_t asid; H2K_vmblock_t *vmblock = &TH_vm.vm; __asm__ __volatile(GLOBAL_REG_STR " = %0 " : : "r"(&H2K_kg)); - H2K_runlist_init(); H2K_readylist_init(); - H2K_lowprio_init(); + H2K_gp->wait_mask = 0; H2K_thread_init(); H2K_asid_table_init(); TH_vm_init(); diff --git a/kernel/thread/stop/stop.ref.c b/kernel/thread/stop/stop.ref.c index e77bfc2a4..3624731c5 100644 --- a/kernel/thread/stop/stop.ref.c +++ b/kernel/thread/stop/stop.ref.c @@ -30,7 +30,6 @@ void H2K_thread_stop_withlock(s32_t status, H2K_thread_context *me) H2K_vmblock_t *parent_vmblock; H2K_timer_cancel_withlock(me); - H2K_runlist_remove(me); H2K_asid_table_dec(me->ssr_asid); H2K_thread_context_clear(me); me->next = vmblock->free_threads; diff --git a/kernel/thread/stop/test/test.c b/kernel/thread/stop/test/test.c index e1561ce53..bd43f8ce0 100644 --- a/kernel/thread/stop/test/test.c +++ b/kernel/thread/stop/test/test.c @@ -75,9 +75,8 @@ int main() H2K_vmblock_t myblock; H2K_vmblock_t *vmblock = &myblock; __asm__ __volatile(GLOBAL_REG_STR " = %0 " : : "r"(&H2K_kg)); - H2K_runlist_init(); H2K_readylist_init(); - H2K_lowprio_init(); + H2K_gp->wait_mask = 0; H2K_thread_init(); a.prio = 2; @@ -93,9 +92,7 @@ int main() vmblock->free_threads = NULL; if (vmblock->free_threads != NULL) FAIL("free threads not clear"); H2K_runlist_push(&a); - if (H2K_gp->runlist[a.hthread] != &a) FAIL("Thread not in expected place in runlist (0)"); H2K_runlist_push(&b); - if (H2K_gp->runlist[b.hthread] != &b) FAIL("Thread not in expected place in runlist (1)"); TH_me = &a; a.prev = &a; TH_saw_dosched = 0; @@ -111,7 +108,6 @@ int main() //puts("h"); if (a.next != 0) FAIL("Free thread list incorrect"); //puts("i"); - if (H2K_gp->runlist[a.hthread] == &a) FAIL("Thread not removed from runlist"); //puts("B"); TH_saw_dosched = 0; TH_me = &b; @@ -121,12 +117,7 @@ int main() if (b.prev != 0) FAIL("thread not cleared"); if (vmblock->free_threads != &b) FAIL("free thread list incorrect"); if (b.next != &a) FAIL("Free thread list incorrect"); - if (H2K_gp->runlist[b.hthread] == &b) FAIL("Thread not removed from runlist"); - if (H2K_gp->runlist[a.hthread] != NULL) FAIL("Unexpected runlist"); - if (H2K_gp->runlist[b.hthread] != NULL) FAIL("Unexpected runlist"); puts("A"); - H2K_runlist_init(); - H2K_lowprio_init(); H2K_readylist_init(); H2K_thread_init(); a.prio = 2; @@ -135,11 +126,8 @@ int main() vmblock->free_threads = NULL; if (vmblock->free_threads != NULL) FAIL("free threads not clear"); H2K_runlist_push(&a); - if (H2K_gp->runlist[a.hthread] != &a) FAIL("Thread not in expected place in runlist (2)"); H2K_runlist_push(&b); - if (H2K_gp->runlist[b.hthread] != &b) FAIL("Thread not in expected place in runlist (3)"); H2K_runlist_push(&c); - if (H2K_gp->runlist[c.hthread] != &c) FAIL("Thread not in expected place in runlist (4)"); TH_me = &a; a.prev = &a; TH_saw_dosched = 0; @@ -149,9 +137,7 @@ int main() if (a.prev != 0) FAIL("thread not cleared"); if (vmblock->free_threads != &a) FAIL("free thread list incorrect"); if (a.next != 0) FAIL("Free thread list incorrect"); - if (H2K_gp->runlist[a.hthread] == &a) FAIL("Thread not removed from runlist"); puts("TEST PASSED\n"); return 0; } - diff --git a/kernel/traps/config/config.ref.c b/kernel/traps/config/config.ref.c index 34157434f..48e75e302 100644 --- a/kernel/traps/config/config.ref.c +++ b/kernel/traps/config/config.ref.c @@ -226,7 +226,7 @@ static u32_t H2K_config_vmblock_init_set_fences(H2K_vmblock_t *vmblock, u32_t vm static u32_t H2K_config_vmblock_init_prio_trapmask(H2K_vmblock_t *vmblock, u32_t vm, u32_t unused, u32_t arg1, u32_t arg2, H2K_thread_context *me) { - if (arg1 > MAX_PRIO) return 0; /* bad arg */ + if (arg1 > MAX_READY_PRIO) return 0; /* bad arg */ vmblock->bestprio = (u8_t)arg1; vmblock->trapmask = (u32_t)arg2; if (vmblock->bestprio > 200) vmblock->tlbidxmask = 0x0f; /* EJP: KLUDGE */ diff --git a/kernel/traps/config/test/test.c b/kernel/traps/config/test/test.c index c4f4e639a..1b9267e21 100644 --- a/kernel/traps/config/test/test.c +++ b/kernel/traps/config/test/test.c @@ -134,7 +134,7 @@ int main() DPRINTF("SET_PRIO_TRAPMASK\n\n"); /* SET_PRIO_TRAPMASK bad prio */ - ret = H2K_trap_config(CONFIG_VMBLOCK_INIT, vm, SET_PRIO_TRAPMASK, MAX_PRIO + 1, 0, NULL); + ret = H2K_trap_config(CONFIG_VMBLOCK_INIT, vm, SET_PRIO_TRAPMASK, WAITING_PRIO + 1, 0, NULL); if (ret!= 0) FAIL("Missed bad prio"); /* SET_PRIO_TRAPMASK */ diff --git a/kernel/traps/hwconfig/hwconfig.ref.c b/kernel/traps/hwconfig/hwconfig.ref.c index 665dd8d5a..5da31a431 100644 --- a/kernel/traps/hwconfig/hwconfig.ref.c +++ b/kernel/traps/hwconfig/hwconfig.ref.c @@ -277,7 +277,6 @@ u32_t H2K_trap_hwconfig_hlxbits(u32_t unused, void *unusedp, u32_t xa3, u32_t x me->ccr = Q6_R_insert_RII(me->ccr, xa3, CCR_XA3_NBITS, CCR_XA3_BITS); me->ccr = Q6_R_insert_RII(me->ccr, xe3, 1, CCR_XE3_BIT); me->r00 = 0; - H2K_runlist_remove(me); H2K_ready_append(me); H2K_dosched(me, me->hthread); } @@ -313,7 +312,6 @@ u32_t H2K_trap_hwconfig_hmxbits(u32_t unused, void *unusedp, u32_t xe2, u32_t un // me->ccr = Q6_R_insert_RII(me->ccr, xa2, CCR_XA2_NBITS, CCR_XA2_BITS); me->ssr = Q6_R_insert_RII(me->ssr, xe2, 1, SSR_XE2_BIT); me->r00 = 0; - H2K_runlist_remove(me); H2K_ready_append(me); H2K_dosched(me, me->hthread); } @@ -359,7 +357,6 @@ u32_t H2K_trap_hwconfig_extbits(u32_t unused, void *unusedp, u32_t xa, u32_t xe, } /* else (when in hvx range and do_ext) kernel is managing xa/xe, so do nothing here */ me->r00 = 0; - H2K_runlist_remove(me); H2K_ready_append(me); H2K_dosched(me, me->hthread); } diff --git a/kernel/traps/prio/prio.ref.c b/kernel/traps/prio/prio.ref.c index 47d65df3a..a55ee3c3d 100644 --- a/kernel/traps/prio/prio.ref.c +++ b/kernel/traps/prio/prio.ref.c @@ -8,32 +8,15 @@ #include #include -#if 0 -s32_t H2K_prio_set(H2K_thread_context *dest, u32_t prio, H2K_thread_context *me) -{ - s32_t ret; - if (prio > MAX_PRIO) prio = MAX_PRIO; - if (dest == me) { - H2K_runlist_remove(me); - ret = me->prio; - me->prio = prio; - H2K_runlist_push(me); - } else { - /* UNIMPLEMENTED */ - ret = -1; - } - return ret; -} -#else + s32_t H2K_prio_set(H2K_thread_context *dest, u32_t prio, H2K_thread_context *me) { s32_t ret = me->base_prio; - if (prio > MAX_PRIO) return -1; + if (prio > MAX_READY_PRIO) return -1; if ((me->vmblock != NULL) && (prio < me->vmblock->bestprio)) return -1; me->base_prio = (u8_t)prio; return ret; } -#endif u32_t H2K_prio_get(unsigned int threadid_in, H2K_thread_context *me) { diff --git a/kernel/traps/prio/test/test.c b/kernel/traps/prio/test/test.c index 5c6ccb506..ce3916429 100644 --- a/kernel/traps/prio/test/test.c +++ b/kernel/traps/prio/test/test.c @@ -24,7 +24,7 @@ int main() { u32_t i; __asm__ __volatile(GLOBAL_REG_STR " = %0 " : : "r"(&H2K_kg)); - for (i = 0; i < MAX_PRIOS; i++) { + for (i = 0; i <= MAX_READY_PRIO; i++) { a.base_prio = i; if (H2K_prio_get(0,&a) != i) FAIL("prio_get"); } @@ -32,12 +32,14 @@ int main() H2K_prio_set(&a,0,&a); if (a.base_prio != 0) FAIL("prio_set_null"); a.vmblock = &vmblock; - for (i = 0; i < MAX_PRIOS-1; i++) { - a.vmblock->bestprio = i+1; + for (i = 0; i <= MAX_READY_PRIO; i++) { + a.vmblock->bestprio = i; H2K_prio_set(&a,a.vmblock->bestprio,&a); if (a.base_prio != a.vmblock->bestprio) FAIL("prio_set_mid"); } - if (H2K_prio_set(&a,MAX_PRIOS,&a) != -1) FAIL("prio_set_limit"); + for (i = MAX_READY_PRIO + 1; i <= MAX_PRIOS; i++) { + if(H2K_prio_set(&a,i,&a) != -1) FAIL("prio_set_limit"); + } puts("TEST PASSED\n"); return 0; } diff --git a/kernel/util/hw/hw.h b/kernel/util/hw/hw.h index a63da5206..7d98734ef 100644 --- a/kernel/util/hw/hw.h +++ b/kernel/util/hw/hw.h @@ -60,7 +60,7 @@ static inline u32_t get_imask(u32_t thread) * Mirrors setimask -- uses predicate register to name the target thread. */ static inline void set_thread_stid_prio(u32_t thread, u32_t prio) { - asm volatile (" p0 = %0\n setprio(p0,%1)" : : "r"(thread),"r"(prio):"p0"); + asm volatile (" p0 = %0\n setprio(p0,%1); isync;" : : "r"(thread),"r"(prio):"p0"); } static inline void iassignw(u32_t intno, u32_t threadmask) @@ -102,29 +102,6 @@ static inline void cluster_resched_int() asm(" swi(%0) // cluster resched" : : "r"(CLUSTER_RESCHED_INT_INTMASK)); } -#if (ARCHV <= 2) -static inline void highprio_imask(u32_t hthread) -{ - asm(" imask = %0 // set to high priority " : : "r"((-1)-(HW_TH_0_INTMASK << (hthread)))); -} - -static inline void lowprio_imask(u32_t hthread) -{ - asm(" imask = %0 // set to low priority " : : "r"(HW_TH_ALL_INTMASK ^ (HW_TH_0_INTMASK << (hthread)))); -} -#elif (ARCHV >= 3) -static inline void highprio_imask(u32_t hthread) -{ - asm(" imask = %0 // set to high priority " : : "r"(-1)); -} - -static inline void lowprio_imask(u32_t hthread) -{ - asm(" imask = %0 // set to low priority " : : "r"(0)); -} - -#endif - static inline u32_t H2K_get_ssr() { u32_t ret; diff --git a/kernel/util/max/max.h b/kernel/util/max/max.h index 71a3a7f8c..011577022 100644 --- a/kernel/util/max/max.h +++ b/kernel/util/max/max.h @@ -76,7 +76,8 @@ #endif #define MAX_PRIOS 256 -#define MAX_PRIO ((MAX_PRIOS) - 1) +#define WAITING_PRIO ((MAX_PRIOS) - 1) +#define MAX_READY_PRIO ((MAX_PRIOS) - 2) #define BEST_PRIO 0 #define BESTWAIT_MASK 0x1ff diff --git a/kernel/util/stmode/test/test.c b/kernel/util/stmode/test/test.c index cd4160e9f..434f7e05d 100644 --- a/kernel/util/stmode/test/test.c +++ b/kernel/util/stmode/test/test.c @@ -59,7 +59,7 @@ IN_SECTION(".text.misc.create") s32_t H2K_thread_create_no_squash(u32_t pc, u32_ extra = H2K_gp->asid_table[me->ssr_asid].fields.extra; } - if (prio > MAX_PRIO) return -1; + if (prio > MAX_READY_PRIO) return -1; if (prio < bestprio) return -1; if ((sp & 7) != 0) return -1; if ((pc & 3) != 0) return -1; diff --git a/kernel/vm/vmfuncs/test/test.c b/kernel/vm/vmfuncs/test/test.c index bd0bae517..df16b1a87 100644 --- a/kernel/vm/vmfuncs/test/test.c +++ b/kernel/vm/vmfuncs/test/test.c @@ -248,8 +248,6 @@ int main() a.vmblock = &av; av.waiting_cpus = 0; a.id.cpuidx = 5; - H2K_kg.runlist[0] = &a; - H2K_kg.runlist_prios[0] = 4; a.status = 0; TH_expected_dosched = 1; if (setjmp(env) == 0) H2K_vmtrap_wait(&a); @@ -257,8 +255,6 @@ int main() if (a.r00 != -1) FAIL("r00 clobbered"); if (av.waiting_cpus != (1<<5)) FAIL("wrong waiting cpus"); if (a.status != H2K_STATUS_VMWAIT) FAIL("wrong status"); - if (H2K_kg.runlist[0] != NULL) FAIL("runlist"); - if (H2K_kg.runlist_prios[0] != -1) FAIL("runlist_prios"); /* RETURN */ diff --git a/kernel/vm/vmfuncs/vmfuncs.ref.c b/kernel/vm/vmfuncs/vmfuncs.ref.c index 520017f1d..336591a89 100644 --- a/kernel/vm/vmfuncs/vmfuncs.ref.c +++ b/kernel/vm/vmfuncs/vmfuncs.ref.c @@ -122,7 +122,6 @@ void H2K_vmtrap_wait(H2K_thread_context *me) if (me->id.cpuidx < sizeof(long_bitmask_t) * 8) { me->vmblock->waiting_cpus |= (0x1 << me->id.cpuidx); } - H2K_runlist_remove(me); H2K_dosched(me,me->hthread); } else { /* Interrupt pending; either it was taken or interrupts are disabled. In diff --git a/scripts/testlist.v61 b/scripts/testlist.v61 index 1ecef6cb7..42eb921e4 100644 --- a/scripts/testlist.v61 +++ b/scripts/testlist.v61 @@ -1,7 +1,7 @@ ./kernel/data/intconfig/test ./kernel/data/context/test ./kernel/data/readylist/test -./kernel/data/runlist/test +#./kernel/data/runlist/test ./kernel/data/thread/test ./kernel/error/fatal/test ./kernel/event/error/test @@ -42,7 +42,7 @@ ./kernel/futex/futex/test/tests/multi_wake ./kernel/futex/futex/test/tests/pi ./kernel/futex/futex/test/tests/simple_lock_unlock -./kernel/sched/lowprio/test +#./kernel/sched/lowprio/test ./kernel/sched/resched/test ./kernel/sched/switch/test ./kernel/sched/yield/test From 5a88e2cdc0d83c3e59063787042f04f3585f582c Mon Sep 17 00:00:00 2001 From: zbelinsk Date: Tue, 14 Jul 2026 02:07:58 +0300 Subject: [PATCH 3/3] Update dosched.ref.c Signed-off-by: zbelinsk --- kernel/sched/dosched/dosched.ref.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/sched/dosched/dosched.ref.c b/kernel/sched/dosched/dosched.ref.c index 4c596b89c..bcd3f8636 100644 --- a/kernel/sched/dosched/dosched.ref.c +++ b/kernel/sched/dosched/dosched.ref.c @@ -19,7 +19,8 @@ void H2K_dosched(H2K_thread_context *me,u32_t hthread) new = H2K_ready_getbest(hthread); if (new == NULL) { change_imask(hthread,0); //This is not needed actually. - // Thing is that if we dont put this here-it is not working. + // Thing is that if we dont put this here-it is not working, + // see comment next to H2K_runlist_push below. /* GO TO SLEEP */ /* FIXME: temporary ugly hack for broken 8.7+ compiler -- investigate * whether this is still needed and if a proper direct call can be