On 3/2/20 6:17 PM, Lukas Auer wrote:
> On Fri, 2020-02-28 at 16:05 -0500, Sean Anderson wrote:
> 
>> The IPI code could have race conditions in several places.
>> * Several harts could race on the value of gd->arch->clint/plic
>> * Non-boot harts could race with the main hart on the DM subsystem In
>>   addition, if an IPI was pending when U-Boot started, it would cause the
>>   IPI handler to jump to address 0.
>>
>> To address these problems, a new function riscv_init_ipi is introduced. It
>> is called once during arch_cpu_init_dm. Before this point, no riscv_*_ipi
>> functions may be called. Access is synchronized by gd->arch->ipi_ready.
>>
>> Signed-off-by: Sean Anderson <sean...@gmail.com>
>> ---
>>
>> Changes in v5:
>> - New
>>
>>  arch/riscv/cpu/cpu.c                 |  9 ++++
>>  arch/riscv/include/asm/global_data.h |  1 +
>>  arch/riscv/include/asm/smp.h         | 43 ++++++++++++++++++
>>  arch/riscv/lib/andes_plic.c          | 34 +++++---------
>>  arch/riscv/lib/sbi_ipi.c             |  5 ++
>>  arch/riscv/lib/sifive_clint.c        | 33 +++++---------
>>  arch/riscv/lib/smp.c                 | 68 ++++++++--------------------
>>  7 files changed, 101 insertions(+), 92 deletions(-)
>>
>> diff --git a/arch/riscv/cpu/cpu.c b/arch/riscv/cpu/cpu.c
>> index e457f6acbf..a971ec8694 100644
>> --- a/arch/riscv/cpu/cpu.c
>> +++ b/arch/riscv/cpu/cpu.c
>> @@ -96,6 +96,15 @@ int arch_cpu_init_dm(void)
>>                      csr_write(CSR_SATP, 0);
>>      }
>>  
>> +#ifdef CONFIG_SMP
>> +    ret = riscv_init_ipi();
>> +    if (ret)
>> +            return ret;
>> +
>> +    /* Atomically set a flag enabling IPI handling */
>> +    WRITE_ONCE(gd->arch.ipi_ready, 1);
>> +#endif
>> +
>>      return 0;
>>  }
>>  
>> diff --git a/arch/riscv/include/asm/global_data.h 
>> b/arch/riscv/include/asm/global_data.h
>> index 7276d9763f..b24f8fd2a7 100644
>> --- a/arch/riscv/include/asm/global_data.h
>> +++ b/arch/riscv/include/asm/global_data.h
>> @@ -28,6 +28,7 @@ struct arch_global_data {
>>  #endif
>>  #ifdef CONFIG_SMP
>>      struct ipi_data ipi[CONFIG_NR_CPUS];
>> +    long ipi_ready; /* Set after riscv_init_ipi is called */
>>  #endif
>>  #ifndef CONFIG_XIP
>>      ulong available_harts;
>> diff --git a/arch/riscv/include/asm/smp.h b/arch/riscv/include/asm/smp.h
>> index 74de92ed13..1b428856b2 100644
>> --- a/arch/riscv/include/asm/smp.h
>> +++ b/arch/riscv/include/asm/smp.h
>> @@ -51,4 +51,47 @@ void handle_ipi(ulong hart);
>>   */
>>  int smp_call_function(ulong addr, ulong arg0, ulong arg1, int wait);
>>  
>> +/**
>> + * riscv_init_ipi() - Initialize inter-process interrupt (IPI) driver
>> + *
>> + * Platform code must provide this function. This function is called once 
>> after
>> + * the cpu driver is initialized. No other riscv_*_ipi() calls will be made
>> + * before this function is called.
>> + *
>> + * @return 0 if OK, -ve on error
>> + */
>> +int riscv_init_ipi(void);
>> +
>> +/**
>> + * riscv_send_ipi() - Send inter-processor interrupt (IPI)
>> + *
>> + * Platform code must provide this function.
>> + *
>> + * @hart: Hart ID of receiving hart
>> + * @return 0 if OK, -ve on error
>> + */
>> +int riscv_send_ipi(int hart);
>> +
>> +/**
>> + * riscv_clear_ipi() - Clear inter-processor interrupt (IPI)
>> + *
>> + * Platform code must provide this function.
>> + *
>> + * @hart: Hart ID of hart to be cleared
>> + * @return 0 if OK, -ve on error
>> + */
>> +int riscv_clear_ipi(int hart);
>> +
>> +/**
>> + * riscv_get_ipi() - Get status of inter-processor interrupt (IPI)
>> + *
>> + * Platform code must provide this function.
>> + *
>> + * @hart: Hart ID of hart to be checked
>> + * @pending: Pointer to variable with result of the check,
>> + *           1 if IPI is pending, 0 otherwise
>> + * @return 0 if OK, -ve on error
>> + */
>> +int riscv_get_ipi(int hart, int *pending);
>> +
>>  #endif
>> diff --git a/arch/riscv/lib/andes_plic.c b/arch/riscv/lib/andes_plic.c
>> index 20529ab3eb..8484f76386 100644
>> --- a/arch/riscv/lib/andes_plic.c
>> +++ b/arch/riscv/lib/andes_plic.c
>> @@ -30,20 +30,6 @@
>>  #define SEND_IPI_TO_HART(hart)  (0x80 >> (hart))
>>  
>>  DECLARE_GLOBAL_DATA_PTR;
>> -static int init_plic(void);
>> -
>> -#define PLIC_BASE_GET(void)                                         \
>> -    do {                                                            \
>> -            long *ret;                                              \
>> -                                                                    \
>> -            if (!gd->arch.plic) {                                   \
>> -                    ret = syscon_get_first_range(RISCV_SYSCON_PLIC); \
>> -                    if (IS_ERR(ret))                                \
>> -                            return PTR_ERR(ret);                    \
>> -                    gd->arch.plic = ret;                            \
>> -                    init_plic();                                    \
>> -            }                                                       \
>> -    } while (0)
>>  
>>  static int enable_ipi(int hart)
>>  {
>> @@ -93,13 +79,21 @@ static int init_plic(void)
>>      return -ENODEV;
>>  }
>>  
>> +int riscv_init_ipi(void)
>> +{
>> +    int ret = syscon_get_first_range(RISCV_SYSCON_PLIC);
>> +
>> +    if (IS_ERR(ret))
>> +            return PTR_ERR(ret);
>> +    gd->arch.plic = ret;
>> +
>> +    return init_plic();
>> +}
>> +
>>  int riscv_send_ipi(int hart)
>>  {
>> -    unsigned int ipi;
>> +    unsigned int ipi = (SEND_IPI_TO_HART(hart) << (8 * gd->arch.boot_hart));
>>  
>> -    PLIC_BASE_GET();
>> -
>> -    ipi = (SEND_IPI_TO_HART(hart) << (8 * gd->arch.boot_hart));
>>      writel(ipi, (void __iomem *)PENDING_REG(gd->arch.plic,
>>                              gd->arch.boot_hart));
>>  
>> @@ -110,8 +104,6 @@ int riscv_clear_ipi(int hart)
>>  {
>>      u32 source_id;
>>  
>> -    PLIC_BASE_GET();
>> -
>>      source_id = readl((void __iomem *)CLAIM_REG(gd->arch.plic, hart));
>>      writel(source_id, (void __iomem *)CLAIM_REG(gd->arch.plic, hart));
>>  
>> @@ -120,8 +112,6 @@ int riscv_clear_ipi(int hart)
>>  
>>  int riscv_get_ipi(int hart, int *pending)
>>  {
>> -    PLIC_BASE_GET();
>> -
>>      *pending = readl((void __iomem *)PENDING_REG(gd->arch.plic,
>>                                                   gd->arch.boot_hart));
>>      *pending = !!(*pending & SEND_IPI_TO_HART(hart));
>> diff --git a/arch/riscv/lib/sbi_ipi.c b/arch/riscv/lib/sbi_ipi.c
>> index 9a698ce74e..310d1bd2a4 100644
>> --- a/arch/riscv/lib/sbi_ipi.c
>> +++ b/arch/riscv/lib/sbi_ipi.c
>> @@ -7,6 +7,11 @@
>>  #include <common.h>
>>  #include <asm/sbi.h>
>>  
>> +int riscv_init_ipi(void)
>> +{
>> +    return 0;
>> +}
>> +
>>  int riscv_send_ipi(int hart)
>>  {
>>      ulong mask;
>> diff --git a/arch/riscv/lib/sifive_clint.c b/arch/riscv/lib/sifive_clint.c
>> index 5e0d25720b..62c1b2b0ef 100644
>> --- a/arch/riscv/lib/sifive_clint.c
>> +++ b/arch/riscv/lib/sifive_clint.c
>> @@ -24,22 +24,8 @@
>>  
>>  DECLARE_GLOBAL_DATA_PTR;
>>  
>> -#define CLINT_BASE_GET(void)                                                
>> \
>> -    do {                                                            \
>> -            long *ret;                                              \
>> -                                                                    \
>> -            if (!gd->arch.clint) {                                  \
>> -                    ret = syscon_get_first_range(RISCV_SYSCON_CLINT); \
>> -                    if (IS_ERR(ret))                                \
>> -                            return PTR_ERR(ret);                    \
>> -                    gd->arch.clint = ret;                           \
>> -            }                                                       \
>> -    } while (0)
>> -
>>  int riscv_get_time(u64 *time)
>>  {
>> -    CLINT_BASE_GET();
>> -
>>      *time = readq((void __iomem *)MTIME_REG(gd->arch.clint));
>>  
>>      return 0;
>> @@ -47,17 +33,24 @@ int riscv_get_time(u64 *time)
>>  
>>  int riscv_set_timecmp(int hart, u64 cmp)
>>  {
>> -    CLINT_BASE_GET();
>> -
>>      writeq(cmp, (void __iomem *)MTIMECMP_REG(gd->arch.clint, hart));
>>  
>>      return 0;
>>  }
>>  
>> +int riscv_init_ipi(void)
>> +{
>> +            long *ret = syscon_get_first_range(RISCV_SYSCON_CLINT);
>> +
>> +            if (IS_ERR(ret))
>> +                    return PTR_ERR(ret);
>> +            gd->arch.clint = ret;
>> +
>> +            return 0;
>> +}
>> +
> 
> Please fix the indentation here.
>

Ok.

>>  int riscv_send_ipi(int hart)
>>  {
>> -    CLINT_BASE_GET();
>> -
>>      writel(1, (void __iomem *)MSIP_REG(gd->arch.clint, hart));
>>  
>>      return 0;
>> @@ -65,8 +58,6 @@ int riscv_send_ipi(int hart)
>>  
>>  int riscv_clear_ipi(int hart)
>>  {
>> -    CLINT_BASE_GET();
>> -
>>      writel(0, (void __iomem *)MSIP_REG(gd->arch.clint, hart));
>>  
>>      return 0;
>> @@ -74,8 +65,6 @@ int riscv_clear_ipi(int hart)
>>  
>>  int riscv_get_ipi(int hart, int *pending)
>>  {
>> -    CLINT_BASE_GET();
>> -
>>      *pending = readl((void __iomem *)MSIP_REG(gd->arch.clint, hart));
>>  
>>      return 0;
>> diff --git a/arch/riscv/lib/smp.c b/arch/riscv/lib/smp.c
>> index 17adb35730..3b1e52e9b2 100644
>> --- a/arch/riscv/lib/smp.c
>> +++ b/arch/riscv/lib/smp.c
>> @@ -12,38 +12,6 @@
>>  
>>  DECLARE_GLOBAL_DATA_PTR;
>>  
>> -/**
>> - * riscv_send_ipi() - Send inter-processor interrupt (IPI)
>> - *
>> - * Platform code must provide this function.
>> - *
>> - * @hart: Hart ID of receiving hart
>> - * @return 0 if OK, -ve on error
>> - */
>> -extern int riscv_send_ipi(int hart);
>> -
>> -/**
>> - * riscv_clear_ipi() - Clear inter-processor interrupt (IPI)
>> - *
>> - * Platform code must provide this function.
>> - *
>> - * @hart: Hart ID of hart to be cleared
>> - * @return 0 if OK, -ve on error
>> - */
>> -extern int riscv_clear_ipi(int hart);
>> -
>> -/**
>> - * riscv_get_ipi() - Get status of inter-processor interrupt (IPI)
>> - *
>> - * Platform code must provide this function.
>> - *
>> - * @hart: Hart ID of hart to be checked
>> - * @pending: Pointer to variable with result of the check,
>> - *           1 if IPI is pending, 0 otherwise
>> - * @return 0 if OK, -ve on error
>> - */
>> -extern int riscv_get_ipi(int hart, int *pending);
>> -
>>  static int send_ipi_many(struct ipi_data *ipi, int wait)
>>  {
>>      ofnode node, cpus;
>> @@ -110,37 +78,41 @@ void handle_ipi(ulong hart)
>>      int ret;
>>      void (*smp_function)(ulong hart, ulong arg0, ulong arg1);
>>  
>> -    if (hart >= CONFIG_NR_CPUS)
>> +    if (hart >= CONFIG_NR_CPUS || !READ_ONCE(gd->arch.ipi_ready))
>>              return;
>>  
>> -    __smp_mb();
>> -
>> -    smp_function = (void (*)(ulong, ulong, ulong))gd->arch.ipi[hart].addr;
>> -    invalidate_icache_all();
>> -
> 
> Don't move this. It is intended to be run before the IPI is cleared.

Hm, ok. I think I moved it to after because of the 'if (!smp_function)'
check, but those two don't really need to be done together.

>>      /*
>>       * Clear the IPI to acknowledge the request before jumping to the
>>       * requested function.
>>       */
>>      ret = riscv_clear_ipi(hart);
>>      if (ret) {
>> -            pr_err("Cannot clear IPI of hart %ld\n", hart);
>> +            pr_err("Cannot clear IPI of hart %ld (error %d)\n", hart, ret);
>>              return;
>>      }
>>  
>> +    __smp_mb();
>> +
>> +    smp_function = (void (*)(ulong, ulong, ulong))gd->arch.ipi[hart].addr;
>> +    /*
>> +     * There may be an IPI raised before u-boot begins execution, so check
>> +     * to ensure we actually have a function to call.
>> +     */
>> +    if (!smp_function)
>> +            return;
>> +    log_debug("hart = %lu func = %p\n", hart, smp_function);
> 
> The log messages might be corrupted if multiple harts are calling the
> log function here. I have not looked into the details so this might not
> be an issue. In that case it is fine to keep, otherwise please remove
> it.

I ran into this problem a lot when debugging. I ended up implementing a
spinlock around puts/putc. I agree it's probably better to remove this,
but I worry that concurrency bugs will become much harder to track down
without some kind of feedback. (This same criticism applies to the log
message above as well).

> 
> Thanks,
> Lukas
> 
>> +    invalidate_icache_all();
>> +
>>      smp_function(hart, gd->arch.ipi[hart].arg0, gd->arch.ipi[hart].arg1);
>>  }
>>  
>>  int smp_call_function(ulong addr, ulong arg0, ulong arg1, int wait)
>>  {
>> -    int ret = 0;
>> -    struct ipi_data ipi;
>> +    struct ipi_data ipi = {
>> +            .addr = addr,
>> +            .arg0 = arg0,
>> +            .arg1 = arg1,
>> +    };
>>  
>> -    ipi.addr = addr;
>> -    ipi.arg0 = arg0;
>> -    ipi.arg1 = arg1;
>> -
>> -    ret = send_ipi_many(&ipi, wait);
>> -
>> -    return ret;
>> +    return send_ipi_many(&ipi, wait);
>>  }


Reply via email to