]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/char/ipmi/ipmi_msghandler.c
Merge master.kernel.org:/pub/scm/linux/kernel/git/lethal/sh-2.6
[mv-sheeva.git] / drivers / char / ipmi / ipmi_msghandler.c
index 83ed6ae466a56cd5549b07dba914ba426fe86c3e..34a4fd13fa817ec0241a05157593872716b49600 100644 (file)
@@ -31,7 +31,6 @@
  *  675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/errno.h>
 #include <asm/system.h>
@@ -97,6 +96,7 @@ struct cmd_rcvr
        ipmi_user_t   user;
        unsigned char netfn;
        unsigned char cmd;
+       unsigned int  chans;
 
        /*
         * This is used to form a linked lised during mass deletion.
@@ -954,24 +954,41 @@ int ipmi_set_gets_events(ipmi_user_t user, int val)
 
 static struct cmd_rcvr *find_cmd_rcvr(ipmi_smi_t    intf,
                                      unsigned char netfn,
-                                     unsigned char cmd)
+                                     unsigned char cmd,
+                                     unsigned char chan)
 {
        struct cmd_rcvr *rcvr;
 
        list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) {
-               if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd))
+               if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd)
+                                       && (rcvr->chans & (1 << chan)))
                        return rcvr;
        }
        return NULL;
 }
 
+static int is_cmd_rcvr_exclusive(ipmi_smi_t    intf,
+                                unsigned char netfn,
+                                unsigned char cmd,
+                                unsigned int  chans)
+{
+       struct cmd_rcvr *rcvr;
+
+       list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) {
+               if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd)
+                                       && (rcvr->chans & chans))
+                       return 0;
+       }
+       return 1;
+}
+
 int ipmi_register_for_cmd(ipmi_user_t   user,
                          unsigned char netfn,
-                         unsigned char cmd)
+                         unsigned char cmd,
+                         unsigned int  chans)
 {
        ipmi_smi_t      intf = user->intf;
        struct cmd_rcvr *rcvr;
-       struct cmd_rcvr *entry;
        int             rv = 0;
 
 
@@ -980,12 +997,12 @@ int ipmi_register_for_cmd(ipmi_user_t   user,
                return -ENOMEM;
        rcvr->cmd = cmd;
        rcvr->netfn = netfn;
+       rcvr->chans = chans;
        rcvr->user = user;
 
        mutex_lock(&intf->cmd_rcvrs_mutex);
        /* Make sure the command/netfn is not already registered. */
-       entry = find_cmd_rcvr(intf, netfn, cmd);
-       if (entry) {
+       if (!is_cmd_rcvr_exclusive(intf, netfn, cmd, chans)) {
                rv = -EBUSY;
                goto out_unlock;
        }
@@ -1002,24 +1019,39 @@ int ipmi_register_for_cmd(ipmi_user_t   user,
 
 int ipmi_unregister_for_cmd(ipmi_user_t   user,
                            unsigned char netfn,
-                           unsigned char cmd)
+                           unsigned char cmd,
+                           unsigned int  chans)
 {
        ipmi_smi_t      intf = user->intf;
        struct cmd_rcvr *rcvr;
+       struct cmd_rcvr *rcvrs = NULL;
+       int i, rv = -ENOENT;
 
        mutex_lock(&intf->cmd_rcvrs_mutex);
-       /* Make sure the command/netfn is not already registered. */
-       rcvr = find_cmd_rcvr(intf, netfn, cmd);
-       if ((rcvr) && (rcvr->user == user)) {
-               list_del_rcu(&rcvr->link);
-               mutex_unlock(&intf->cmd_rcvrs_mutex);
-               synchronize_rcu();
+       for (i = 0; i < IPMI_NUM_CHANNELS; i++) {
+               if (((1 << i) & chans) == 0)
+                       continue;
+               rcvr = find_cmd_rcvr(intf, netfn, cmd, i);
+               if (rcvr == NULL)
+                       continue;
+               if (rcvr->user == user) {
+                       rv = 0;
+                       rcvr->chans &= ~chans;
+                       if (rcvr->chans == 0) {
+                               list_del_rcu(&rcvr->link);
+                               rcvr->next = rcvrs;
+                               rcvrs = rcvr;
+                       }
+               }
+       }
+       mutex_unlock(&intf->cmd_rcvrs_mutex);
+       synchronize_rcu();
+       while (rcvrs) {
+               rcvr = rcvrs;
+               rcvrs = rcvr->next;
                kfree(rcvr);
-               return 0;
-       } else {
-               mutex_unlock(&intf->cmd_rcvrs_mutex);
-               return -ENOENT;
        }
+       return rv;
 }
 
 void ipmi_user_set_run_to_completion(ipmi_user_t user, int val)
@@ -1896,13 +1928,8 @@ static ssize_t guid_show(struct device *dev, struct device_attribute *attr,
                        (long long) bmc->guid[8]);
 }
 
-static void
-cleanup_bmc_device(struct kref *ref)
+static void remove_files(struct bmc_device *bmc)
 {
-       struct bmc_device *bmc;
-
-       bmc = container_of(ref, struct bmc_device, refcount);
-
        device_remove_file(&bmc->dev->dev,
                           &bmc->device_id_attr);
        device_remove_file(&bmc->dev->dev,
@@ -1919,12 +1946,23 @@ cleanup_bmc_device(struct kref *ref)
                           &bmc->manufacturer_id_attr);
        device_remove_file(&bmc->dev->dev,
                           &bmc->product_id_attr);
+
        if (bmc->id.aux_firmware_revision_set)
                device_remove_file(&bmc->dev->dev,
                                   &bmc->aux_firmware_rev_attr);
        if (bmc->guid_set)
                device_remove_file(&bmc->dev->dev,
                                   &bmc->guid_attr);
+}
+
+static void
+cleanup_bmc_device(struct kref *ref)
+{
+       struct bmc_device *bmc;
+
+       bmc = container_of(ref, struct bmc_device, refcount);
+
+       remove_files(bmc);
        platform_device_unregister(bmc->dev);
        kfree(bmc);
 }
@@ -1945,6 +1983,79 @@ static void ipmi_bmc_unregister(ipmi_smi_t intf)
        mutex_unlock(&ipmidriver_mutex);
 }
 
