target/riscv: Trigger interrupt on MIP update asynchronously
The requirement of holding the iothread_mutex is burdersome when swapping the background and foreground registers in the Hypervisor extension. To avoid the requrirement let's set the interrupt asynchronously. Signed-off-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Palmer Dabbelt <palmer@sifive.com> Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
This commit is contained in:
parent
356d74192a
commit
0a01f2eecb
|
@ -82,10 +82,31 @@ int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint32_t interrupts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* iothread_mutex must be held */
|
struct CpuAsyncInfo {
|
||||||
|
uint32_t new_mip;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void riscv_cpu_update_mip_irqs_async(CPUState *target_cpu_state,
|
||||||
|
run_on_cpu_data data)
|
||||||
|
{
|
||||||
|
CPURISCVState *env = &RISCV_CPU(target_cpu_state)->env;
|
||||||
|
RISCVCPU *cpu = riscv_env_get_cpu(env);
|
||||||
|
struct CpuAsyncInfo *info = (struct CpuAsyncInfo *) data.host_ptr;
|
||||||
|
|
||||||
|
if (info->new_mip) {
|
||||||
|
cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
|
||||||
|
} else {
|
||||||
|
cpu_reset_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(info);
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value)
|
uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value)
|
||||||
{
|
{
|
||||||
CPURISCVState *env = &cpu->env;
|
CPURISCVState *env = &cpu->env;
|
||||||
|
CPUState *cs = CPU(cpu);
|
||||||
|
struct CpuAsyncInfo *info;
|
||||||
uint32_t old, new, cmp = atomic_read(&env->mip);
|
uint32_t old, new, cmp = atomic_read(&env->mip);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
@ -94,11 +115,11 @@ uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value)
|
||||||
cmp = atomic_cmpxchg(&env->mip, old, new);
|
cmp = atomic_cmpxchg(&env->mip, old, new);
|
||||||
} while (old != cmp);
|
} while (old != cmp);
|
||||||
|
|
||||||
if (new) {
|
info = g_new(struct CpuAsyncInfo, 1);
|
||||||
cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
|
info->new_mip = new;
|
||||||
} else {
|
|
||||||
cpu_reset_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
|
async_run_on_cpu(cs, riscv_cpu_update_mip_irqs_async,
|
||||||
}
|
RUN_ON_CPU_HOST_PTR(info));
|
||||||
|
|
||||||
return old;
|
return old;
|
||||||
}
|
}
|
||||||
|
|
|
@ -555,9 +555,7 @@ static int rmw_mip(CPURISCVState *env, int csrno, target_ulong *ret_value,
|
||||||
uint32_t old_mip;
|
uint32_t old_mip;
|
||||||
|
|
||||||
if (mask) {
|
if (mask) {
|
||||||
qemu_mutex_lock_iothread();
|
|
||||||
old_mip = riscv_cpu_update_mip(cpu, mask, (new_value & mask));
|
old_mip = riscv_cpu_update_mip(cpu, mask, (new_value & mask));
|
||||||
qemu_mutex_unlock_iothread();
|
|
||||||
} else {
|
} else {
|
||||||
old_mip = atomic_read(&env->mip);
|
old_mip = atomic_read(&env->mip);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue