[PATCH v3 18/32] target/hexagon: Implement setprio, resched

Brian Cain posted 32 patches 3 weeks, 6 days ago
[PATCH v3 18/32] target/hexagon: Implement setprio, resched
Posted by Brian Cain 3 weeks, 6 days ago
From: Brian Cain <bcain@quicinc.com>

The hardware-assisted scheduler helps manage tasks on the run queue
and interrupt steering.

This instruction is defined in the Qualcomm Hexagon V71 Programmer's Reference
Manual -
https://docs.qualcomm.com/bundle/publicresource/80-N2040-51_REV_AB_Hexagon_V71_ProgrammerS_Reference_Manual.pdf
See §11.9.2 SYSTEM MONITOR.

Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
 target/hexagon/op_helper.c | 75 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 75 insertions(+)

diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c
index 9090a5c20ab..36822219b4b 100644
--- a/target/hexagon/op_helper.c
+++ b/target/hexagon/op_helper.c
@@ -1575,6 +1575,62 @@ static void hexagon_wait_thread(CPUHexagonState *env, uint32_t PC)
     cpu_interrupt(cs, CPU_INTERRUPT_HALT);
 }
 
+static inline QEMU_ALWAYS_INLINE void resched(CPUHexagonState *env)
+{
+    uint32_t schedcfg;
+    uint32_t schedcfg_en;
+    int int_number;
+    CPUState *cs;
+    uint32_t lowest_th_prio = 0; /* 0 is highest prio */
+    uint32_t bestwait_reg;
+    uint32_t best_prio;
+
+    BQL_LOCK_GUARD();
+    qemu_log_mask(CPU_LOG_INT, "%s: check resched\n", __func__);
+    HexagonCPU *cpu = env_archcpu(env);
+    schedcfg = cpu->globalregs ?
+        hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SCHEDCFG,
+                               env->threadId) : 0;
+    schedcfg_en = GET_FIELD(SCHEDCFG_EN, schedcfg);
+    int_number = GET_FIELD(SCHEDCFG_INTNO, schedcfg);
+
+    if (!schedcfg_en) {
+        return;
+    }
+
+    CPU_FOREACH(cs) {
+        HexagonCPU *thread = HEXAGON_CPU(cs);
+        CPUHexagonState *thread_env = &(thread->env);
+        uint32_t th_prio = GET_FIELD(
+            STID_PRIO, thread_env->t_sreg[HEX_SREG_STID]);
+        if (!hexagon_thread_is_enabled(thread_env)) {
+            continue;
+        }
+
+        lowest_th_prio = (lowest_th_prio > th_prio)
+            ? lowest_th_prio
+            : th_prio;
+    }
+
+    bestwait_reg = cpu->globalregs ?
+        hexagon_globalreg_read(cpu->globalregs, HEX_SREG_BESTWAIT,
+                               env->threadId) : 0;
+    best_prio = GET_FIELD(BESTWAIT_PRIO, bestwait_reg);
+
+    /*
+     * If the lowest priority thread is lower priority than the
+     * value in the BESTWAIT register, we must raise the reschedule
+     * interrupt on the lowest priority thread.
+     */
+    if (lowest_th_prio > best_prio) {
+        qemu_log_mask(CPU_LOG_INT,
+                "%s: raising resched int %d, cur PC 0x" TARGET_FMT_lx "\n",
+                __func__, int_number, env->gpr[HEX_REG_PC]);
+        SET_SYSTEM_FIELD(env, HEX_SREG_BESTWAIT, BESTWAIT_PRIO, ~0);
+        hex_raise_interrupts(env, 1 << int_number, CPU_INTERRUPT_SWI);
+    }
+}
+
 void HELPER(wait)(CPUHexagonState *env, uint32_t PC)
 {
     BQL_LOCK_GUARD();
@@ -1680,8 +1736,27 @@ uint64_t HELPER(greg_read_pair)(CPUHexagonState *env, uint32_t reg)
     g_assert_not_reached();
 }
 
