From: Corey Minyard <[email protected]>

A BMC's guid or device id info may change dynamically, this could
result in a different configuration that needs to be done.  Adjust
the BMCs dynamically.

Signed-off-by: Corey Minyard <[email protected]>
---
 drivers/char/ipmi/ipmi_msghandler.c | 262 +++++++++++++++++++++++-------------
 1 file changed, 171 insertions(+), 91 deletions(-)

diff --git a/drivers/char/ipmi/ipmi_msghandler.c 
b/drivers/char/ipmi/ipmi_msghandler.c
index 249ca56..467b23a 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -265,19 +265,25 @@ struct ipmi_proc_entry {
 };
 #endif
 
+/*
+ * Note that the product id, manufacturer id, guid, and device id are
+ * immutable in this structure, so dyn_mutex is not required for
+ * accessing those.  If those change on a BMC, a new BMC is allocated.
+ */
 struct bmc_device {
        struct platform_device pdev;
-       struct list_head       intfs;
+       struct list_head       intfs; /* Interfaces on this BMC. */
        struct ipmi_device_id  id;
        struct ipmi_device_id  fetch_id;
        int                    dyn_id_set;
        unsigned long          dyn_id_expiry;
-       struct mutex           dyn_mutex; /* protects id & dyn* fields */
+       struct mutex           dyn_mutex; /* Protects id, intfs, & dyn* */
        u8                     guid[16];
        u8                     fetch_guid[16];
        int                    dyn_guid_set;
        char                   name[16];
        struct kref            usecount;
+       struct work_struct     remove_work;
 };
 #define to_bmc_device(x) container_of((x), struct bmc_device, pdev.dev)
 
