Doorbells in SMT need to coordinate msgsnd/msgclr and DPDES access from
multiple threads that affect the same state.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
hw/ppc/ppc.c | 6 ++++++
include/hw/ppc/ppc.h | 1 +
target/ppc/excp_helper.c | 30 ++++++++++++++++++++++-----
target/ppc/misc_helper.c | 44 ++++++++++++++++++++++++++++++++++------
target/ppc/translate.c | 8 ++++++++
5 files changed, 78 insertions(+), 11 deletions(-)
diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c
index 1b1220c423..82e4408c5c 100644
--- a/hw/ppc/ppc.c
+++ b/hw/ppc/ppc.c
@@ -1436,6 +1436,12 @@ int ppc_cpu_pir(PowerPCCPU *cpu)
return env->spr_cb[SPR_PIR].default_value;
}
+int ppc_cpu_tir(PowerPCCPU *cpu)
+{
+ CPUPPCState *env = &cpu->env;
+ return env->spr_cb[SPR_TIR].default_value;
+}
+
PowerPCCPU *ppc_get_vcpu_by_pir(int pir)
{
CPUState *cs;
diff --git a/include/hw/ppc/ppc.h b/include/hw/ppc/ppc.h
index 02af03ada2..e095c002dc 100644
--- a/include/hw/ppc/ppc.h
+++ b/include/hw/ppc/ppc.h
@@ -6,6 +6,7 @@
void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level);
PowerPCCPU *ppc_get_vcpu_by_pir(int pir);
int ppc_cpu_pir(PowerPCCPU *cpu);
+int ppc_cpu_tir(PowerPCCPU *cpu);
/* PowerPC hardware exceptions management helpers */
typedef void (*clk_setup_cb)(void *opaque, uint32_t freq);
diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c
index 7d45035447..d40eecb4c7 100644
--- a/target/ppc/excp_helper.c
+++ b/target/ppc/excp_helper.c
@@ -3187,22 +3187,42 @@ void helper_book3s_msgclrp(CPUPPCState *env, target_ulong rb)
}
/*
- * sends a message to other threads that are on the same
+ * sends a message to another thread on the same
* multi-threaded processor
*/
void helper_book3s_msgsndp(CPUPPCState *env, target_ulong rb)
{
- int pir = env->spr_cb[SPR_PIR].default_value;
+ CPUState *cs = env_cpu(env);
+ PowerPCCPU *cpu = POWERPC_CPU(cs);
+ CPUState *ccs;
+ uint32_t nr_threads = cs->nr_threads;
+ int ttir = rb & PPC_BITMASK(57, 63);
helper_hfscr_facility_check(env, HFSCR_MSGP, "msgsndp", HFSCR_IC_MSGP);
- if (!dbell_type_server(rb)) {
+ if (!dbell_type_server(rb) || ttir >= nr_threads) {
+ return;
+ }
+
+ if (nr_threads == 1) {
+ ppc_set_irq(cpu, PPC_INTERRUPT_DOORBELL, 1);
return;
}
- /* TODO: TCG supports only one thread */
+ /* Does iothread need to be locked for walking CPU list? */
+ qemu_mutex_lock_iothread();
+ THREAD_SIBLING_FOREACH(cs, ccs) {
+ PowerPCCPU *ccpu = POWERPC_CPU(ccs);
+ uint32_t thread_id = ppc_cpu_tir(ccpu);
+
+ if (ttir == thread_id) {
+ ppc_set_irq(ccpu, PPC_INTERRUPT_DOORBELL, 1);
+ qemu_mutex_unlock_iothread();
+ return;
+ }
+ }
- book3s_msgsnd_common(pir, PPC_INTERRUPT_DOORBELL);
+ g_assert_not_reached();
}
#endif /* TARGET_PPC64 */
diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c
index a058eb24cd..1f1af21f33 100644
--- a/target/ppc/misc_helper.c
+++ b/target/ppc/misc_helper.c
@@ -184,14 +184,31 @@ void helper_store_pcr(CPUPPCState *env, target_ulong value)
*/
target_ulong helper_load_dpdes(CPUPPCState *env)
{
+ CPUState *cs = env_cpu(env);
+ CPUState *ccs;
+ uint32_t nr_threads = cs->nr_threads;
target_ulong dpdes = 0;
helper_hfscr_facility_check(env, HFSCR_MSGP, "load DPDES", HFSCR_IC_MSGP);
- /* TODO: TCG supports only one thread */
- if (env->pending_interrupts & PPC_INTERRUPT_DOORBELL) {
- dpdes = 1;
+ if (nr_threads == 1) {
+ if (env->pending_interrupts & PPC_INTERRUPT_DOORBELL) {
+ dpdes = 1;
+ }
+ return dpdes;
+ }
+
+ qemu_mutex_lock_iothread();
+ THREAD_SIBLING_FOREACH(cs, ccs) {
+ PowerPCCPU *ccpu = POWERPC_CPU(ccs);
+ CPUPPCState *cenv = &ccpu->env;
+ uint32_t thread_id = ppc_cpu_tir(ccpu);
+
+ if (cenv->pending_interrupts & PPC_INTERRUPT_DOORBELL) {
+ dpdes |= (0x1 << thread_id);
+ }
}
+ qemu_mutex_unlock_iothread();
return dpdes;
}
@@ -199,17 +216,32 @@ target_ulong helper_load_dpdes(CPUPPCState *env)
void helper_store_dpdes(CPUPPCState *env, target_ulong val)
{
PowerPCCPU *cpu = env_archcpu(env);
+ CPUState *cs = env_cpu(env);
+ CPUState *ccs;
+ uint32_t nr_threads = cs->nr_threads;
helper_hfscr_facility_check(env, HFSCR_MSGP, "store DPDES", HFSCR_IC_MSGP);
- /* TODO: TCG supports only one thread */
- if (val & ~0x1) {
+ if (val & ~(nr_threads - 1)) {
qemu_log_mask(LOG_GUEST_ERROR, "Invalid DPDES register value "
TARGET_FMT_lx"\n", val);
+ val &= (nr_threads - 1); /* Ignore the invalid bits */
+ }
+
+ if (nr_threads == 1) {
+ ppc_set_irq(cpu, PPC_INTERRUPT_DOORBELL, val & 0x1);
return;
}
- ppc_set_irq(cpu, PPC_INTERRUPT_DOORBELL, val & 0x1);
+ /* Does iothread need to be locked for walking CPU list? */
+ qemu_mutex_lock_iothread();
+ THREAD_SIBLING_FOREACH(cs, ccs) {
+ PowerPCCPU *ccpu = POWERPC_CPU(ccs);
+ uint32_t thread_id = ppc_cpu_tir(ccpu);
+
+ ppc_set_irq(cpu, PPC_INTERRUPT_DOORBELL, val & (0x1 << thread_id));
+ }
+ qemu_mutex_unlock_iothread();
}
#endif /* defined(TARGET_PPC64) */
diff --git a/target/ppc/translate.c b/target/ppc/translate.c
index 41a8b800bd..eb278c2683 100644
--- a/target/ppc/translate.c
+++ b/target/ppc/translate.c
@@ -815,11 +815,19 @@ void spr_write_pcr(DisasContext *ctx, int sprn, int gprn)
/* DPDES */
void spr_read_dpdes(DisasContext *ctx, int gprn, int sprn)
{
+ if (!gen_serialize_core(ctx)) {
+ return;
+ }
+
gen_helper_load_dpdes(cpu_gpr[gprn], cpu_env);
}
void spr_write_dpdes(DisasContext *ctx, int sprn, int gprn)
{
+ if (!gen_serialize_core(ctx)) {
+ return;
+ }
+
gen_helper_store_dpdes(cpu_env, cpu_gpr[gprn]);
}
#endif
--
2.40.1
On 6/22/23 11:33, Nicholas Piggin wrote:
> Doorbells in SMT need to coordinate msgsnd/msgclr and DPDES access from
> multiple threads that affect the same state.
>
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Reviewed-by: Cédric Le Goater <clg@kaod.org>
Thanks,
C.
> ---
> hw/ppc/ppc.c | 6 ++++++
> include/hw/ppc/ppc.h | 1 +
> target/ppc/excp_helper.c | 30 ++++++++++++++++++++++-----
> target/ppc/misc_helper.c | 44 ++++++++++++++++++++++++++++++++++------
> target/ppc/translate.c | 8 ++++++++
> 5 files changed, 78 insertions(+), 11 deletions(-)
>
> diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c
> index 1b1220c423..82e4408c5c 100644
> --- a/hw/ppc/ppc.c
> +++ b/hw/ppc/ppc.c
> @@ -1436,6 +1436,12 @@ int ppc_cpu_pir(PowerPCCPU *cpu)
> return env->spr_cb[SPR_PIR].default_value;
> }
>
> +int ppc_cpu_tir(PowerPCCPU *cpu)
> +{
> + CPUPPCState *env = &cpu->env;
> + return env->spr_cb[SPR_TIR].default_value;
> +}
> +
> PowerPCCPU *ppc_get_vcpu_by_pir(int pir)
> {
> CPUState *cs;
> diff --git a/include/hw/ppc/ppc.h b/include/hw/ppc/ppc.h
> index 02af03ada2..e095c002dc 100644
> --- a/include/hw/ppc/ppc.h
> +++ b/include/hw/ppc/ppc.h
> @@ -6,6 +6,7 @@
> void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level);
> PowerPCCPU *ppc_get_vcpu_by_pir(int pir);
> int ppc_cpu_pir(PowerPCCPU *cpu);
> +int ppc_cpu_tir(PowerPCCPU *cpu);
>
> /* PowerPC hardware exceptions management helpers */
> typedef void (*clk_setup_cb)(void *opaque, uint32_t freq);
> diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c
> index 7d45035447..d40eecb4c7 100644
> --- a/target/ppc/excp_helper.c
> +++ b/target/ppc/excp_helper.c
> @@ -3187,22 +3187,42 @@ void helper_book3s_msgclrp(CPUPPCState *env, target_ulong rb)
> }
>
> /*
> - * sends a message to other threads that are on the same
> + * sends a message to another thread on the same
> * multi-threaded processor
> */
> void helper_book3s_msgsndp(CPUPPCState *env, target_ulong rb)
> {
> - int pir = env->spr_cb[SPR_PIR].default_value;
> + CPUState *cs = env_cpu(env);
> + PowerPCCPU *cpu = POWERPC_CPU(cs);
> + CPUState *ccs;
> + uint32_t nr_threads = cs->nr_threads;
> + int ttir = rb & PPC_BITMASK(57, 63);
>
> helper_hfscr_facility_check(env, HFSCR_MSGP, "msgsndp", HFSCR_IC_MSGP);
>
> - if (!dbell_type_server(rb)) {
> + if (!dbell_type_server(rb) || ttir >= nr_threads) {
> + return;
> + }
> +
> + if (nr_threads == 1) {
> + ppc_set_irq(cpu, PPC_INTERRUPT_DOORBELL, 1);
> return;
> }
>
> - /* TODO: TCG supports only one thread */
> + /* Does iothread need to be locked for walking CPU list? */
> + qemu_mutex_lock_iothread();
> + THREAD_SIBLING_FOREACH(cs, ccs) {
> + PowerPCCPU *ccpu = POWERPC_CPU(ccs);
> + uint32_t thread_id = ppc_cpu_tir(ccpu);
> +
> + if (ttir == thread_id) {
> + ppc_set_irq(ccpu, PPC_INTERRUPT_DOORBELL, 1);
> + qemu_mutex_unlock_iothread();
> + return;
> + }
> + }
>
> - book3s_msgsnd_common(pir, PPC_INTERRUPT_DOORBELL);
> + g_assert_not_reached();
> }
> #endif /* TARGET_PPC64 */
>
> diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c
> index a058eb24cd..1f1af21f33 100644
> --- a/target/ppc/misc_helper.c
> +++ b/target/ppc/misc_helper.c
> @@ -184,14 +184,31 @@ void helper_store_pcr(CPUPPCState *env, target_ulong value)
> */
> target_ulong helper_load_dpdes(CPUPPCState *env)
> {
> + CPUState *cs = env_cpu(env);
> + CPUState *ccs;
> + uint32_t nr_threads = cs->nr_threads;
> target_ulong dpdes = 0;
>
> helper_hfscr_facility_check(env, HFSCR_MSGP, "load DPDES", HFSCR_IC_MSGP);
>
> - /* TODO: TCG supports only one thread */
> - if (env->pending_interrupts & PPC_INTERRUPT_DOORBELL) {
> - dpdes = 1;
> + if (nr_threads == 1) {
> + if (env->pending_interrupts & PPC_INTERRUPT_DOORBELL) {
> + dpdes = 1;
> + }
> + return dpdes;
> + }
> +
> + qemu_mutex_lock_iothread();
> + THREAD_SIBLING_FOREACH(cs, ccs) {
> + PowerPCCPU *ccpu = POWERPC_CPU(ccs);
> + CPUPPCState *cenv = &ccpu->env;
> + uint32_t thread_id = ppc_cpu_tir(ccpu);
> +
> + if (cenv->pending_interrupts & PPC_INTERRUPT_DOORBELL) {
> + dpdes |= (0x1 << thread_id);
> + }
> }
> + qemu_mutex_unlock_iothread();
>
> return dpdes;
> }
> @@ -199,17 +216,32 @@ target_ulong helper_load_dpdes(CPUPPCState *env)
> void helper_store_dpdes(CPUPPCState *env, target_ulong val)
> {
> PowerPCCPU *cpu = env_archcpu(env);
> + CPUState *cs = env_cpu(env);
> + CPUState *ccs;
> + uint32_t nr_threads = cs->nr_threads;
>
> helper_hfscr_facility_check(env, HFSCR_MSGP, "store DPDES", HFSCR_IC_MSGP);
>
> - /* TODO: TCG supports only one thread */
> - if (val & ~0x1) {
> + if (val & ~(nr_threads - 1)) {
> qemu_log_mask(LOG_GUEST_ERROR, "Invalid DPDES register value "
> TARGET_FMT_lx"\n", val);
> + val &= (nr_threads - 1); /* Ignore the invalid bits */
> + }
> +
> + if (nr_threads == 1) {
> + ppc_set_irq(cpu, PPC_INTERRUPT_DOORBELL, val & 0x1);
> return;
> }
>
> - ppc_set_irq(cpu, PPC_INTERRUPT_DOORBELL, val & 0x1);
> + /* Does iothread need to be locked for walking CPU list? */
> + qemu_mutex_lock_iothread();
> + THREAD_SIBLING_FOREACH(cs, ccs) {
> + PowerPCCPU *ccpu = POWERPC_CPU(ccs);
> + uint32_t thread_id = ppc_cpu_tir(ccpu);
> +
> + ppc_set_irq(cpu, PPC_INTERRUPT_DOORBELL, val & (0x1 << thread_id));
> + }
> + qemu_mutex_unlock_iothread();
> }
> #endif /* defined(TARGET_PPC64) */
>
> diff --git a/target/ppc/translate.c b/target/ppc/translate.c
> index 41a8b800bd..eb278c2683 100644
> --- a/target/ppc/translate.c
> +++ b/target/ppc/translate.c
> @@ -815,11 +815,19 @@ void spr_write_pcr(DisasContext *ctx, int sprn, int gprn)
> /* DPDES */
> void spr_read_dpdes(DisasContext *ctx, int gprn, int sprn)
> {
> + if (!gen_serialize_core(ctx)) {
> + return;
> + }
> +
> gen_helper_load_dpdes(cpu_gpr[gprn], cpu_env);
> }
>
> void spr_write_dpdes(DisasContext *ctx, int sprn, int gprn)
> {
> + if (!gen_serialize_core(ctx)) {
> + return;
> + }
> +
> gen_helper_store_dpdes(cpu_env, cpu_gpr[gprn]);
> }
> #endif
© 2016 - 2026 Red Hat, Inc.