diff options
author | Corey Minyard <cminyard@mvista.com> | 2018-08-22 13:08:13 -0400 |
---|---|---|
committer | Corey Minyard <cminyard@mvista.com> | 2018-08-31 09:42:29 -0400 |
commit | 2512e40e48d21d8bac09f7e91d2c3ceb2d3b50b2 (patch) | |
tree | d3f88ae5767b53c41a4a9d8c4089dc98ac956dd7 | |
parent | cd2315d471f45a36cb1329722920d89cd6d3d11f (diff) |
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
-rw-r--r-- | drivers/char/ipmi/ipmi_msghandler.c | 53 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_si_intf.c | 17 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_ssif.c | 13 |
3 files changed, 37 insertions, 46 deletions
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 51832b8a2c62..7fc9612070a1 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c | |||
@@ -3381,39 +3381,45 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, | |||
3381 | 3381 | ||
3382 | rv = handlers->start_processing(send_info, intf); | 3382 | rv = handlers->start_processing(send_info, intf); |
3383 | if (rv) | 3383 | if (rv) |
3384 | goto out; | 3384 | goto out_err; |
3385 | 3385 | ||
3386 | rv = __bmc_get_device_id(intf, NULL, &id, NULL, NULL, i); | 3386 | rv = __bmc_get_device_id(intf, NULL, &id, NULL, NULL, i); |
3387 | if (rv) { | 3387 | if (rv) { |
3388 | dev_err(si_dev, "Unable to get the device id: %d\n", rv); | 3388 | dev_err(si_dev, "Unable to get the device id: %d\n", rv); |
3389 | goto out; | 3389 | goto out_err_started; |
3390 | } | 3390 | } |
3391 | 3391 | ||
3392 | mutex_lock(&intf->bmc_reg_mutex); | 3392 | mutex_lock(&intf->bmc_reg_mutex); |
3393 | rv = __scan_channels(intf, &id); | 3393 | rv = __scan_channels(intf, &id); |
3394 | mutex_unlock(&intf->bmc_reg_mutex); | 3394 | mutex_unlock(&intf->bmc_reg_mutex); |
3395 | if (rv) | ||
3396 | goto out_err_bmc_reg; | ||
3395 | 3397 | ||
3396 | out: | 3398 | /* |
3397 | if (rv) { | 3399 | * Keep memory order straight for RCU readers. Make |
3398 | ipmi_bmc_unregister(intf); | 3400 | * sure everything else is committed to memory before |
3399 | list_del_rcu(&intf->link); | 3401 | * setting intf_num to mark the interface valid. |
3400 | mutex_unlock(&ipmi_interfaces_mutex); | 3402 | */ |
3401 | synchronize_srcu(&ipmi_interfaces_srcu); | 3403 | smp_wmb(); |
3402 | cleanup_srcu_struct(&intf->users_srcu); | 3404 | intf->intf_num = i; |
3403 | kref_put(&intf->refcount, intf_free); | 3405 | mutex_unlock(&ipmi_interfaces_mutex); |
3404 | } else { | ||
3405 | /* | ||
3406 | * Keep memory order straight for RCU readers. Make | ||
3407 | * sure everything else is committed to memory before | ||
3408 | * setting intf_num to mark the interface valid. | ||
3409 | */ | ||
3410 | smp_wmb(); | ||
3411 | intf->intf_num = i; | ||
3412 | mutex_unlock(&ipmi_interfaces_mutex); | ||
3413 | 3406 | ||
3414 | /* After this point the interface is legal to use. */ | 3407 | /* After this point the interface is legal to use. */ |
3415 | call_smi_watchers(i, intf->si_dev); | 3408 | call_smi_watchers(i, intf->si_dev); |
3416 | } | 3409 | |
3410 | return 0; | ||
3411 | |||
3412 | out_err_bmc_reg: | ||
3413 | ipmi_bmc_unregister(intf); | ||
3414 | out_err_started: | ||
3415 | if (intf->handlers->shutdown) | ||
3416 | intf->handlers->shutdown(intf->send_info); | ||
3417 | out_err: | ||
3418 | list_del_rcu(&intf->link); | ||
3419 | mutex_unlock(&ipmi_interfaces_mutex); | ||
3420 | synchronize_srcu(&ipmi_interfaces_srcu); | ||
3421 | cleanup_srcu_struct(&intf->users_srcu); | ||
3422 | kref_put(&intf->refcount, intf_free); | ||
3417 | 3423 | ||
3418 | return rv; | 3424 | return rv; |
3419 | } | 3425 | } |
@@ -3504,7 +3510,8 @@ void ipmi_unregister_smi(struct ipmi_smi *intf) | |||
3504 | } | 3510 | } |
3505 | srcu_read_unlock(&intf->users_srcu, index); | 3511 | srcu_read_unlock(&intf->users_srcu, index); |
3506 | 3512 | ||
3507 | intf->handlers->shutdown(intf->send_info); | 3513 | if (intf->handlers->shutdown) |
3514 | intf->handlers->shutdown(intf->send_info); | ||
3508 | 3515 | ||
3509 | cleanup_smi_msgs(intf); | 3516 | cleanup_smi_msgs(intf); |
3510 | 3517 | ||
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 90ec010bffbd..5faa917df1b6 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c | |||
@@ -2083,18 +2083,9 @@ static int try_smi_init(struct smi_info *new_smi) | |||
2083 | si_to_str[new_smi->io.si_type]); | 2083 | si_to_str[new_smi->io.si_type]); |
2084 | 2084 | ||
2085 | WARN_ON(new_smi->io.dev->init_name != NULL); | 2085 | WARN_ON(new_smi->io.dev->init_name != NULL); |
2086 | kfree(init_name); | ||
2087 | |||
2088 | return 0; | ||
2089 | |||
2090 | out_err: | ||
2091 | if (new_smi->intf) { | ||
2092 | ipmi_unregister_smi(new_smi->intf); | ||
2093 | new_smi->intf = NULL; | ||
2094 | } | ||
2095 | 2086 | ||
2087 | out_err: | ||
2096 | kfree(init_name); | 2088 | kfree(init_name); |
2097 | |||
2098 | return rv; | 2089 | return rv; |
2099 | } | 2090 | } |
2100 | 2091 | ||
@@ -2227,6 +2218,8 @@ static void shutdown_smi(void *send_info) | |||
2227 | 2218 | ||
2228 | kfree(smi_info->si_sm); | 2219 | kfree(smi_info->si_sm); |
2229 | smi_info->si_sm = NULL; | 2220 | smi_info->si_sm = NULL; |
2221 | |||
2222 | smi_info->intf = NULL; | ||
2230 | } | 2223 | } |
2231 | 2224 | ||
2232 | /* | 2225 | /* |
@@ -2240,10 +2233,8 @@ static void cleanup_one_si(struct smi_info *smi_info) | |||
2240 | 2233 | ||
2241 | list_del(&smi_info->link); | 2234 | list_del(&smi_info->link); |
2242 | 2235 | ||
2243 | if (smi_info->intf) { | 2236 | if (smi_info->intf) |
2244 | ipmi_unregister_smi(smi_info->intf); | 2237 | ipmi_unregister_smi(smi_info->intf); |
2245 | smi_info->intf = NULL; | ||
2246 | } | ||
2247 | 2238 | ||
2248 | if (smi_info->pdev) { | 2239 | if (smi_info->pdev) { |
2249 | if (smi_info->pdev_registered) | 2240 | if (smi_info->pdev_registered) |
diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index 18e4650c233b..c12edc8e91df 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c | |||
@@ -1214,18 +1214,11 @@ static void shutdown_ssif(void *send_info) | |||
1214 | complete(&ssif_info->wake_thread); | 1214 | complete(&ssif_info->wake_thread); |
1215 | kthread_stop(ssif_info->thread); | 1215 | kthread_stop(ssif_info->thread); |
1216 | } | 1216 | } |
1217 | |||
1218 | /* | ||
1219 | * No message can be outstanding now, we have removed the | ||
1220 | * upper layer and it permitted us to do so. | ||
1221 | */ | ||
1222 | kfree(ssif_info); | ||
1223 | } | 1217 | } |
1224 | 1218 | ||
1225 | static int ssif_remove(struct i2c_client *client) | 1219 | static int ssif_remove(struct i2c_client *client) |
1226 | { | 1220 | { |
1227 | struct ssif_info *ssif_info = i2c_get_clientdata(client); | 1221 | struct ssif_info *ssif_info = i2c_get_clientdata(client); |
1228 | struct ipmi_smi *intf; | ||
1229 | struct ssif_addr_info *addr_info; | 1222 | struct ssif_addr_info *addr_info; |
1230 | 1223 | ||
1231 | if (!ssif_info) | 1224 | if (!ssif_info) |
@@ -1235,9 +1228,7 @@ static int ssif_remove(struct i2c_client *client) | |||
1235 | * After this point, we won't deliver anything asychronously | 1228 | * After this point, we won't deliver anything asychronously |
1236 | * to the message handler. We can unregister ourself. | 1229 | * to the message handler. We can unregister ourself. |
1237 | */ | 1230 | */ |
1238 | intf = ssif_info->intf; | 1231 | ipmi_unregister_smi(ssif_info->intf); |
1239 | ssif_info->intf = NULL; | ||
1240 | ipmi_unregister_smi(intf); | ||
1241 | 1232 | ||
1242 | list_for_each_entry(addr_info, &ssif_infos, link) { | 1233 | list_for_each_entry(addr_info, &ssif_infos, link) { |
1243 | if (addr_info->client == client) { | 1234 | if (addr_info->client == client) { |
@@ -1246,6 +1237,8 @@ static int ssif_remove(struct i2c_client *client) | |||
1246 | } | 1237 | } |
1247 | } | 1238 | } |
1248 | 1239 | ||
1240 | kfree(ssif_info); | ||
1241 | |||
1249 | return 0; | 1242 | return 0; |
1250 | } | 1243 | } |
1251 | 1244 | ||