mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-09-22 09:34:56 +02:00
ipmi: Rework SMI registration failure
There were certain situations where ipmi_register_smi() would return a failure, but the interface would still be registered and would need to be unregistered. This is obviously a bad design and resulted in an oops in certain failure cases. If the interface is started up in ipmi_register_smi(), then an error occurs, shut down the interface there so the cleanup can be done properly. Fix the various smi users, too. Signed-off-by: Corey Minyard <cminyard@mvista.com> Reported-by: Justin Ernst <justin.ernst@hpe.com> Tested-by: Justin Ernst <justin.ernst@hpe.com> Cc: Andrew Banman <abanman@hpe.com> Cc: Russ Anderson <russ.anderson@hpe.com> Cc: <stable@vger.kernel.org> # 4.18.x
This commit is contained in:
@@ -3381,39 +3381,45 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers,
|
|||||||
|
|
||||||
rv = handlers->start_processing(send_info, intf);
|
rv = handlers->start_processing(send_info, intf);
|
||||||
if (rv)
|
if (rv)
|
||||||
goto out;
|
goto out_err;
|
||||||
|
|
||||||
rv = __bmc_get_device_id(intf, NULL, &id, NULL, NULL, i);
|
rv = __bmc_get_device_id(intf, NULL, &id, NULL, NULL, i);
|
||||||
if (rv) {
|
if (rv) {
|
||||||
dev_err(si_dev, "Unable to get the device id: %d\n", rv);
|
dev_err(si_dev, "Unable to get the device id: %d\n", rv);
|
||||||
goto out;
|
goto out_err_started;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_lock(&intf->bmc_reg_mutex);
|
mutex_lock(&intf->bmc_reg_mutex);
|
||||||
rv = __scan_channels(intf, &id);
|
rv = __scan_channels(intf, &id);
|
||||||
mutex_unlock(&intf->bmc_reg_mutex);
|
mutex_unlock(&intf->bmc_reg_mutex);
|
||||||
|
if (rv)
|
||||||
|
goto out_err_bmc_reg;
|
||||||
|
|
||||||
out:
|
/*
|
||||||
if (rv) {
|
* Keep memory order straight for RCU readers. Make
|
||||||
ipmi_bmc_unregister(intf);
|
* sure everything else is committed to memory before
|
||||||
list_del_rcu(&intf->link);
|
* setting intf_num to mark the interface valid.
|
||||||
mutex_unlock(&ipmi_interfaces_mutex);
|
*/
|
||||||
synchronize_srcu(&ipmi_interfaces_srcu);
|
smp_wmb();
|
||||||
cleanup_srcu_struct(&intf->users_srcu);
|
intf->intf_num = i;
|
||||||
kref_put(&intf->refcount, intf_free);
|
mutex_unlock(&ipmi_interfaces_mutex);
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* Keep memory order straight for RCU readers. Make
|
|
||||||
* sure everything else is committed to memory before
|
|
||||||
* setting intf_num to mark the interface valid.
|
|
||||||
*/
|
|
||||||
smp_wmb();
|
|
||||||
intf->intf_num = i;
|
|
||||||
mutex_unlock(&ipmi_interfaces_mutex);
|
|
||||||
|
|
||||||
/* After this point the interface is legal to use. */
|
/* After this point the interface is legal to use. */
|
||||||
call_smi_watchers(i, intf->si_dev);
|
call_smi_watchers(i, intf->si_dev);
|
||||||
}
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
out_err_bmc_reg:
|
||||||
|
ipmi_bmc_unregister(intf);
|
||||||
|
out_err_started:
|
||||||
|
if (intf->handlers->shutdown)
|
||||||
|
intf->handlers->shutdown(intf->send_info);
|
||||||
|
out_err:
|
||||||
|
list_del_rcu(&intf->link);
|
||||||
|
mutex_unlock(&ipmi_interfaces_mutex);
|
||||||
|
synchronize_srcu(&ipmi_interfaces_srcu);
|
||||||
|
cleanup_srcu_struct(&intf->users_srcu);
|
||||||
|
kref_put(&intf->refcount, intf_free);
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
@@ -3504,7 +3510,8 @@ void ipmi_unregister_smi(struct ipmi_smi *intf)
|
|||||||
}
|
}
|
||||||
srcu_read_unlock(&intf->users_srcu, index);
|
srcu_read_unlock(&intf->users_srcu, index);
|
||||||
|
|
||||||
intf->handlers->shutdown(intf->send_info);
|
if (intf->handlers->shutdown)
|
||||||
|
intf->handlers->shutdown(intf->send_info);
|
||||||
|
|
||||||
cleanup_smi_msgs(intf);
|
cleanup_smi_msgs(intf);
|
||||||
|
|
||||||
|
|||||||
@@ -2083,18 +2083,9 @@ static int try_smi_init(struct smi_info *new_smi)
|
|||||||
si_to_str[new_smi->io.si_type]);
|
si_to_str[new_smi->io.si_type]);
|
||||||
|
|
||||||
WARN_ON(new_smi->io.dev->init_name != NULL);
|
WARN_ON(new_smi->io.dev->init_name != NULL);
|
||||||
|
|
||||||
|
out_err:
|
||||||
kfree(init_name);
|
kfree(init_name);
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
out_err:
|
|
||||||
if (new_smi->intf) {
|
|
||||||
ipmi_unregister_smi(new_smi->intf);
|
|
||||||
new_smi->intf = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
kfree(init_name);
|
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2227,6 +2218,8 @@ static void shutdown_smi(void *send_info)
|
|||||||
|
|
||||||
kfree(smi_info->si_sm);
|
kfree(smi_info->si_sm);
|
||||||
smi_info->si_sm = NULL;
|
smi_info->si_sm = NULL;
|
||||||
|
|
||||||
|
smi_info->intf = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -2240,10 +2233,8 @@ static void cleanup_one_si(struct smi_info *smi_info)
|
|||||||
|
|
||||||
list_del(&smi_info->link);
|
list_del(&smi_info->link);
|
||||||
|
|
||||||
if (smi_info->intf) {
|
if (smi_info->intf)
|
||||||
ipmi_unregister_smi(smi_info->intf);
|
ipmi_unregister_smi(smi_info->intf);
|
||||||
smi_info->intf = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (smi_info->pdev) {
|
if (smi_info->pdev) {
|
||||||
if (smi_info->pdev_registered)
|
if (smi_info->pdev_registered)
|
||||||
|
|||||||
@@ -1214,18 +1214,11 @@ static void shutdown_ssif(void *send_info)
|
|||||||
complete(&ssif_info->wake_thread);
|
complete(&ssif_info->wake_thread);
|
||||||
kthread_stop(ssif_info->thread);
|
kthread_stop(ssif_info->thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* No message can be outstanding now, we have removed the
|
|
||||||
* upper layer and it permitted us to do so.
|
|
||||||
*/
|
|
||||||
kfree(ssif_info);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ssif_remove(struct i2c_client *client)
|
static int ssif_remove(struct i2c_client *client)
|
||||||
{
|
{
|
||||||
struct ssif_info *ssif_info = i2c_get_clientdata(client);
|
struct ssif_info *ssif_info = i2c_get_clientdata(client);
|
||||||
struct ipmi_smi *intf;
|
|
||||||
struct ssif_addr_info *addr_info;
|
struct ssif_addr_info *addr_info;
|
||||||
|
|
||||||
if (!ssif_info)
|
if (!ssif_info)
|
||||||
@@ -1235,9 +1228,7 @@ static int ssif_remove(struct i2c_client *client)
|
|||||||
* After this point, we won't deliver anything asychronously
|
* After this point, we won't deliver anything asychronously
|
||||||
* to the message handler. We can unregister ourself.
|
* to the message handler. We can unregister ourself.
|
||||||
*/
|
*/
|
||||||
intf = ssif_info->intf;
|
ipmi_unregister_smi(ssif_info->intf);
|
||||||
ssif_info->intf = NULL;
|
|
||||||
ipmi_unregister_smi(intf);
|
|
||||||
|
|
||||||
list_for_each_entry(addr_info, &ssif_infos, link) {
|
list_for_each_entry(addr_info, &ssif_infos, link) {
|
||||||
if (addr_info->client == client) {
|
if (addr_info->client == client) {
|
||||||
@@ -1246,6 +1237,8 @@ static int ssif_remove(struct i2c_client *client)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kfree(ssif_info);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user