@@ -418,6 +424,7 @@ struct ipmi_smi {
        bool bmc_registered;
        struct list_head bmc_link;
        char *my_dev_name;
+       bool in_bmc_register;  /* Handle recursive situations.  Yuck. */
 
        /*
         * This is the lower-layer's sender routine.  Note that you
@@ -504,10 +511,7 @@ struct ipmi_smi {
         * it.  Note that the message will still be freed by the
         * caller.  This only works on the system interface.
         *
-        * The only user outside of initialization an panic handling is
-        * the dynamic device id fetching, so no mutex is currently
-        * required on this.  If more users come along, some sort of
-        * mutex will be required.
+        * Protected by bmc_reg_mutex.
         */
        void (*null_user_handler)(ipmi_smi_t intf, struct ipmi_recv_msg *msg);
 
@@ -536,6 +540,11 @@ struct ipmi_smi {
 #define to_si_intf_from_dev(device) container_of(device, struct ipmi_smi, dev)
 
 static void __get_guid(ipmi_smi_t intf);
+static void __ipmi_bmc_unregister(ipmi_smi_t intf);
+static int __ipmi_bmc_register(ipmi_smi_t intf,
+                              struct ipmi_device_id *id,
+                              bool guid_set, u8 *guid, int intf_num);
+
 
 /**
  * The driver model view of the IPMI messaging driver.
@@ -547,9 +556,7 @@ static struct platform_driver ipmidriver = {
        }
 };
 /*
- * 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.
+ * This mutex keeps us from adding the same BMC twice.
  */
 static DEFINE_MUTEX(ipmidriver_mutex);
 
@@ -2192,12 +2199,13 @@ static int __get_device_id(ipmi_smi_t intf, struct 
bmc_device *bmc)
  * Except for the first time this is called (in ipmi_register_smi()),
  * this will always return good data;
  */
-static int bmc_get_device_id(ipmi_smi_t intf, struct bmc_device *bmc,
-                            struct ipmi_device_id *id,
-                            bool *guid_set, u8 *guid)
+static int __bmc_get_device_id(ipmi_smi_t intf, struct bmc_device *bmc,
+                              struct ipmi_device_id *id,
+                              bool *guid_set, u8 *guid, int intf_num)
 {
        int rv = 0;
        int prev_dyn_id_set, prev_guid_set;
+       bool intf_set = intf != NULL;
 
        if (!intf) {
                mutex_lock(&bmc->dyn_mutex);
@@ -2226,27 +2234,62 @@ static int bmc_get_device_id(ipmi_smi_t intf, struct 
bmc_device *bmc,
        }
 
        /* If we have a valid and current ID, just return that. */
-       if (bmc->dyn_id_set && time_is_after_jiffies(bmc->dyn_id_expiry))
-               goto out;
+       if (intf->in_bmc_register ||
+           (bmc->dyn_id_set && time_is_after_jiffies(bmc->dyn_id_expiry)))
+               goto out_noprocessing;
 
        prev_guid_set = bmc->dyn_guid_set;
        __get_guid(intf);
 
-       if (bmc->dyn_guid_set)
-               memcpy(bmc->guid, bmc->fetch_guid, 16);
-       else if (prev_guid_set)
-               /*
-                * The guid used to be valid and it failed to fetch,
-                * just use the cached value.
-                */
-               bmc->dyn_guid_set = prev_guid_set;
-
        prev_dyn_id_set = bmc->dyn_id_set;
        rv = __get_device_id(intf, bmc);
        if (rv)
                goto out;
 
-       memcpy(&bmc->id, &bmc->fetch_id, sizeof(bmc->id));
+       /*
+        * The guid, device id, manufacturer id, and product id should
+        * not change on a BMC.  If it does we have to do some dancing.
+        */
+       if (!intf->bmc_registered
+           || (!prev_guid_set && bmc->dyn_guid_set)
+           || (!prev_dyn_id_set && bmc->dyn_id_set)
+           || (prev_guid_set && bmc->dyn_guid_set
+               && memcmp(bmc->guid, bmc->fetch_guid, 16))
+           || bmc->id.device_id != bmc->fetch_id.device_id
+           || bmc->id.manufacturer_id != bmc->fetch_id.manufacturer_id
+           || bmc->id.product_id != bmc->fetch_id.product_id) {
+               struct ipmi_device_id id = bmc->fetch_id;
+               int guid_set = bmc->dyn_guid_set;
+               u8 guid[16];
+
+               memcpy(guid, bmc->fetch_guid, 16);
+               mutex_unlock(&bmc->dyn_mutex);
+
+               __ipmi_bmc_unregister(intf);
+               /* Fill in the temporary BMC for good measure. */
+               intf->bmc->id = id;
+               intf->bmc->dyn_guid_set = guid_set;
+               memcpy(intf->bmc->guid, guid, 16);
+               rv = __ipmi_bmc_register(intf, &id, guid_set, guid, intf_num);
+
+               __scan_channels(intf, &id);
+
+               if (!intf_set) {
+                       /*
+                        * We weren't given the interface on the
+                        * command line, so restart the operation on
+                        * the next interface for the BMC.
+                        */
+                       mutex_unlock(&intf->bmc_reg_mutex);
+                       mutex_lock(&bmc->dyn_mutex);
+                       goto retry_bmc_lock;
+               }
+
+               /* We have a new BMC, set it up. */
+               bmc = intf->bmc;
+               mutex_lock(&bmc->dyn_mutex);
+               goto out_noprocessing;
+       }
 
        bmc->dyn_id_expiry = jiffies + IPMI_DYN_DEV_ID_EXPIRY;
 
@@ -2255,15 +2298,28 @@ static int bmc_get_device_id(ipmi_smi_t intf, struct 
bmc_device *bmc,
                rv = 0; /* Ignore failures if we have previous data. */
                bmc->dyn_id_set = prev_dyn_id_set;
        }
+       if (!rv) {
+               bmc->id = bmc->fetch_id;
+               if (bmc->dyn_guid_set)
+                       memcpy(bmc->guid, bmc->fetch_guid, 16);
+               else if (prev_guid_set)
+                       /*
+                        * The guid used to be valid and it failed to fetch,
+                        * just use the cached value.
+                        */
+                       bmc->dyn_guid_set = prev_guid_set;
+       }
+out_noprocessing:
+       if (!rv) {
+               if (id)
+                       *id = bmc->id;
 
-       if (id)
-               *id = bmc->id;
-
-       if (guid_set)
-               *guid_set = bmc->dyn_guid_set;
+               if (guid_set)
+                       *guid_set = bmc->dyn_guid_set;
 
-       if (guid && bmc->dyn_guid_set)
-               memcpy(guid, bmc->guid, 16);
+               if (guid && bmc->dyn_guid_set)
+                       memcpy(guid, bmc->guid, 16);
+       }
 
        mutex_unlock(&bmc->dyn_mutex);
        mutex_unlock(&intf->bmc_reg_mutex);
@@ -2272,6 +2328,13 @@ static int bmc_get_device_id(ipmi_smi_t intf, struct 
bmc_device *bmc,
        return rv;
 }
 
+static int bmc_get_device_id(ipmi_smi_t intf, struct bmc_device *bmc,
+                            struct ipmi_device_id *id,
+                            bool *guid_set, u8 *guid)
+{
+       return __bmc_get_device_id(intf, bmc, id, guid_set, guid, -1);
+}
+
 #ifdef CONFIG_PROC_FS
 static int smi_ipmb_proc_show(struct seq_file *m, void *v)
 {
@@ -2713,26 +2776,22 @@ static const struct device_type bmc_device_type = {
 
 static int __find_bmc_guid(struct device *dev, void *data)
 {
-       unsigned char *id = data;
+       unsigned char *guid = data;
        struct bmc_device *bmc;
-       bool guid_set;
-       u8 guid[16];
        int rv;
 
        if (dev->type != &bmc_device_type)
                return 0;
 
        bmc = to_bmc_device(dev);
-       rv = bmc_get_device_id(NULL, bmc, NULL, &guid_set, guid);
-       if (rv || !guid_set)
-               return 0;
-
-       return memcmp(guid, id, 16) == 0;
+       rv = bmc->dyn_guid_set && memcmp(bmc->guid, guid, 16) == 0;
+       if (rv)
+               rv = kref_get_unless_zero(&bmc->usecount);
+       return rv;
 }
 
 /*
- * Must be called with ipmidriver_mutex held.  Returns with the
- * bmc's usecount incremented, if it is non-NULL.
+ * 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)
@@ -2743,7 +2802,6 @@ static struct bmc_device *ipmi_find_bmc_guid(struct 
device_driver *drv,
        dev = driver_find_device(drv, NULL, guid, __find_bmc_guid);
        if (dev) {
                bmc = to_bmc_device(dev);
-               kref_get(&bmc->usecount);
                put_device(dev);
        }
        return bmc;
@@ -2758,24 +2816,21 @@ static int __find_bmc_prod_dev_id(struct device *dev, 
void *data)
 {
        struct prod_dev_id *cid = data;
        struct bmc_device *bmc;
-       struct ipmi_device_id id;
        int rv;
 
        if (dev->type != &bmc_device_type)
                return 0;
 
        bmc = to_bmc_device(dev);
-       rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL);
+       rv = (bmc->id.product_id == cid->product_id
+             && bmc->id.device_id == cid->device_id);
        if (rv)
-               return 0;
-
-       return (id.product_id == cid->product_id
-               && id.device_id == cid->device_id);
+               rv = kref_get_unless_zero(&bmc->usecount);
+       return rv;
 }
 
 /*
- * Must be called with ipmidriver_mutex held.  Returns with the
- * bmc's usecount incremented, if it is non-NULL.
+ * 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,
@@ -2791,7 +2846,6 @@ static struct bmc_device *ipmi_find_bmc_prod_dev_id(
        dev = driver_find_device(drv, NULL, &id, __find_bmc_prod_dev_id);
        if (dev) {
                bmc = to_bmc_device(dev);
-               kref_get(&bmc->usecount);
                put_device(dev);
        }
        return bmc;
@@ -2803,23 +2857,37 @@ release_bmc_device(struct device *dev)
        kfree(to_bmc_device(dev));
 }
 
+static void cleanup_bmc_work(struct work_struct *work)
+{
+       struct bmc_device *bmc = container_of(work, struct bmc_device,
+                                             remove_work);
+
+       platform_device_unregister(&bmc->pdev);
+}
+
 static void
 cleanup_bmc_device(struct kref *ref)
 {
        struct bmc_device *bmc = container_of(ref, struct bmc_device, usecount);
 
-       platform_device_unregister(&bmc->pdev);
+       /*
+        * Remove the platform device in a work queue to avoid issues
+        * with removing the device attributes while reading a device
+        * attribute.
+        */
+       schedule_work(&bmc->remove_work);
 }
 
-static void ipmi_bmc_unregister(ipmi_smi_t intf)
+/*
+ * Must be called with intf->bmc_reg_mutex held.
+ */
+static void __ipmi_bmc_unregister(ipmi_smi_t intf)
 {
        struct bmc_device *bmc = intf->bmc;
 
        if (!intf->bmc_registered)
                return;
 
-       mutex_lock(&intf->bmc_reg_mutex);
-
        sysfs_remove_link(&intf->si_dev->kobj, "bmc");
        sysfs_remove_link(&bmc->pdev.dev.kobj, intf->my_dev_name);
        kfree(intf->my_dev_name);
@@ -2829,49 +2897,63 @@ static void ipmi_bmc_unregister(ipmi_smi_t intf)
        list_del(&intf->bmc_link);
        mutex_unlock(&bmc->dyn_mutex);
        intf->bmc = &intf->tmp_bmc;
-       mutex_lock(&ipmidriver_mutex);
        kref_put(&bmc->usecount, cleanup_bmc_device);
-       mutex_unlock(&ipmidriver_mutex);
        intf->bmc_registered = false;
+}
 
+static void ipmi_bmc_unregister(ipmi_smi_t intf)
+{
+       mutex_lock(&intf->bmc_reg_mutex);
+       __ipmi_bmc_unregister(intf);
        mutex_unlock(&intf->bmc_reg_mutex);
 }
 
-static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum)
+/*
+ * Must be called with intf->bmc_reg_mutex held.
+ */
+static int __ipmi_bmc_register(ipmi_smi_t intf,
+                              struct ipmi_device_id *id,
+                              bool guid_set, u8 *guid, int intf_num)
 {
        int               rv;
        struct bmc_device *bmc = intf->bmc;
        struct bmc_device *old_bmc;
 
        /*
-        * Prevent ipmi_bmc_unregister() from running on this
-        * intf until we are done here.
+        * platform_device_register() can cause bmc_reg_mutex to
+        * be claimed because of the is_visible functions of
+        * the attributes.  Eliminate possible recursion and
+        * release the lock.
         */
-       mutex_lock(&intf->bmc_reg_mutex);
+       intf->in_bmc_register = true;
+       mutex_unlock(&intf->bmc_reg_mutex);
 
        /*
         * Try to find if there is an bmc_device struct
         * representing the interfaced BMC already
         */
        mutex_lock(&ipmidriver_mutex);
-       if (bmc->dyn_guid_set)
-               old_bmc = ipmi_find_bmc_guid(&ipmidriver.driver, bmc->guid);
+       if (guid_set)
+               old_bmc = ipmi_find_bmc_guid(&ipmidriver.driver, guid);
        else
                old_bmc = ipmi_find_bmc_prod_dev_id(&ipmidriver.driver,
-                                                   bmc->id.product_id,
-                                                   bmc->id.device_id);
-       mutex_unlock(&ipmidriver_mutex);
+                                                   id->product_id,
+                                                   id->device_id);
 
        /*
         * If there is already an bmc_device, free the new one,
         * otherwise register the new BMC device
         */
        if (old_bmc) {
+               /*
+                * Note: old_bmc already has usecount incremented by
+                * the BMC find functions.
+                */
                intf->bmc = old_bmc;
+               bmc = old_bmc;
                mutex_lock(&bmc->dyn_mutex);
                list_add_tail(&intf->bmc_link, &bmc->intfs);
                mutex_unlock(&bmc->dyn_mutex);
-               bmc = old_bmc;
 
                printk(KERN_INFO
                       "ipmi: interfacing existing BMC (man_id: 0x%6.6x,"
@@ -2880,7 +2962,7 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum)
                       bmc->id.product_id,
                       bmc->id.device_id);
        } else {
-               unsigned char orig_dev_id = bmc->id.device_id;
+               u8 working_dev_id = id->device_id;
                int warn_printed = 0;
                struct bmc_device *tmp_bmc;
 
@@ -2891,15 +2973,21 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum)
                }
                INIT_LIST_HEAD(&bmc->intfs);
                mutex_init(&bmc->dyn_mutex);
+               INIT_WORK(&bmc->remove_work, cleanup_bmc_work);
+
+               bmc->id = *id;
+               bmc->dyn_id_set = 1;
+               bmc->dyn_guid_set = guid_set;
+               memcpy(bmc->guid, guid, 16);
+               bmc->dyn_id_expiry = jiffies + IPMI_DYN_DEV_ID_EXPIRY;
 
                snprintf(bmc->name, sizeof(bmc->name),
                         "ipmi_bmc.%4.4x", bmc->id.product_id);
                bmc->pdev.name = bmc->name;
 
-               mutex_lock(&ipmidriver_mutex);
                while ((tmp_bmc = ipmi_find_bmc_prod_dev_id(&ipmidriver.driver,
-                                                bmc->id.product_id,
-                                                bmc->id.device_id))) {
+                                                id->product_id,
+                                                id->device_id))) {
                        kref_put(&tmp_bmc->usecount, cleanup_bmc_device);
                        if (!warn_printed) {
                                printk(KERN_WARNING PFX
@@ -2912,19 +3000,18 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum)
                                       bmc->id.product_id, bmc->id.device_id);
                                warn_printed = 1;
                        }
