lib: sbi: Clear IPIs before init_warm_startup in non-boot harts
authorEvgenii Shatokhin <e.shatokhin@yadro.com>
Sun, 5 Mar 2023 21:22:47 +0000 (00:22 +0300)
committerAnup Patel <anup@brainfault.org>
Fri, 10 Mar 2023 08:43:40 +0000 (14:13 +0530)
Since commit 50d4fde1c5a4 ("lib: Remove redundant sbi_platform_ipi_clear()
calls"), the IPI sent from the boot hart in wake_coldboot_harts() is not
cleared in the secondary harts until they reach sbi_ipi_init(). However,
sbi_hsm_init() and sbi_hsm_hart_wait() are called earlier, so a secondary
hart might enter sbi_hsm_hart_wait() with an already pending IPI.

sbi_hsm_hart_wait() makes sure the hart leaves the loop only when it is
actually ready, so a pending unrelated IPI should not cause safety issues.
However, it might be inefficient on certain hardware, because it prevents
"wfi" from stalling the hart even if the hardware supports this, making the
hart needlessly spin in a "busy-wait" loop.

This behaviour can be observed, for example, in a QEMU VM (QEMU 7.2.0) with
"-machine virt" running a Linux guest. Inserting delays in
sbi_hsm_hart_start() allows reproducing the issue more reliably.

The comment in wait_for_coldboot() suggests that the initial IPI is needed
in the warm resume path, so let us clear it before init_warm_startup()
only.

To do this, sbi_ipi_raw_clear() was created similar to sbi_ipi_raw_send().

Signed-off-by: Evgenii Shatokhin <e.shatokhin@yadro.com>
Reviewed-by: Anup Patel <anup@brainfault.org>
include/sbi/sbi_ipi.h
lib/sbi/sbi_init.c
lib/sbi/sbi_ipi.c

index f6ac8072f68a74ac43281b1af529bc60c149ed5a..f384e748672a00a09351e5da1b41ba16a03cfa06 100644 (file)
@@ -77,6 +77,8 @@ void sbi_ipi_process(void);
 
 int sbi_ipi_raw_send(u32 target_hart);
 
+void sbi_ipi_raw_clear(u32 target_hart);
+
 const struct sbi_ipi_device *sbi_ipi_get_device(void);
 
 void sbi_ipi_set_device(const struct sbi_ipi_device *dev);
index dcca2c811e1c43c239082ab190ae2faae8c4a972..ffa214ca7ee2c5b8d2320660e4d0e6df722de410 100644 (file)
@@ -442,10 +442,12 @@ static void __noreturn init_warmboot(struct sbi_scratch *scratch, u32 hartid)
        if (hstate < 0)
                sbi_hart_hang();
 
-       if (hstate == SBI_HSM_STATE_SUSPENDED)
+       if (hstate == SBI_HSM_STATE_SUSPENDED) {
                init_warm_resume(scratch, hartid);
-       else
+       } else {
+               sbi_ipi_raw_clear(hartid);
                init_warm_startup(scratch, hartid);
+       }
 }
 
 static atomic_t coldboot_lottery = ATOMIC_INITIALIZER(0);
index 1bcc2e40c90fe14254ba29f2419a947e8a1d5b04..b9f62056cb67eb3cfb668486fce0129ac82d4ae2 100644 (file)
@@ -217,6 +217,12 @@ int sbi_ipi_raw_send(u32 target_hart)
        return 0;
 }
 
+void sbi_ipi_raw_clear(u32 target_hart)
+{
+       if (ipi_dev && ipi_dev->ipi_clear)
+               ipi_dev->ipi_clear(target_hart);
+}
+
 const struct sbi_ipi_device *sbi_ipi_get_device(void)
 {
        return ipi_dev;