Re: [Openipmi-developer] [PATCH RFC] ipmi_ssif: avoid registering duplicate ssif interface
Brought to you by:
cminyard
|
From: Corey M. <mi...@ac...> - 2019-07-24 20:47:02
|
On Wed, Jul 17, 2019 at 04:08:59PM +0000, Kamlakant Patel wrote:
In general I think this is ok. A few issues, though:
> 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 one.
Text in the header needs to be <=75 characters wide, per kernel style.
>
> 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:
> 1. Adds ACPI entry also to the 'ssif_infos' list.
> 2. Checks the list if SMBIOS is already registered, removes it and adds ACPI.
> 3. If ACPI is already registered, it ignores SMBIOS.
> 4. Adds mutex lock throughout the probe process to avoid race.
>
> Signed-off-by: Kamlakant Patel <kam...@ma...>
> ---
> drivers/char/ipmi/ipmi_ssif.c | 89 +++++++++++++++++++++++++++++++++--
> 1 file changed, 84 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c
> index 8b5aec5430f1..763279b5e17c 100644
> --- a/drivers/char/ipmi/ipmi_ssif.c
> +++ b/drivers/char/ipmi/ipmi_ssif.c
> @@ -185,6 +185,7 @@ struct ssif_addr_info {
> char *adapter_name;
> int debug;
> int slave_addr;
> + bool is_probed;
> enum ipmi_addr_src addr_src;
> union ipmi_smi_info_union addr_info;
> struct device *dev;
> @@ -1312,6 +1313,7 @@ static int ssif_remove(struct i2c_client *client)
>
> list_for_each_entry(addr_info, &ssif_infos, link) {
> if (addr_info->client == client) {
> + addr_info->is_probed = 0;
> addr_info->client = NULL;
> break;
> }
> @@ -1414,7 +1416,8 @@ static int strcmp_nospace(char *s1, char *s2)
> return 0;
> }
>
> -static struct ssif_addr_info *ssif_info_find(unsigned short addr,
> +static struct ssif_addr_info *ssif_info_find(struct i2c_client *client,
> + unsigned short addr,
> char *adapter_name,
> bool match_null_name)
> {
> @@ -1423,6 +1426,13 @@ 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->client && info->addr_src == SI_SMBIOS &&
> + client) {
> + info->client = client;
> + info->adapter_name = kstrdup(client->adapter->name,
> + GFP_KERNEL);
> + }
> +
> if (info->adapter_name || adapter_name) {
> if (!info->adapter_name != !adapter_name) {
> /* One is NULL and one is not */
> @@ -1592,12 +1602,73 @@ static void test_multipart_messages(struct i2c_client *client,
> return;
> }
>
> +static void ssif_remove_dup(struct i2c_client *client)
> +{
> + struct ssif_info *ssif_info = i2c_get_clientdata(client);
> +
> + if (!ssif_info)
> + return;
> + ipmi_unregister_smi(ssif_info->intf);
> + kfree(ssif_info);
> +}
Space here.
> /*
> * Global enables we care about.
> */
> #define GLOBAL_ENABLES_MASK (IPMI_BMC_EVT_MSG_BUFF | IPMI_BMC_RCV_MSG_INTR | \
> IPMI_BMC_EVT_MSG_INTR)
>
> +static int ssif_check_present(struct ssif_addr_info *info,
> + struct i2c_client *client)
ssif_client_match() or something like that would probably be a better
name here.
> +{
> + if (!strcmp(info->adapter_name, client->adapter->name) &&
> + (info->binfo.addr == client->addr))
> + return true;
> +
> + return false;
> +}
> +
> +/*
> + * 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_info *ssif_info)
> +{
It would be better if is_xxx() functions don't have side effects. That
can surprise the user. A different function name would probably be
better.
> + struct ssif_addr_info *info;
> +
> + list_for_each_entry(info, &ssif_infos, link) {
> + if (info->is_probed) {
> + if (ssif_check_present(info, client)) {
> + if (info->addr_src == SI_ACPI) {
> + return -EEXIST;
> + } else 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);
> + }
> + }
> + } else if (ssif_info->addr_source == SI_SMBIOS) {
> + info->is_probed = true;
I'm trying to figure out the purpose of is_probed. Wouldn't
checkng client be good enough?
Then you don't need all this, and the check_present call
could just return false if client is not set.
> + return 0;
> + }
> + }
> +
> + info = kzalloc(sizeof(*info), GFP_KERNEL);
> + if (!info)
> + return -ENOMEM;
> + info->addr_src = ssif_info->addr_source;
> + info->is_probed = true;
> + info->client = client;
> + info->adapter_name = kstrdup(client->adapter->name, GFP_KERNEL);
> + info->binfo.addr = client->addr;
> + list_add_tail(&info->link, &ssif_infos);
You are duplicating a bunch of code here. Make a function.
Also, hard-coded additions may be an issue here, as they are
not SMBIOS, but they already should have addr_info items.
I really should rename ssif_infos to ssif_addr_infos and
document it better :(.
I'm wishing there was a simpler way to do this, but I don't
see one.
-corey
> +
> + return 0;
> +}
> +
> static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
> {
> unsigned char msg[3];
> @@ -1609,6 +1680,7 @@ 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)
> return -ENOMEM;
> @@ -1620,8 +1692,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
> }
>
> if (!check_acpi(ssif_info, &client->dev)) {
> - addr_info = ssif_info_find(client->addr, client->adapter->name,
> - true);
> + addr_info = ssif_info_find(client, client->addr,
> + client->adapter->name, true);
> if (!addr_info) {
> /* Must have come in through sysfs. */
> ssif_info->addr_source = SI_HOTMOD;
> @@ -1633,7 +1705,13 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
> slave_addr = addr_info->slave_addr;
> }
> }
> -
> + /* Check if SSIF is already registered */
> + if (ssif_is_registered(client, ssif_info)) {
> + kfree(resp);
> + kfree(ssif_info);
> + mutex_unlock(&ssif_infos_mutex);
> + return 0;
> + }
> slave_addr = find_slave_address(client, slave_addr);
>
> dev_info(&client->dev,
> @@ -1846,6 +1924,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:
> @@ -1878,7 +1957,7 @@ static int new_ssif_client(int addr, char *adapter_name,
> int rv = 0;
>
> mutex_lock(&ssif_infos_mutex);
> - if (ssif_info_find(addr, adapter_name, false)) {
> + if (ssif_info_find(NULL, addr, adapter_name, false)) {
> rv = -EEXIST;
> goto out_unlock;
> }
> --
> 2.17.1
>
|