-                       bmc->id.device_id++; /* Wraps at 255 */
-                       if (bmc->id.device_id == orig_dev_id) {
+                       working_dev_id++; /* Wraps at 255 */
+                       if (bmc->id.device_id == working_dev_id) {
                                printk(KERN_ERR PFX
                                       "Out of device ids!\n");
                                kfree(bmc);
-                               mutex_unlock(&ipmidriver_mutex);
                                rv = -EAGAIN;
                                goto out;
                        }
                }
 
                bmc->pdev.dev.driver = &ipmidriver.driver;
-               bmc->pdev.id = bmc->id.device_id;
+               bmc->pdev.id = working_dev_id;
                bmc->pdev.dev.release = release_bmc_device;
                bmc->pdev.dev.type = &bmc_device_type;
                kref_init(&bmc->usecount);
@@ -2934,14 +3021,7 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum)
                mutex_unlock(&bmc->dyn_mutex);
                intf->bmc = bmc;
 
-               /*
-                * platform_device_register can cause bmc_reg_mutex
-                * to be claimed.
-                */
-               mutex_unlock(&intf->bmc_reg_mutex);
                rv = platform_device_register(&bmc->pdev);
-               mutex_unlock(&ipmidriver_mutex);
-               mutex_lock(&intf->bmc_reg_mutex);
                if (rv) {
                        printk(KERN_ERR
                               "ipmi_msghandler:"
@@ -2969,7 +3049,9 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum)
                goto out_put_bmc;
        }
 