+static int create_files(struct bmc_device *bmc)
+{
+       int err;
+
+       err = device_create_file(&bmc->dev->dev,
+                          &bmc->device_id_attr);
+       if (err) goto out;
+       err = device_create_file(&bmc->dev->dev,
+                          &bmc->provides_dev_sdrs_attr);
+       if (err) goto out_devid;
+       err = device_create_file(&bmc->dev->dev,
+                          &bmc->revision_attr);
+       if (err) goto out_sdrs;
+       err = device_create_file(&bmc->dev->dev,
+                          &bmc->firmware_rev_attr);
+       if (err) goto out_rev;
+       err = device_create_file(&bmc->dev->dev,
+                          &bmc->version_attr);
+       if (err) goto out_firm;
+       err = device_create_file(&bmc->dev->dev,
+                          &bmc->add_dev_support_attr);
+       if (err) goto out_version;
+       err = device_create_file(&bmc->dev->dev,
+                          &bmc->manufacturer_id_attr);
+       if (err) goto out_add_dev;
+       err = device_create_file(&bmc->dev->dev,
+                          &bmc->product_id_attr);
+       if (err) goto out_manu;
+       if (bmc->id.aux_firmware_revision_set) {
+               err = device_create_file(&bmc->dev->dev,
+                                  &bmc->aux_firmware_rev_attr);
+               if (err) goto out_prod_id;
+       }
+       if (bmc->guid_set) {
+               err = device_create_file(&bmc->dev->dev,
+                                  &bmc->guid_attr);
+               if (err) goto out_aux_firm;
+       }
+
+       return 0;
+
+out_aux_firm:
+       if (bmc->id.aux_firmware_revision_set)
+               device_remove_file(&bmc->dev->dev,
+                                  &bmc->aux_firmware_rev_attr);
+out_prod_id:
+       device_remove_file(&bmc->dev->dev,
+                          &bmc->product_id_attr);
+out_manu:
+       device_remove_file(&bmc->dev->dev,
+                          &bmc->manufacturer_id_attr);
+out_add_dev:
+       device_remove_file(&bmc->dev->dev,
+                          &bmc->add_dev_support_attr);
+out_version:
+       device_remove_file(&bmc->dev->dev,
+                          &bmc->version_attr);
+out_firm:
+       device_remove_file(&bmc->dev->dev,
+                          &bmc->firmware_rev_attr);
+out_rev:
+       device_remove_file(&bmc->dev->dev,
+                          &bmc->revision_attr);
+out_sdrs:
+       device_remove_file(&bmc->dev->dev,
+                          &bmc->provides_dev_sdrs_attr);
+out_devid:
+       device_remove_file(&bmc->dev->dev,
+                          &bmc->device_id_attr);
+out:
+       return err;
+}
+
 static int ipmi_bmc_register(ipmi_smi_t intf)
 {
        int               rv;
@@ -2019,7 +2130,6 @@ static int ipmi_bmc_register(ipmi_smi_t intf)
                bmc->provides_dev_sdrs_attr.attr.mode = S_IRUGO;
                bmc->provides_dev_sdrs_attr.show = provides_dev_sdrs_show;
 
-
                bmc->revision_attr.attr.name = "revision";
                bmc->revision_attr.attr.owner = THIS_MODULE;
                bmc->revision_attr.attr.mode = S_IRUGO;
@@ -2061,28 +2171,14 @@ static int ipmi_bmc_register(ipmi_smi_t intf)
                bmc->aux_firmware_rev_attr.attr.mode = S_IRUGO;
                bmc->aux_firmware_rev_attr.show = aux_firmware_rev_show;
 
-               device_create_file(&bmc->dev->dev,
-                                  &bmc->device_id_attr);
-               device_create_file(&bmc->dev->dev,
-                                  &bmc->provides_dev_sdrs_attr);
-               device_create_file(&bmc->dev->dev,
-                                  &bmc->revision_attr);
-               device_create_file(&bmc->dev->dev,
-                                  &bmc->firmware_rev_attr);
-               device_create_file(&bmc->dev->dev,
-                                  &bmc->version_attr);
-               device_create_file(&bmc->dev->dev,
-                                  &bmc->add_dev_support_attr);
-               device_create_file(&bmc->dev->dev,
-                                  &bmc->manufacturer_id_attr);
-               device_create_file(&bmc->dev->dev,
-                                  &bmc->product_id_attr);
-               if (bmc->id.aux_firmware_revision_set)
-                       device_create_file(&bmc->dev->dev,
-                                          &bmc->aux_firmware_rev_attr);
-               if (bmc->guid_set)
-                       device_create_file(&bmc->dev->dev,
-                                          &bmc->guid_attr);
+               rv = create_files(bmc);
+               if (rv) {
+                       mutex_lock(&ipmidriver_mutex);
+                       platform_device_unregister(bmc->dev);
+                       mutex_unlock(&ipmidriver_mutex);
+
+                       return rv;
+               }
 
                printk(KERN_INFO
                       "ipmi: Found new BMC (man_id: 0x%6.6x, "
@@ -2549,6 +2645,7 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t          intf,
        int                      rv = 0;
        unsigned char            netfn;
        unsigned char            cmd;
+       unsigned char            chan;
        ipmi_user_t              user = NULL;
        struct ipmi_ipmb_addr    *ipmb_addr;
        struct ipmi_recv_msg     *recv_msg;
@@ -2569,9 +2666,10 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t          intf,
 
        netfn = msg->rsp[4] >> 2;
        cmd = msg->rsp[8];
+       chan = msg->rsp[3] & 0xf;
 
        rcu_read_lock();
-       rcvr = find_cmd_rcvr(intf, netfn, cmd);
+       rcvr = find_cmd_rcvr(intf, netfn, cmd, chan);
        if (rcvr) {
                user = rcvr->user;
                kref_get(&user->refcount);
@@ -2729,6 +2827,7 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t          intf,
        int                      rv = 0;
        unsigned char            netfn;
        unsigned char            cmd;
+       unsigned char            chan;
        ipmi_user_t              user = NULL;
        struct ipmi_lan_addr     *lan_addr;
        struct ipmi_recv_msg     *recv_msg;
@@ -2749,9 +2848,10 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t          intf,
 
        netfn = msg->rsp[6] >> 2;
        cmd = msg->rsp[10];
+       chan = msg->rsp[3] & 0xf;
 
        rcu_read_lock();
-       rcvr = find_cmd_rcvr(intf, netfn, cmd);
+       rcvr = find_cmd_rcvr(intf, netfn, cmd, chan);
        if (rcvr) {
                user = rcvr->user;
                kref_get(&user->refcount);
@@ -3429,6 +3529,7 @@ struct ipmi_recv_msg *ipmi_alloc_recv_msg(void)
 
        rv = kmalloc(sizeof(struct ipmi_recv_msg), GFP_ATOMIC);
        if (rv) {
+               rv->user = NULL;
                rv->done = free_recv_msg;
                atomic_inc(&recv_msg_inuse_count);
        }
@@ -3738,11 +3839,8 @@ static int ipmi_init_msghandler(void)
        proc_ipmi_root->owner = THIS_MODULE;
 #endif /* CONFIG_PROC_FS */
 
-       init_timer(&ipmi_timer);
-       ipmi_timer.data = 0;
-       ipmi_timer.function = ipmi_timeout;
-       ipmi_timer.expires = jiffies + IPMI_TIMEOUT_JIFFIES;
-       add_timer(&ipmi_timer);
+       setup_timer(&ipmi_timer, ipmi_timeout, 0);
+       mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES);
 
        atomic_notifier_chain_register(&panic_notifier_list, &panic_block);