On Wed, Aug 21, 2019 at 12:04:33PM +0000, Kamlakant Patel wrote:
> It is possible that SSIF interface entry is present in both DMI and ACPI
> tables. In SMP systems, in such cases it is possible that ssif_probe could
> be called simultaneously from i2c interface (from ACPI) and from DMI on
> different CPUs at kernel boot. Both try to register same SSIF interface
> simultaneously and result in race.
> 
> In such cases where ACPI and SMBIOS both IPMI entries are available, we
> need to prefer ACPI over SMBIOS so that ACPI functions work properly if
> they use IPMI.
> So, if we get an ACPI interface and have already registered an SMBIOS
> at the same address, we need to remove the SMBIOS one and add the ACPI.
> 
> Log:
> [   38.774743] ipmi device interface
> [   38.805006] ipmi_ssif: IPMI SSIF Interface driver
> [   38.861979] ipmi_ssif i2c-IPI0001:06: ssif_probe CPU 99 ***
> [   38.863655] ipmi_ssif 0-000e: ssif_probe CPU 14 ***
> [   38.863658] ipmi_ssif: Trying SMBIOS-specified SSIF interface at i2c 
> address 0xe, adapter xlp9xx-i2c, slave address 0x0
> [   38.869500] ipmi_ssif: Trying ACPI-specified SSIF interface at i2c address 
> 0xe, adapter xlp9xx-i2c, slave address 0x0
> [   38.914530] ipmi_ssif: Unable to clear message flags: -22 7 c7
> [   38.952429] ipmi_ssif: Unable to clear message flags: -22 7 00
> [   38.994734] ipmi_ssif: Error getting global enables: -22 7 00
> [   39.015877] ipmi_ssif 0-000e: IPMI message handler: Found new BMC (man_id: 
> 0x00b3d1, prod_id: 0x0001, dev_id: 0x20)
> [   39.377645] ipmi_ssif i2c-IPI0001:06: IPMI message handler: Found new BMC 
> (man_id: 0x00b3d1, prod_id: 0x0001, dev_id: 0x20)
> [   39.387863] ipmi_ssif 0-000e: IPMI message handler: BMC returned incorrect 
> response, expected netfn 7 cmd 42, got netfn 7 cmd 1
> ...
> [NOTE] : Added custom prints to explain the problem.
> 
> In the above log, ssif_probe is executed simultaneously on two different
> CPUs.
> 
> This patch fixes this issue in following way:
>  - Adds ACPI entry also to the 'ssif_infos' list.
>  - Checks the list if SMBIOS is already registered, removes it and adds
>    ACPI.
>  - If ACPI is already registered, it ignores SMBIOS.
>  - Adds mutex lock throughout the probe process to avoid race.
> 
> Signed-off-by: Kamlakant Patel <kamlaka...@marvell.com>
> ---
> Hi Corey,
> 
> I have done a few improvements on the code based on internal review comments.
> Could you please pick this patch instead of previous one.

Ok, got it, thanks.

-corey

