Skip to content

Commit c4436c9

Browse files
kamlakantpcminyard
authored andcommitted
ipmi_ssif: avoid registering duplicate ssif interface
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 <[email protected]> Message-Id: <[email protected]> Signed-off-by: Corey Minyard <[email protected]>
1 parent 2033f68 commit c4436c9

File tree

1 file changed

+77
-1
lines changed

1 file changed

+77
-1
lines changed

drivers/char/ipmi/ipmi_ssif.c

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1427,6 +1427,10 @@ static struct ssif_addr_info *ssif_info_find(unsigned short addr,
14271427
restart:
14281428
list_for_each_entry(info, &ssif_infos, link) {
14291429
if (info->binfo.addr == addr) {
1430+
if (info->addr_src == SI_SMBIOS)
1431+
info->adapter_name = kstrdup(adapter_name,
1432+
GFP_KERNEL);
1433+
14301434
if (info->adapter_name || adapter_name) {
14311435
if (!info->adapter_name != !adapter_name) {
14321436
/* One is NULL and one is not */
@@ -1602,6 +1606,60 @@ static void test_multipart_messages(struct i2c_client *client,
16021606
#define GLOBAL_ENABLES_MASK (IPMI_BMC_EVT_MSG_BUFF | IPMI_BMC_RCV_MSG_INTR | \
16031607
IPMI_BMC_EVT_MSG_INTR)
16041608

1609+
static void ssif_remove_dup(struct i2c_client *client)
1610+
{
1611+
struct ssif_info *ssif_info = i2c_get_clientdata(client);
1612+
1613+
ipmi_unregister_smi(ssif_info->intf);
1614+
kfree(ssif_info);
1615+
}
1616+
1617+
static int ssif_add_infos(struct i2c_client *client)
1618+
{
1619+
struct ssif_addr_info *info;
1620+
1621+
info = kzalloc(sizeof(*info), GFP_KERNEL);
1622+
if (!info)
1623+
return -ENOMEM;
1624+
info->addr_src = SI_ACPI;
1625+
info->client = client;
1626+
info->adapter_name = kstrdup(client->adapter->name, GFP_KERNEL);
1627+
info->binfo.addr = client->addr;
1628+
list_add_tail(&info->link, &ssif_infos);
1629+
return 0;
1630+
}
1631+
1632+
/*
1633+
* Prefer ACPI over SMBIOS, if both are available.
1634+
* So if we get an ACPI interface and have already registered a SMBIOS
1635+
* interface at the same address, remove the SMBIOS and add the ACPI one.
1636+
*/
1637+
static int ssif_check_and_remove(struct i2c_client *client,
1638+
struct ssif_info *ssif_info)
1639+
{
1640+
struct ssif_addr_info *info;
1641+
1642+
list_for_each_entry(info, &ssif_infos, link) {
1643+
if (!info->client)
1644+
return 0;
1645+
if (!strcmp(info->adapter_name, client->adapter->name) &&
1646+
info->binfo.addr == client->addr) {
1647+
if (info->addr_src == SI_ACPI)
1648+
return -EEXIST;
1649+
1650+
if (ssif_info->addr_source == SI_ACPI &&
1651+
info->addr_src == SI_SMBIOS) {
1652+
dev_info(&client->dev,
1653+
"Removing %s-specified SSIF interface in favor of ACPI\n",
1654+
ipmi_addr_src_to_str(info->addr_src));
1655+
ssif_remove_dup(info->client);
1656+
return 0;
1657+
}
1658+
}
1659+
}
1660+
return 0;
1661+
}
1662+
16051663
static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
16061664
{
16071665
unsigned char msg[3];
@@ -1613,13 +1671,17 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
16131671
u8 slave_addr = 0;
16141672
struct ssif_addr_info *addr_info = NULL;
16151673

1674+
mutex_lock(&ssif_infos_mutex);
16161675
resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);
1617-
if (!resp)
1676+
if (!resp) {
1677+
mutex_unlock(&ssif_infos_mutex);
16181678
return -ENOMEM;
1679+
}
16191680

16201681
ssif_info = kzalloc(sizeof(*ssif_info), GFP_KERNEL);
16211682
if (!ssif_info) {
16221683
kfree(resp);
1684+
mutex_unlock(&ssif_infos_mutex);
16231685
return -ENOMEM;
16241686
}
16251687

@@ -1638,6 +1700,19 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
16381700
}
16391701
}
16401702

1703+
rv = ssif_check_and_remove(client, ssif_info);
1704+
/* If rv is 0 and addr source is not SI_ACPI, continue probing */
1705+
if (!rv && ssif_info->addr_source == SI_ACPI) {
1706+
rv = ssif_add_infos(client);
1707+
if (rv) {
1708+
dev_err(&client->dev, "Out of memory!, exiting ..\n");
1709+
goto out;
1710+
}
1711+
} else if (rv) {
1712+
dev_err(&client->dev, "Not probing, Interface already present\n");
1713+
goto out;
1714+
}
1715+
16411716
slave_addr = find_slave_address(client, slave_addr);
16421717

16431718
dev_info(&client->dev,
@@ -1850,6 +1925,7 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
18501925
kfree(ssif_info);
18511926
}
18521927
kfree(resp);
1928+
mutex_unlock(&ssif_infos_mutex);
18531929
return rv;
18541930

18551931
out_remove_attr:

0 commit comments

Comments
 (0)