-       intf->my_dev_name = kasprintf(GFP_KERNEL, "ipmi%d", ifnum);
+       if (intf_num == -1)
+               intf_num = intf->intf_num;
+       intf->my_dev_name = kasprintf(GFP_KERNEL, "ipmi%d", intf_num);
        if (!intf->my_dev_name) {
                rv = -ENOMEM;
                printk(KERN_ERR
@@ -2993,7 +3075,9 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum)
        intf->bmc_registered = true;
 
 out:
-       mutex_unlock(&intf->bmc_reg_mutex);
+       mutex_unlock(&ipmidriver_mutex);
+       mutex_lock(&intf->bmc_reg_mutex);
+       intf->in_bmc_register = false;
        return rv;
 
 
@@ -3009,9 +3093,7 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum)
        list_del(&intf->bmc_link);
        mutex_unlock(&bmc->dyn_mutex);
        intf->bmc = &intf->tmp_bmc;
-       mutex_lock(&ipmidriver_mutex);
        kref_put(&bmc->usecount, cleanup_bmc_device);
-       mutex_unlock(&ipmidriver_mutex);
        goto out;
 
 out_list_del:
@@ -3315,16 +3397,12 @@ int ipmi_register_smi(const struct ipmi_smi_handlers 
*handlers,
        if (rv)
                goto out;
 
-       rv = bmc_get_device_id(intf, NULL, &id, NULL, NULL);
+       rv = __bmc_get_device_id(intf, NULL, &id, NULL, NULL, i);
        if (rv) {
                dev_err(si_dev, "Unable to get the device id: %d\n", rv);
                goto out;
        }
 
-       rv = ipmi_bmc_register(intf, i);
-       if (rv)
-               goto out;
-
        if (ipmi_version_major(&id) > 1
                        || (ipmi_version_major(&id) == 1
                            && ipmi_version_minor(&id) >= 5)) {
@@ -3332,6 +3410,7 @@ int ipmi_register_smi(const struct ipmi_smi_handlers 
*handlers,
                 * Start scanning the channels to see what is
                 * available.
                 */
+               mutex_lock(&intf->bmc_reg_mutex);
                intf->null_user_handler = channel_handler;
                intf->curr_channel = 0;
                rv = send_channel_info_cmd(intf, 0);
@@ -3346,6 +3425,7 @@ int ipmi_register_smi(const struct ipmi_smi_handlers 
*handlers,
                wait_event(intf->waitq,
                           intf->curr_channel >= IPMI_MAX_CHANNELS);
                intf->null_user_handler = NULL;
+               mutex_unlock(&intf->bmc_reg_mutex);
        } else {
                /* Assume a single IPMB channel at zero. */
                intf->channels[0].medium = IPMI_CHANNEL_MEDIUM_IPMB;
-- 
2.7.4


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Openipmi-developer mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openipmi-developer

Reply via email to