Hi, > 2026年7月20日 17:34,Bibo Mao <[email protected]> 写道: > > > > On 2026/7/20 下午5:20, Miao Wang wrote: >> Hi, >>> 2026年7月20日 17:04,Bibo Mao <[email protected]> 写道: >>> >>> >>> >>> On 2026/7/20 下午4:49, Miao Wang wrote: >>>> Hi, >>>>> 2026年7月20日 16:34,Bibo Mao <[email protected]> 写道: >>>>> >>>>> >>>>> >>>>> On 2026/7/16 上午4:48, Miao Wang via B4 Relay wrote: >>>>>> From: Miao Wang <[email protected]> >>>>>> The CSR_ESTAT register of a CPU can be read and written by both the >>>>>> CPU thread and other threads (e.g., the interrupt controller thread). >>>>>> Currently the possible readers and writers of CSR_ESTAT are: >>>>>> - Readers >>>>>> - tcg generated by trans_csrrd(, CSR_ESTAT) >>>>>> - loongarch_cpu_has_work() >>>>>> - Writers >>>>>> - tcg generated by trans_csrxchg(, CSR_ESTAT) >>>>>> - helper_csrwr_estat() >>>>>> - helper_csrrd_msgir() >>>>>> - loongarch_cpu_set_irq() >>>>>> - loongarch_cpu_do_interrupt() >>>>>> - loongarch_cpu_exec_interrupt() >>>>>> The access from the CPU thread is not synchronized with the access from >>>>>> other threads, which may lead to data races. The above readers and >>>>>> writers shall all run on the corresponding CPU thread except for >>>>>> loongarch_cpu_set_irq(). To fix this, the access to CSR_ESTAT in >>>>>> loongarch_cpu_set_irq() is moved to the CPU thread by using >>>>>> async_run_on_cpu(). >>>>>> The data race has been identified while running the test cases from >>>>>> dracut, which is using QEMU to boot a LoongArch guest. By running the >>>>>> tests repeatedly (about 30 times) in the following conditions, the >>>>>> guest will hang in the middle of booting: >>>>>> - Host architecture: LoongArch64 or Aarch64 >>>>>> - Guest kernel: 7.1.3+deb14-loong64 >>>>>> - Number of vCPUs: 1 or 2 >>>>>> - CPU Features: max, la464,msgint=off,ptw=off, or la464,msgint=off,ptw=on >>>>>> - Accelerator: tcg >>>>>> When the guest hangs, the guest kernel log shows various errors related >>>>>> to RCU stalls or other Soft Lockup or Hard Lockup issues. When running >>>>>> with 1 vCPU and the guest hangs, the guest kernel directly hangs without >>>>>> any messages and stucks at idle_exit. >>>>>> With this patch, the guest can boot successfully without any hangs >>>>>> during repeated runs of the test cases. >>>>>> Signed-off-by: Miao Wang <[email protected]> >>>>>> --- >>>>>> Changes in v2: >>>>>> - Simplify the changes to move the access to CSR_ESTAT from the only >>>>>> unsynchronized loongarch_cpu_set_irq() to the CPU thread using >>>>>> async_run_on_cpu() to avoid the race condition. >>>>>> - Link to v1: >>>>>> https://lore.kernel.org/qemu-devel/[email protected] >>>>>> --- >>>>>> target/loongarch/cpu.c | 27 +++++++++++++++++++-------- >>>>>> 1 file changed, 19 insertions(+), 8 deletions(-) >>>>>> diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c >>>>>> index >>>>>> fb03424ffa8cd5e5d250531cc2a36fa789fc25a9..126c3ad276cc44311942542e086bcabe41d91b63 >>>>>> 100644 >>>>>> --- a/target/loongarch/cpu.c >>>>>> +++ b/target/loongarch/cpu.c >>>>>> @@ -57,12 +57,27 @@ static vaddr loongarch_cpu_get_pc(CPUState *cs) >>>>>> #ifndef CONFIG_USER_ONLY >>>>>> #include "hw/loongarch/virt.h" >>>>>> +static void do_set_cpu_estat(CPUState *cs, run_on_cpu_data data) >>>>>> +{ >>>>>> + CPULoongArchState *env = cpu_env(cs); >>>>>> + CPUSysState *sys = env_sys(env); >>>>>> + >>>>>> + int irq = data.host_int; >>>>>> + int level = irq >= 0 ? 1 : 0; >>>>>> + irq = level ? irq : -irq; >>>>>> + >>>>>> + sys->CSR_ESTAT = deposit64(sys->CSR_ESTAT, irq, 1, level != 0); >>>>>> + if (FIELD_EX64(sys->CSR_ESTAT, CSR_ESTAT, IS)) { >>>>>> + cpu_interrupt(cs, CPU_INTERRUPT_HARD); >>>>>> + } else { >>>>>> + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); >>>>>> + } >>>>>> +} >>>>>> + >>>>>> void loongarch_cpu_set_irq(void *opaque, int irq, int level) >>>>>> { >>>>>> LoongArchCPU *cpu = opaque; >>>>>> - CPULoongArchState *env = &cpu->env; >>>>>> CPUState *cs = CPU(cpu); >>>>>> - CPUSysState *sys = env_sys(env); >>>>>> if (irq < 0 || irq >= N_IRQS) { >>>>>> return; >>>>>> @@ -71,12 +86,8 @@ void loongarch_cpu_set_irq(void *opaque, int irq, int >>>>>> level) >>>>>> if (kvm_enabled()) { >>>>>> kvm_loongarch_set_interrupt(cpu, irq, level); >>>>>> } else if (tcg_enabled()) { >>>>>> - sys->CSR_ESTAT = deposit64(sys->CSR_ESTAT, irq, 1, level != 0); >>>>>> - if (FIELD_EX64(sys->CSR_ESTAT, CSR_ESTAT, IS)) { >>>>>> - cpu_interrupt(cs, CPU_INTERRUPT_HARD); >>>>>> - } else { >>>>>> - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); >>>>>> - } >>>>>> + async_run_on_cpu(cs, do_set_cpu_estat, >>>>>> + RUN_ON_CPU_HOST_INT(level ? irq : -irq)); >>>>> This is a good method with async_run_on_cpu, there is one small issue >>>>> with irq == 0 interrupt injection where -irq/irq are both 0. >>>> Since according to Section 7.4.6. of Loongarch Reference Manual Vol1, >>>> bit [1:0] stands for the software interrupt SW1 and SW0, I expect that >>>> these two irqs should not be manipulated by hardware sources and thus >>>> the irq here should be larger than 2. Can you further explain under >>>> what condition the irq line 0 and 1 of a cpu core would be connected >>>> to external outputs. >>> loongarch_cpu_set_irq() is not only for external interrupts, percpu >>> interrupt injection also uses loongarch_cpu_set_irq() such as IRQ_TIMER etc. >> The users of loongarch_cpu_set_irq() are limited. The main usage is >> as the callback of qdev_init_gpio_in(). Other usage is for the timer, >> where the irq number is IRQ_TIMER, i.e. 11. >>> With SW1 and SW0, if the CPU set bit0 of estat, it will inject SW0 to this >>> CPU itself. And de-assert the irq if the CPU clear bit0 of estat. >> When SW1 and/or SW0 are set by the guest code running in the emulated > Linux kernel does not use SW0/SW1 now, which does not meaning that it is not > an interrupt. It is only interrupt generated by software, rather than > hardware. >
Yes, but it seem that in qemu, loongarch_cpu_set_irq() is used to handle interrupt generated by hardware. When software interrupts are asserted by the guest code of the emulated CPU, setting CSR_ESTAT[1:0] bits are handled by helper_csrwr_estat() and picked up by cpu_loongarch_hw_interrupts_pending() and are not relevant with loongarch_cpu_set_irq(). I do admit that SW1 and SW0 are really interrupts. What I insist is that the handling of them is not going through loongarch_cpu_set_irq(). >> CPU, the handling of the software IRQs in the qemu side does not >> involve loongarch_cpu_set_irq(). As a result, I believe that it is >> safe to assume that the second parameter of loongarch_cpu_set_irq(), >> i.e. irq should not be smaller than 2. I also suggest enforce this > So, What is behavior with the code *set_csr_estat(BIT(0))* on real hardware? > and how should QEMU TCG implement this instruction? If the guest code set CSR_ESTAT[1:0], then according to the manual, the CPU will behave as if it receives a hard interrupt. The handling of this in QEMU is described above, and I don't think the process is related to loongarch_cpu_set_irq(). I have searched the code and see no other callers of it. Cheers, Miao Wang