+/*
+ * setprio/resched - See the Hexagon V73 Programmer's Reference Manual,
+ * Section 6.4 "Interrupt and Thread Scheduling"
+ */
 void HELPER(setprio)(CPUHexagonState *env, uint32_t thread, uint32_t prio)
 {
+    CPUState *cs;
+
+    BQL_LOCK_GUARD();
+    CPU_FOREACH(cs) {
+        HexagonCPU *found_cpu = HEXAGON_CPU(cs);
+        CPUHexagonState *found_env = &found_cpu->env;
+        if (thread == found_env->threadId) {
+            SET_SYSTEM_FIELD(found_env, HEX_SREG_STID, STID_PRIO, prio);
+            qemu_log_mask(CPU_LOG_INT,
+                          "%s: tid " TARGET_FMT_lx " prio = 0x%x\n",
+                          __func__, found_env->threadId, prio);
+            resched(env);
+            return;
+        }
+    }
     g_assert_not_reached();
 }
 
-- 
2.34.1

Re: [PATCH v3 18/32] target/hexagon: Implement setprio, resched
Posted by Taylor Simpson 3 weeks, 1 day ago
On Tue, Mar 10, 2026 at 10:08 PM Brian Cain <brian.cain@oss.qualcomm.com>
wrote:

> From: Brian Cain <bcain@quicinc.com>
>
> The hardware-assisted scheduler helps manage tasks on the run queue
> and interrupt steering.
>
> This instruction is defined in the Qualcomm Hexagon V71 Programmer's
> Reference
> Manual -
>
> https://docs.qualcomm.com/bundle/publicresource/80-N2040-51_REV_AB_Hexagon_V71_ProgrammerS_Reference_Manual.pdf
> See §11.9.2 SYSTEM MONITOR.
>

This document doesn't have a section 11.9.2.


>
> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
> ---
>  target/hexagon/op_helper.c | 75 ++++++++++++++++++++++++++++++++++++++
>  1 file changed, 75 insertions(+)
>
> diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c
> index 9090a5c20ab..36822219b4b 100644
> --- a/target/hexagon/op_helper.c
> +++ b/target/hexagon/op_helper.c
> @@ -1575,6 +1575,62 @@ static void hexagon_wait_thread(CPUHexagonState
> *env, uint32_t PC)
>      cpu_interrupt(cs, CPU_INTERRUPT_HALT);
>  }
>
> +static inline QEMU_ALWAYS_INLINE void resched(CPUHexagonState *env)
> +{
> +    uint32_t schedcfg;
> +    uint32_t schedcfg_en;
> +    int int_number;
> +    CPUState *cs;
> +    uint32_t lowest_th_prio = 0; /* 0 is highest prio */
> +    uint32_t bestwait_reg;
> +    uint32_t best_prio;
> +
> +    BQL_LOCK_GUARD();
> +    qemu_log_mask(CPU_LOG_INT, "%s: check resched\n", __func__);
> +    HexagonCPU *cpu = env_archcpu(env);
> +    schedcfg = cpu->globalregs ?
> +        hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SCHEDCFG,
> +                               env->threadId) : 0;
> +    schedcfg_en = GET_FIELD(SCHEDCFG_EN, schedcfg);
> +    int_number = GET_FIELD(SCHEDCFG_INTNO, schedcfg);
> +
> +    if (!schedcfg_en) {
> +        return;
> +    }
> +
> +    CPU_FOREACH(cs) {
> +        HexagonCPU *thread = HEXAGON_CPU(cs);
> +        CPUHexagonState *thread_env = &(thread->env);
> +        uint32_t th_prio = GET_FIELD(
> +            STID_PRIO, thread_env->t_sreg[HEX_SREG_STID]);
> +        if (!hexagon_thread_is_enabled(thread_env)) {
> +            continue;
> +        }
> +
> +        lowest_th_prio = (lowest_th_prio > th_prio)
> +            ? lowest_th_prio
> +            : th_prio;
> +    }
> +
> +    bestwait_reg = cpu->globalregs ?
> +        hexagon_globalreg_read(cpu->globalregs, HEX_SREG_BESTWAIT,
> +                               env->threadId) : 0;
> +    best_prio = GET_FIELD(BESTWAIT_PRIO, bestwait_reg);
> +
> +    /*
> +     * If the lowest priority thread is lower priority than the
> +     * value in the BESTWAIT register, we must raise the reschedule
> +     * interrupt on the lowest priority thread.
> +     */
> +    if (lowest_th_prio > best_prio) {
> +        qemu_log_mask(CPU_LOG_INT,
> +                "%s: raising resched int %d, cur PC 0x" TARGET_FMT_lx
> "\n",
>

Don't use %d


> +                __func__, int_number, env->gpr[HEX_REG_PC]);
> +        SET_SYSTEM_FIELD(env, HEX_SREG_BESTWAIT, BESTWAIT_PRIO, ~0);
>

Is ~0 the correct value here?  Seems like it should be best_prio.


> +        hex_raise_interrupts(env, 1 << int_number, CPU_INTERRUPT_SWI);
> +    }
> +}
> +
>  void HELPER(wait)(CPUHexagonState *env, uint32_t PC)
>  {
>      BQL_LOCK_GUARD();
> @@ -1680,8 +1736,27 @@ uint64_t HELPER(greg_read_pair)(CPUHexagonState
> *env, uint32_t reg)
>      g_assert_not_reached();
>  }
>
> +/*
> + * setprio/resched - See the Hexagon V73 Programmer's Reference Manual,
> + * Section 6.4 "Interrupt and Thread Scheduling"
>

Neither the V71 (mentioned above) or V73 PRM contain section 6.4.


> + */
>  void HELPER(setprio)(CPUHexagonState *env, uint32_t thread, uint32_t prio)
>  {
> +    CPUState *cs;
> +
> +    BQL_LOCK_GUARD();
> +    CPU_FOREACH(cs) {
> +        HexagonCPU *found_cpu = HEXAGON_CPU(cs);
> +        CPUHexagonState *found_env = &found_cpu->env;
> +        if (thread == found_env->threadId) {
> +            SET_SYSTEM_FIELD(found_env, HEX_SREG_STID, STID_PRIO, prio);
> +            qemu_log_mask(CPU_LOG_INT,
> +                          "%s: tid " TARGET_FMT_lx " prio = 0x%x\n",
>

Don't use %x


> +                          __func__, found_env->threadId, prio);
> +            resched(env);
> +            return;
> +        }
> +    }
>      g_assert_not_reached();
>  }
>
> --
> 2.34.1
>
>
Re: [PATCH v3 18/32] target/hexagon: Implement setprio, resched
Posted by Brian Cain 4 days ago
On Mon, Mar 16, 2026 at 11:49 AM Taylor Simpson
<ltaylorsimpson@gmail.com> wrote:
>
>
>
> On Tue, Mar 10, 2026 at 10:08 PM Brian Cain <brian.cain@oss.qualcomm.com> wrote:
>>
>> From: Brian Cain <bcain@quicinc.com>
>>
>> The hardware-assisted scheduler helps manage tasks on the run queue
>> and interrupt steering.
>>
>> This instruction is defined in the Qualcomm Hexagon V71 Programmer's Reference
>> Manual -
>> https://docs.qualcomm.com/bundle/publicresource/80-N2040-51_REV_AB_Hexagon_V71_ProgrammerS_Reference_Manual.pdf
>> See §11.9.2 SYSTEM MONITOR.
>
>
> This document doesn't have a section 11.9.2.
>
>>
>>
>> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
>> ---
>>  target/hexagon/op_helper.c | 75 ++++++++++++++++++++++++++++++++++++++
>>  1 file changed, 75 insertions(+)
>>
>> diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c
>> index 9090a5c20ab..36822219b4b 100644
>> --- a/target/hexagon/op_helper.c
>> +++ b/target/hexagon/op_helper.c
>> @@ -1575,6 +1575,62 @@ static void hexagon_wait_thread(CPUHexagonState *env, uint32_t PC)
>>      cpu_interrupt(cs, CPU_INTERRUPT_HALT);
>>  }
>>
>> +static inline QEMU_ALWAYS_INLINE void resched(CPUHexagonState *env)
>> +{
>> +    uint32_t schedcfg;
>> +    uint32_t schedcfg_en;
>> +    int int_number;
>> +    CPUState *cs;
>> +    uint32_t lowest_th_prio = 0; /* 0 is highest prio */
>> +    uint32_t bestwait_reg;
>> +    uint32_t best_prio;
>> +
>> +    BQL_LOCK_GUARD();
>> +    qemu_log_mask(CPU_LOG_INT, "%s: check resched\n", __func__);
>> +    HexagonCPU *cpu = env_archcpu(env);
>> +    schedcfg = cpu->globalregs ?
>> +        hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SCHEDCFG,
>> +                               env->threadId) : 0;
>> +    schedcfg_en = GET_FIELD(SCHEDCFG_EN, schedcfg);
>> +    int_number = GET_FIELD(SCHEDCFG_INTNO, schedcfg);
>> +
>> +    if (!schedcfg_en) {
>> +        return;
>> +    }
>> +
>> +    CPU_FOREACH(cs) {
>> +        HexagonCPU *thread = HEXAGON_CPU(cs);
>> +        CPUHexagonState *thread_env = &(thread->env);
>> +        uint32_t th_prio = GET_FIELD(
>> +            STID_PRIO, thread_env->t_sreg[HEX_SREG_STID]);
>> +        if (!hexagon_thread_is_enabled(thread_env)) {
>> +            continue;
>> +        }
>> +
>> +        lowest_th_prio = (lowest_th_prio > th_prio)
>> +            ? lowest_th_prio
>> +            : th_prio;
>> +    }
>> +
>> +    bestwait_reg = cpu->globalregs ?
>> +        hexagon_globalreg_read(cpu->globalregs, HEX_SREG_BESTWAIT,
>> +                               env->threadId) : 0;
>> +    best_prio = GET_FIELD(BESTWAIT_PRIO, bestwait_reg);
>> +
>> +    /*
>> +     * If the lowest priority thread is lower priority than the
>> +     * value in the BESTWAIT register, we must raise the reschedule
>> +     * interrupt on the lowest priority thread.
>> +     */
>> +    if (lowest_th_prio > best_prio) {
>> +        qemu_log_mask(CPU_LOG_INT,
>> +                "%s: raising resched int %d, cur PC 0x" TARGET_FMT_lx "\n",
>
>
> Don't use %d
>
>>
>> +                __func__, int_number, env->gpr[HEX_REG_PC]);
>> +        SET_SYSTEM_FIELD(env, HEX_SREG_BESTWAIT, BESTWAIT_PRIO, ~0);
>
>
> Is ~0 the correct value here?  Seems like it should be best_prio.

As tasks wait, the kernel can update the mutable BESTWAIT:PRIO field.

But when the scheduler is activated, then the bestwait priority resets.

>> +        hex_raise_interrupts(env, 1 << int_number, CPU_INTERRUPT_SWI);
>> +    }
>> +}
>> +
>>  void HELPER(wait)(CPUHexagonState *env, uint32_t PC)
>>  {
>>      BQL_LOCK_GUARD();
>> @@ -1680,8 +1736,27 @@ uint64_t HELPER(greg_read_pair)(CPUHexagonState *env, uint32_t reg)
>>      g_assert_not_reached();
>>  }
>>
>> +/*
>> + * setprio/resched - See the Hexagon V73 Programmer's Reference Manual,
>> + * Section 6.4 "Interrupt and Thread Scheduling"
>
>
> Neither the V71 (mentioned above) or V73 PRM contain section 6.4.

I'll remove the section number references to avoid confusion,
especially since they're not stable.

>> + */
>>  void HELPER(setprio)(CPUHexagonState *env, uint32_t thread, uint32_t prio)
>>  {
>> +    CPUState *cs;
>> +
>> +    BQL_LOCK_GUARD();
>> +    CPU_FOREACH(cs) {
>> +        HexagonCPU *found_cpu = HEXAGON_CPU(cs);
>> +        CPUHexagonState *found_env = &found_cpu->env;
>> +        if (thread == found_env->threadId) {
>> +            SET_SYSTEM_FIELD(found_env, HEX_SREG_STID, STID_PRIO, prio);
>> +            qemu_log_mask(CPU_LOG_INT,
>> +                          "%s: tid " TARGET_FMT_lx " prio = 0x%x\n",
>
>
> Don't use %x
>
>>
>> +                          __func__, found_env->threadId, prio);
>> +            resched(env);
>> +            return;
>> +        }
>> +    }
>>      g_assert_not_reached();
>>  }
>>
>> --
>> 2.34.1
>>