From a2cb600fa22a877df48e1a9372ac5f02680c1ee3 Mon Sep 17 00:00:00 2001
From: Corey Minyard <cminyard@mvista.com>
Date: Fri, 1 Sep 2017 14:37:07 -0500
Subject: [PATCH] ipmi: Rework BMC registration
Git-commit: a2cb600fa22a877df48e1a9372ac5f02680c1ee3
Patch-mainline: v4.15-rc1
References: FATE#326156

There was a certain error case where the BMC wouldn't be deregistered
like it should be.  Rework the BMC registration to make calling
ipmi_bmc_unregister() ok even if it's not registered and to clean up
the error handling for ipmi_bmc_register().

Signed-off-by: Corey Minyard <cminyard@mvista.com>
Acked-by: Takashi Iwai <tiwai@suse.de>

---
 drivers/char/ipmi/ipmi_msghandler.c | 68 +++++++++++++++++++----------
 1 file changed, 45 insertions(+), 23 deletions(-)

diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
index 42532f296e93..1517f14ee903 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -404,6 +404,7 @@ struct ipmi_smi {
 	wait_queue_head_t waitq;
 
 	struct bmc_device *bmc;
+	bool bmc_registered;
 	char *my_dev_name;
 
 	/*
@@ -2524,17 +2525,18 @@ static void ipmi_bmc_unregister(ipmi_smi_t intf)
 {
 	struct bmc_device *bmc = intf->bmc;
 
+	if (!intf->bmc_registered)
+		return;
+
 	sysfs_remove_link(&intf->si_dev->kobj, "bmc");
-	if (intf->my_dev_name) {
-		sysfs_remove_link(&bmc->pdev.dev.kobj, intf->my_dev_name);
-		kfree(intf->my_dev_name);
-		intf->my_dev_name = NULL;
-	}
+	sysfs_remove_link(&bmc->pdev.dev.kobj, intf->my_dev_name);
+	kfree(intf->my_dev_name);
+	intf->my_dev_name = NULL;
 
 	mutex_lock(&ipmidriver_mutex);
 	kref_put(&bmc->usecount, cleanup_bmc_device);
 	mutex_unlock(&ipmidriver_mutex);
-	intf->bmc = NULL;
+	intf->bmc_registered = false;
 }
 
 static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum)
@@ -2600,7 +2602,9 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum)
 			if (bmc->id.device_id == orig_dev_id) {
 				printk(KERN_ERR PFX
 				       "Out of device ids!\n");
-				break;
+				mutex_unlock(&ipmidriver_mutex);
+				rv = -EAGAIN;
+				goto out;
 			}
 		}
 
@@ -2613,16 +2617,11 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum)
 		rv = platform_device_register(&bmc->pdev);
 		mutex_unlock(&ipmidriver_mutex);
 		if (rv) {
-			put_device(&bmc->pdev.dev);
 			printk(KERN_ERR
 			       "ipmi_msghandler:"
 			       " Unable to register bmc device: %d\n",
 			       rv);
-			/*
-			 * Don't go to out_err, you can only do that if
-			 * the device is registered already.
-			 */
-			return rv;
+			goto out_list_del;
 		}
 
 		dev_info(intf->si_dev, "Found new BMC (man_id: 0x%6.6x, "
@@ -2641,7 +2640,7 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum)
 		printk(KERN_ERR
 		       "ipmi_msghandler: Unable to create bmc symlink: %d\n",
 		       rv);
-		goto out_err;
+		goto out_put_bmc;
 	}
 
 	intf->my_dev_name = kasprintf(GFP_KERNEL, "ipmi%d", ifnum);
@@ -2650,7 +2649,7 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum)
 		printk(KERN_ERR
 		       "ipmi_msghandler: allocate link from BMC: %d\n",
 		       rv);
-		goto out_err;
+		goto out_unlink1;
 	}
 
 	rv = sysfs_create_link(&bmc->pdev.dev.kobj, &intf->si_dev->kobj,
@@ -2662,14 +2661,35 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum)
 		       "ipmi_msghandler:"
 		       " Unable to create symlink to bmc: %d\n",
 		       rv);
-		goto out_err;
+		goto out_free_my_dev_name;
 	}
 
-	return 0;
+	intf->bmc_registered = true;
 
-out_err:
-	ipmi_bmc_unregister(intf);
+out:
 	return rv;
+
+
+out_free_my_dev_name:
+	kfree(intf->my_dev_name);
+	intf->my_dev_name = NULL;
+
+out_unlink1:
+	sysfs_remove_link(&intf->si_dev->kobj, "bmc");
+
+out_put_bmc:
+	mutex_lock(&ipmidriver_mutex);
+	intf->bmc = NULL;
+	kref_put(&bmc->usecount, cleanup_bmc_device);
+	mutex_unlock(&ipmidriver_mutex);
+	goto out;
+
+out_list_del:
+	mutex_lock(&ipmidriver_mutex);
+	intf->bmc = NULL;
+	mutex_unlock(&ipmidriver_mutex);
+	put_device(&bmc->pdev.dev);
+	goto out;
 }
 
 static int
@@ -2959,6 +2979,10 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers,
 
 	get_guid(intf);
 
+	rv = ipmi_bmc_register(intf, i);
+	if (rv)
+		goto out;
+
 	if ((intf->ipmi_version_major > 1)
 			|| ((intf->ipmi_version_major == 1)
 			    && (intf->ipmi_version_minor >= 5))) {
@@ -2987,13 +3011,11 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers,
 		intf->curr_channel = IPMI_MAX_CHANNELS;
 	}
 
-	rv = ipmi_bmc_register(intf, i);
-
-	if (rv == 0)
-		rv = add_proc_entries(intf, i);
+	rv = add_proc_entries(intf, i);
 
  out:
 	if (rv) {
+		ipmi_bmc_unregister(intf);
 		if (intf->proc_dir)
 			remove_proc_entries(intf);
 		intf->handlers = NULL;
-- 
2.19.2

