We have SSIF interface present in both DMI and ACPI tables, sometimes ssif_probe is called simultaneously from i2c interface (from ACPI) and while loading ipmi_ssif driver(from DMI table) at kernel boot. Both try to register the same SSIF interface simultaneously, where it hits a race.
In cases where ACPI and SMBIOS both 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 interface at the same address, we need to remove the SMBIOS one and add the ACPI one. Signed-off-by: Kamlakant Patel <kamlaka...@marvell.com> --- drivers/char/ipmi/ipmi_ssif.c | 52 +++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index ca9528c4f183..8d60e5cd2e17 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -1146,6 +1146,7 @@ MODULE_PARM_DESC(trydmi, "Setting this to zero will disable the default scan of static DEFINE_MUTEX(ssif_infos_mutex); static LIST_HEAD(ssif_infos); +static LIST_HEAD(ssif_clients); #define IPMI_SSIF_ATTR(name) \ static ssize_t ipmi_##name##_show(struct device *dev, \ @@ -1523,6 +1524,43 @@ 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) +/* + * 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_is_registered(struct i2c_client *client) +{ + struct ssif_addr_info *info; + int rv = 0; + + list_for_each_entry(info, &ssif_clients, link) { + if ((info->binfo.addr == client->addr) && + (!strcmp(info->adapter_name, client->adapter->name))) { + if (!ACPI_HANDLE(&info->client->dev)) { + /* Remove SMBIOS interface */ + ssif_remove(info->client); + rv = 0; + } else + rv = -EEXIST; + goto exit; + } + } + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info){ + rv = -ENOMEM; + goto exit; + } + + info->client = client; + info->adapter_name = client->adapter->name; + info->binfo.addr = client->addr; + list_add_tail(&info->link, &ssif_clients); +exit: + return rv; +} + static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) { unsigned char msg[3]; @@ -1534,6 +1572,13 @@ 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); + /* Check if the client is already registered. */ + if (ssif_is_registered(client)) { + mutex_unlock(&ssif_infos_mutex); + return 0; + } + resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL); if (!resp) return -ENOMEM; @@ -1759,6 +1804,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: @@ -1844,6 +1890,12 @@ static void free_ssif_clients(void) kfree(info->adapter_name); kfree(info); } + + list_for_each_entry_safe(info, tmp, &ssif_clients, link) { + list_del(&info->link); + kfree(info); + } + mutex_unlock(&ssif_infos_mutex); } -- 2.17.1 _______________________________________________ Openipmi-developer mailing list Openipmi-developer@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/openipmi-developer