[PATCH v2 2/2] lib: sbi: Clear IPIs before init_warm_startup in non-boot harts

Evgenii Shatokhin e.shatokhin at yadro.com
Tue Feb 28 08:54:54 PST 2023


On 28.02.2023 19:19, Evgenii Shatokhin wrote:
> On 28.02.2023 07:41, Anup Patel wrote:
>> On Wed, Feb 22, 2023 at 2:39 AM Evgenii Shatokhin 
>> <e.shatokhin at yadro.com> wrote:
>>>
>>> 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 at yadro.com>
>>> ---
>>>   include/sbi/sbi_ipi.h | 2 ++
>>>   lib/sbi/sbi_init.c    | 6 ++++--
>>>   lib/sbi/sbi_ipi.c     | 6 ++++++
>>>   3 files changed, 12 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/include/sbi/sbi_ipi.h b/include/sbi/sbi_ipi.h
>>> index f6ac807..f384e74 100644
>>> --- a/include/sbi/sbi_ipi.h
>>> +++ b/include/sbi/sbi_ipi.h
>>> @@ -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);
>>> diff --git a/lib/sbi/sbi_init.c b/lib/sbi/sbi_init.c
>>> index e353c33..a9a4084 100644
>>> --- a/lib/sbi/sbi_init.c
>>> +++ b/lib/sbi/sbi_init.c
>>> @@ -438,10 +438,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);
>>> -       else
>>> +       } else {
>>> +               sbi_ipi_raw_clear(hartid);
>>
>> This effectively does nothing in many cases because for a lot of
>> platforms all HARTs enter OpenSBI at the same time and the HARTs
>> entering warm boot path will call sbi_ipi_raw_clear() even before
>> ipi_dev is provided by platform code.
> 
> Right, it would be a no-op then.
> 
> However, this is not the case when Linux 6.x boots, e.g. in QEMU with 
> "-machine virt" or with other platforms which provide "cold" IPI init 
> callbacks.
> 
> Here, the boot hart initializes ipi_dev early. For example, in QEMU with 
> "-machine virt", it does the following:
> init_coldboot() => sbi_ipi_init() => ... => aclint_mswi_cold_init().
> 
> Other harts are waiting in sbi_hsm_init() at the moment and will only 
> call sbi_ipi_init() after they leave sbi_hsm_hart_wait(), etc.

Correction: the secondary harts are waiting in init_warmboot() at that 
moment rather than in sbi_hsm_init(). Sorry, my bad.

Still, the boot hart only wakes them up with wake_coldboot_harts() after 
it has called sbi_ipi_init(), etc. So, by the time the secondary harts 
get to init_warm_startup(), ipi_dev should already be initialized and 
sbi_ipi_raw_clear() should work.

> 
> While they are waiting, their previous IPI remains pending. It can be 
> easily demonstrated if one checks the relevant bits of CSR_MIP in 
> sbi_hsm_hart_wait() right before the wfi loop. A delay in 
> sbi_hsm_hart_start(), same as for the first issue, also helps make it 
> visible.
> 
> csr_read(CSR_MIP) & (MIP_MSIP | MIP_MEIP) == 0x8 in my case at that 
> point. That is, MIP_MSIP bit is set even before the boot hart has send 
> an IPI to the target hart in sbi_hsm_hart_start(). Not good.
> 
> So, sbi_ipi_raw_clear(hartid) really helps in such cases, even if it is 
> a no-op in other situations.


> 
>>
>>>                  init_warm_startup(scratch, hartid);
>>> +       }
>>>
>>>          sbi_hart_switch_mode(hartid, scratch->next_arg1,
>>>                               scratch->next_addr,
>>> diff --git a/lib/sbi/sbi_ipi.c b/lib/sbi/sbi_ipi.c
>>> index 1bcc2e4..b9f6205 100644
>>> --- a/lib/sbi/sbi_ipi.c
>>> +++ b/lib/sbi/sbi_ipi.c
>>> @@ -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;
>>> -- 
>>> 2.34.1
>>>
>>>
>>>
>>> -- 
>>> opensbi mailing list
>>> opensbi at lists.infradead.org
>>> http://lists.infradead.org/mailman/listinfo/opensbi
>>
>> Regards,
>> Anup
>>

Regards,
Evgenii




More information about the opensbi mailing list