> 
> Thanks,
> Kamlakant Patel
> 
> Changes since v2:
>  - Handle mutex_unlock for all error paths.
>  - Removed unnecessary goto from ssif_check_and_remove.
>  - Removed unnecessary continue from ssif_check_and_remove.
>  - Removed unnecessary ssif_client_match function.
>  - Removed unnecessary NULL check.
>  - Moved ssif_add_infos to ssif_probe to avoid to make it more readable.
> 
> Changes since v1:
>  - Avoid using separate list for handling ACPI entries.
>  - This patch adds ACPI entry also to the ssif_infos list.
> 
>  drivers/char/ipmi/ipmi_ssif.c | 78 
> ++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 77 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c
> index 305fa505..ee60892 100644
> --- a/drivers/char/ipmi/ipmi_ssif.c
> +++ b/drivers/char/ipmi/ipmi_ssif.c
> @@ -1428,6 +1428,10 @@ static struct ssif_addr_info *ssif_info_find(unsigned 
> short addr,
>  restart:
>       list_for_each_entry(info, &ssif_infos, link) {
>               if (info->binfo.addr == addr) {
> +                     if (info->addr_src == SI_SMBIOS)
> +                             info->adapter_name = kstrdup(adapter_name,
> +                                                          GFP_KERNEL);
> +
>                       if (info->adapter_name || adapter_name) {
>                               if (!info->adapter_name != !adapter_name) {
>                                       /* One is NULL and one is not */
> @@ -1603,6 +1607,60 @@ static void test_multipart_messages(struct i2c_client 
> *client,
>  #define GLOBAL_ENABLES_MASK (IPMI_BMC_EVT_MSG_BUFF | IPMI_BMC_RCV_MSG_INTR | 
> \
>                            IPMI_BMC_EVT_MSG_INTR)
>  
> +static void ssif_remove_dup(struct i2c_client *client)
> +{
> +     struct ssif_info *ssif_info = i2c_get_clientdata(client);
> +
> +     ipmi_unregister_smi(ssif_info->intf);
> +     kfree(ssif_info);
> +}
> +
> +static int ssif_add_infos(struct i2c_client *client)
> +{
> +     struct ssif_addr_info *info;
> +
> +     info = kzalloc(sizeof(*info), GFP_KERNEL);
> +     if (!info)
> +             return -ENOMEM;
> +     info->addr_src = SI_ACPI;
> +     info->client = client;
> +     info->adapter_name = kstrdup(client->adapter->name, GFP_KERNEL);
> +     info->binfo.addr = client->addr;
> +     list_add_tail(&info->link, &ssif_infos);
> +     return 0;
> +}
> +
> +/*
> + * Prefer ACPI over SMBIOS, if both are available.
> + * So if we get an ACPI interface and have already registered a SMBIOS
> + * interface at the same address, remove the SMBIOS and add the ACPI one.
> + */
> +static int ssif_check_and_remove(struct i2c_client *client,
> +                           struct ssif_info *ssif_info)
> +{
> +     struct ssif_addr_info *info;
> +
> +     list_for_each_entry(info, &ssif_infos, link) {
> +             if (!info->client)
> +                     return 0;
> +             if (!strcmp(info->adapter_name, client->adapter->name) &&
> +                 info->binfo.addr == client->addr) {
> +                     if (info->addr_src == SI_ACPI)
> +                             return -EEXIST;
> +
> +                     if (ssif_info->addr_source == SI_ACPI &&
> +                         info->addr_src == SI_SMBIOS) {
> +                             dev_info(&client->dev,
> +                                      "Removing %s-specified SSIF interface 
> in favor of ACPI\n",
> +                                      ipmi_addr_src_to_str(info->addr_src));
> +                             ssif_remove_dup(info->client);
> +                             return 0;
> +                     }
> +             }
> +     }
> +     return 0;
> +}
> +
>  static int ssif_probe(struct i2c_client *client, const struct i2c_device_id 
> *id)
>  {
>       unsigned char     msg[3];
> @@ -1614,13 +1672,17 @@ static int ssif_probe(struct i2c_client *client, 
> const struct i2c_device_id *id)
>       u8                slave_addr = 0;
>       struct ssif_addr_info *addr_info = NULL;
>  
> +     mutex_lock(&ssif_infos_mutex);
>       resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);
> -     if (!resp)
> +     if (!resp) {
> +             mutex_unlock(&ssif_infos_mutex);
>               return -ENOMEM;
> +     }
>  
>       ssif_info = kzalloc(sizeof(*ssif_info), GFP_KERNEL);
>       if (!ssif_info) {
>               kfree(resp);
> +             mutex_unlock(&ssif_infos_mutex);
>               return -ENOMEM;
>       }
>  
> @@ -1639,6 +1701,19 @@ static int ssif_probe(struct i2c_client *client, const 
> struct i2c_device_id *id)
>               }
>       }
>  
> +     rv = ssif_check_and_remove(client, ssif_info);
> +     /* If rv is 0 and addr source is not SI_ACPI, continue probing */
> +     if (!rv && ssif_info->addr_source == SI_ACPI) {
> +             rv = ssif_add_infos(client);
> +             if (rv) {
> +                     dev_err(&client->dev, "Out of memory!, exiting ..\n");
> +                     goto out;
> +             }
> +     } else if (rv) {
> +             dev_err(&client->dev, "Not probing, Interface already 
> present\n");
> +             goto out;
> +     }
> +
>       slave_addr = find_slave_address(client, slave_addr);
>  
>       dev_info(&client->dev,
> @@ -1851,6 +1926,7 @@ static int ssif_probe(struct i2c_client *client, const 
> struct i2c_device_id *id)
>               kfree(ssif_info);
>       }
>       kfree(resp);
> +     mutex_unlock(&ssif_infos_mutex);
>       return rv;
>  
>  out_remove_attr:
> -- 
> 1.8.3.1
> 


_______________________________________________
Openipmi-developer mailing list
Openipmi-developer@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/openipmi-developer

Reply via email to