aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char
diff options
context:
space:
mode:
authorCorey Minyard <cminyard@mvista.com>2017-09-01 12:52:20 -0500
committerCorey Minyard <cminyard@mvista.com>2017-09-27 16:03:45 -0500
commit9ca15af3164f3bb84db101fc7843fde25be3288c (patch)
treefa0225e27009a07def3dcc57818c157a0ad2f4a5 /drivers/char
parentipmi: Check that the device type is BMC when scanning device (diff)
downloadlinux-dev-9ca15af3164f3bb84db101fc7843fde25be3288c.tar.xz
linux-dev-9ca15af3164f3bb84db101fc7843fde25be3288c.zip
ipmi: Fix issues with BMC refcounts
BMC device refcounts were not being decremented after fetching from driver_find_device(). Also, document the use of ipmidriver_mutex and tighten it's span some by incrementing the BMC's usecount in the BMC find routines and not later. This will be important for future changes where a long mutex hold area will complicate things. Signed-off-by: Corey Minyard <cminyard@mvista.com>
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/ipmi/ipmi_msghandler.c51
1 files changed, 35 insertions, 16 deletions
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
index efa5581c2f8b..42532f296e93 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -526,6 +526,11 @@ static struct platform_driver ipmidriver = {
.bus = &platform_bus_type
}
};
+/*
+ * This mutex protects adding/removing BMCs on the ipmidriver's device
+ * list. This way we can pull items out of the driver's list and reuse
+ * them.
+ */
static DEFINE_MUTEX(ipmidriver_mutex);
static LIST_HEAD(ipmi_interfaces);
@@ -2440,16 +2445,23 @@ static int __find_bmc_guid(struct device *dev, void *data)
return memcmp(to_bmc_device(dev)->guid, id, 16) == 0;
}
+/*
+ * Must be called with ipmidriver_mutex held. Returns with the
+ * bmc's usecount incremented, if it is non-NULL.
+ */
static struct bmc_device *ipmi_find_bmc_guid(struct device_driver *drv,
unsigned char *guid)
{
struct device *dev;
+ struct bmc_device *bmc = NULL;
dev = driver_find_device(drv, NULL, guid, __find_bmc_guid);
- if (dev)
- return to_bmc_device(dev);
- else
- return NULL;
+ if (dev) {
+ bmc = to_bmc_device(dev);
+ kref_get(&bmc->usecount);
+ put_device(dev);
+ }
+ return bmc;
}
struct prod_dev_id {
@@ -2470,6 +2482,10 @@ static int __find_bmc_prod_dev_id(struct device *dev, void *data)
&& bmc->id.device_id == id->device_id);
}
+/*
+ * Must be called with ipmidriver_mutex held. Returns with the
+ * bmc's usecount incremented, if it is non-NULL.
+ */
static struct bmc_device *ipmi_find_bmc_prod_dev_id(
struct device_driver *drv,
unsigned int product_id, unsigned char device_id)
@@ -2479,12 +2495,15 @@ static struct bmc_device *ipmi_find_bmc_prod_dev_id(
.device_id = device_id,
};
struct device *dev;
+ struct bmc_device *bmc = NULL;
dev = driver_find_device(drv, NULL, &id, __find_bmc_prod_dev_id);
- if (dev)
- return to_bmc_device(dev);
- else
- return NULL;
+ if (dev) {
+ bmc = to_bmc_device(dev);
+ kref_get(&bmc->usecount);
+ put_device(dev);
+ }
+ return bmc;
}
static void
@@ -2514,8 +2533,8 @@ static void ipmi_bmc_unregister(ipmi_smi_t intf)
mutex_lock(&ipmidriver_mutex);
kref_put(&bmc->usecount, cleanup_bmc_device);
- intf->bmc = NULL;
mutex_unlock(&ipmidriver_mutex);
+ intf->bmc = NULL;
}
static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum)
@@ -2524,18 +2543,18 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum)
struct bmc_device *bmc = intf->bmc;
struct bmc_device *old_bmc;
- mutex_lock(&ipmidriver_mutex);
-
/*
* Try to find if there is an bmc_device struct
* representing the interfaced BMC already
*/
+ mutex_lock(&ipmidriver_mutex);
if (bmc->guid_set)
old_bmc = ipmi_find_bmc_guid(&ipmidriver.driver, bmc->guid);
else
old_bmc = ipmi_find_bmc_prod_dev_id(&ipmidriver.driver,
bmc->id.product_id,
bmc->id.device_id);
+ mutex_unlock(&ipmidriver_mutex);
/*
* If there is already an bmc_device, free the new one,
@@ -2546,9 +2565,6 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum)
intf->bmc = old_bmc;
bmc = old_bmc;
- kref_get(&bmc->usecount);
- mutex_unlock(&ipmidriver_mutex);
-
printk(KERN_INFO
"ipmi: interfacing existing BMC (man_id: 0x%6.6x,"
" prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n",
@@ -2558,14 +2574,17 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum)
} else {
unsigned char orig_dev_id = bmc->id.device_id;
int warn_printed = 0;
+ struct bmc_device *tmp_bmc;
snprintf(bmc->name, sizeof(bmc->name),
"ipmi_bmc.%4.4x", bmc->id.product_id);
bmc->pdev.name = bmc->name;
- while (ipmi_find_bmc_prod_dev_id(&ipmidriver.driver,
+ mutex_lock(&ipmidriver_mutex);
+ while ((tmp_bmc = ipmi_find_bmc_prod_dev_id(&ipmidriver.driver,
bmc->id.product_id,
- bmc->id.device_id)) {
+ bmc->id.device_id))) {
+ kref_put(&tmp_bmc->usecount, cleanup_bmc_device);
if (!warn_printed) {
printk(KERN_WARNING PFX
"This machine has two different BMCs"