ipmi: Rescan channel list on BMC changes
authorCorey Minyard <cminyard@mvista.com>
Wed, 6 Sep 2017 13:23:31 +0000 (08:23 -0500)
committerCorey Minyard <cminyard@mvista.com>
Wed, 27 Sep 2017 21:03:45 +0000 (16:03 -0500)
If the BMC changes versions or a change is otherwise detected,
rescan the channels on the BMC.

Signed-off-by: Corey Minyard <cminyard@mvista.com>
drivers/char/ipmi/ipmi_msghandler.c

index 58cecebe950ff2ae4afae52389ce12ac3f86b62b..b5fc150e5128a3ccb307a71a9125d112bbe26501 100644 (file)
@@ -242,11 +242,16 @@ struct seq_table {
 
 #define NEXT_SEQID(seqid) (((seqid) + 1) & 0x3ffffff)
 
+#define IPMI_MAX_CHANNELS       16
 struct ipmi_channel {
        unsigned char medium;
        unsigned char protocol;
 };
 
+struct ipmi_channel_set {
+       struct ipmi_channel c[IPMI_MAX_CHANNELS];
+};
+
 struct ipmi_my_addrinfo {
        /*
         * My slave address.  This is initialized to IPMI_BMC_SLAVE_ADDR,
@@ -398,7 +403,6 @@ enum ipmi_stat_indexes {
 
 
 #define IPMI_IPMB_NUM_SEQ      64
-#define IPMI_MAX_CHANNELS       16
 struct ipmi_smi {
        /* What interface number are we? */
        int intf_num;
@@ -531,8 +535,11 @@ struct ipmi_smi {
        int curr_channel;
 
        /* Channel information */
-       struct ipmi_channel channels[IPMI_MAX_CHANNELS];
+       struct ipmi_channel_set *channel_list;
+       unsigned int curr_working_cset; /* First index into the following. */
+       struct ipmi_channel_set wchannels[2];
        struct ipmi_my_addrinfo addrinfo[IPMI_MAX_CHANNELS];
+       bool channels_ready;
 
        /* Proc FS stuff. */
        struct proc_dir_entry *proc_dir;
@@ -554,6 +561,7 @@ 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);
+static int __scan_channels(ipmi_smi_t intf, struct ipmi_device_id *id);
 
 
 /**
@@ -1775,6 +1783,7 @@ static int i_ipmi_request(ipmi_user_t          user,
                unsigned char         ipmb_seq;
                long                  seqid;
                int                   broadcast = 0;
+               struct ipmi_channel   *chans;
 
                if (addr->channel >= IPMI_MAX_CHANNELS) {
                        ipmi_inc_stat(intf, sent_invalid_commands);
@@ -1782,8 +1791,9 @@ static int i_ipmi_request(ipmi_user_t          user,
                        goto out_err;
                }
 
-               if (intf->channels[addr->channel].medium
-                                       != IPMI_CHANNEL_MEDIUM_IPMB) {
+               chans = READ_ONCE(intf->channel_list)->c;
+
+               if (chans[addr->channel].medium != IPMI_CHANNEL_MEDIUM_IPMB) {
                        ipmi_inc_stat(intf, sent_invalid_commands);
                        rv = -EINVAL;
                        goto out_err;
@@ -1905,6 +1915,7 @@ static int i_ipmi_request(ipmi_user_t          user,
                struct ipmi_lan_addr  *lan_addr;
                unsigned char         ipmb_seq;
                long                  seqid;
+               struct ipmi_channel   *chans;
 
                if (addr->channel >= IPMI_MAX_CHANNELS) {
                        ipmi_inc_stat(intf, sent_invalid_commands);
@@ -1912,9 +1923,11 @@ static int i_ipmi_request(ipmi_user_t          user,
                        goto out_err;
                }
 
-               if ((intf->channels[addr->channel].medium
+               chans = READ_ONCE(intf->channel_list)->c;
+
+               if ((chans[addr->channel].medium
                                != IPMI_CHANNEL_MEDIUM_8023LAN)
-                   && (intf->channels[addr->channel].medium
+                   && (chans[addr->channel].medium
                                != IPMI_CHANNEL_MEDIUM_ASYNC)) {
                        ipmi_inc_stat(intf, sent_invalid_commands);
                        rv = -EINVAL;
@@ -2282,6 +2295,9 @@ retry_bmc_lock:
                memcpy(intf->bmc->guid, guid, 16);
                if (__ipmi_bmc_register(intf, &id, guid_set, guid, intf_num))
                        need_waiter(intf); /* Retry later on an error. */
+               else
+                       __scan_channels(intf, &id);
+
 
                if (!intf_set) {
                        /*
@@ -2298,7 +2314,9 @@ retry_bmc_lock:
                bmc = intf->bmc;
                mutex_lock(&bmc->dyn_mutex);
                goto out_noprocessing;
-       }
+       } else if (memcmp(&bmc->fetch_id, &bmc->id, sizeof(bmc->id)))
+               /* Version info changes, scan the channels again. */
+               __scan_channels(intf, &bmc->fetch_id);
 
        bmc->dyn_id_expiry = jiffies + IPMI_DYN_DEV_ID_EXPIRY;
 
@@ -3212,7 +3230,9 @@ static void
 channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
 {
        int rv = 0;
-       int chan;
+       int ch;
+       unsigned int set = intf->curr_working_cset;
+       struct ipmi_channel *chans;
 
        if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE)
            && (msg->msg.netfn == IPMI_NETFN_APP_RESPONSE)
@@ -3228,12 +3248,13 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
                                 * assume it has one IPMB at channel
                                 * zero.
                                 */
-                               intf->channels[0].medium
+                               intf->wchannels[set].c[0].medium
                                        = IPMI_CHANNEL_MEDIUM_IPMB;
-                               intf->channels[0].protocol
+                               intf->wchannels[set].c[0].protocol
                                        = IPMI_CHANNEL_PROTOCOL_IPMB;
 
-                               intf->curr_channel = IPMI_MAX_CHANNELS;
+                               intf->channel_list = intf->wchannels + set;
+                               intf->channels_ready = true;
                                wake_up(&intf->waitq);
                                goto out;
                        }
@@ -3243,16 +3264,22 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
                        /* Message not big enough, just go on. */
                        goto next_channel;
                }
-               chan = intf->curr_channel;
-               intf->channels[chan].medium = msg->msg.data[2] & 0x7f;
-               intf->channels[chan].protocol = msg->msg.data[3] & 0x1f;
+               ch = intf->curr_channel;
+               chans = intf->wchannels[set].c;
+               chans[ch].medium = msg->msg.data[2] & 0x7f;
+               chans[ch].protocol = msg->msg.data[3] & 0x1f;
 
  next_channel:
                intf->curr_channel++;
-               if (intf->curr_channel >= IPMI_MAX_CHANNELS)
+               if (intf->curr_channel >= IPMI_MAX_CHANNELS) {
+                       intf->channel_list = intf->wchannels + set;
+                       intf->channels_ready = true;
                        wake_up(&intf->waitq);
-               else
+               } else {
+                       intf->channel_list = intf->wchannels + set;
+                       intf->channels_ready = true;
                        rv = send_channel_info_cmd(intf, intf->curr_channel);
+               }
 
                if (rv) {
                        /* Got an error somehow, just give up. */
@@ -3260,7 +3287,8 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
                               "Error sending channel information for channel"
                               " %d: %d\n", intf->curr_channel, rv);
 
-                       intf->curr_channel = IPMI_MAX_CHANNELS;
+                       intf->channel_list = intf->wchannels + set;
+                       intf->channels_ready = true;
                        wake_up(&intf->waitq);
                }
        }
@@ -3268,6 +3296,53 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
        return;
 }
 
+/*
+ * Must be holding intf->bmc_reg_mutex to call this.
+ */
+static int __scan_channels(ipmi_smi_t intf, struct ipmi_device_id *id)
+{
+       int rv;
+
+       if (ipmi_version_major(id) > 1
+                       || (ipmi_version_major(id) == 1
+                           && ipmi_version_minor(id) >= 5)) {
+               unsigned int set;
+
+               /*
+                * Start scanning the channels to see what is
+                * available.
+                */
+               set = !intf->curr_working_cset;
+               intf->curr_working_cset = set;
+               memset(&intf->wchannels[set], 0,
+                      sizeof(struct ipmi_channel_set));
+
+               intf->null_user_handler = channel_handler;
+               intf->curr_channel = 0;
+               rv = send_channel_info_cmd(intf, 0);
+               if (rv) {
+                       dev_warn(intf->si_dev,
+                                "Error sending channel information for channel 0, %d\n",
+                                rv);
+                       return -EIO;
+               }
+
+               /* Wait for the channel info to be read. */
+               wait_event(intf->waitq, intf->channels_ready);
+               intf->null_user_handler = NULL;
+       } else {
+               unsigned int set = intf->curr_working_cset;
+
+               /* Assume a single IPMB channel at zero. */
+               intf->wchannels[set].c[0].medium = IPMI_CHANNEL_MEDIUM_IPMB;
+               intf->wchannels[set].c[0].protocol = IPMI_CHANNEL_PROTOCOL_IPMB;
+               intf->channel_list = intf->wchannels + set;
+               intf->channels_ready = true;
+       }
+
+       return 0;
+}
+
 static void ipmi_poll(ipmi_smi_t intf)
 {
        if (intf->handlers->poll)
@@ -3402,35 +3477,11 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers,
                goto out;
        }
 
-       if (ipmi_version_major(&id) > 1
-                       || (ipmi_version_major(&id) == 1
-                           && ipmi_version_minor(&id) >= 5)) {
-               /*
-                * 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);
-               if (rv) {
-                       printk(KERN_WARNING PFX
-                              "Error sending channel information for channel"
-                              " 0, %d\n", rv);
-                       goto out;
-               }
-
-               /* Wait for the channel info to be read. */
-               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;
-               intf->channels[0].protocol = IPMI_CHANNEL_PROTOCOL_IPMB;
-               intf->curr_channel = IPMI_MAX_CHANNELS;
-       }
+       mutex_lock(&intf->bmc_reg_mutex);
+       rv = __scan_channels(intf, &id);
+       mutex_unlock(&intf->bmc_reg_mutex);
+       if (rv)
+               goto out;
 
        rv = add_proc_entries(intf, i);
 
@@ -4259,6 +4310,8 @@ static int handle_one_recv_msg(ipmi_smi_t          intf,
                deliver_response(recv_msg);
        } else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2))
                   && (msg->rsp[1] == IPMI_GET_MSG_CMD)) {
+               struct ipmi_channel   *chans;
+
                /* It's from the receive queue. */
                chan = msg->rsp[3] & 0xf;
                if (chan >= IPMI_MAX_CHANNELS) {
@@ -4273,12 +4326,14 @@ static int handle_one_recv_msg(ipmi_smi_t          intf,
                 * equal to or greater than IPMI_MAX_CHANNELS when all the
                 * channels for this interface have been initialized.
                 */
-               if (intf->curr_channel < IPMI_MAX_CHANNELS) {
+               if (!intf->channels_ready) {
                        requeue = 0; /* Throw the message away */
                        goto out;
                }
 
-               switch (intf->channels[chan].medium) {
+               chans = READ_ONCE(intf->channel_list)->c;
+
+               switch (chans[chan].medium) {
                case IPMI_CHANNEL_MEDIUM_IPMB:
                        if (msg->rsp[4] & 0x04) {
                                /*
@@ -4315,9 +4370,8 @@ static int handle_one_recv_msg(ipmi_smi_t          intf,
                default:
                        /* Check for OEM Channels.  Clients had better
                           register for these commands. */
-                       if ((intf->channels[chan].medium
-                            >= IPMI_CHANNEL_MEDIUM_OEM_MIN)
-                           && (intf->channels[chan].medium
+                       if ((chans[chan].medium >= IPMI_CHANNEL_MEDIUM_OEM_MIN)
+                           && (chans[chan].medium
                                <= IPMI_CHANNEL_MEDIUM_OEM_MAX)) {
                                requeue = handle_oem_get_msg_cmd(intf, msg);
                        } else {
@@ -4479,15 +4533,14 @@ void ipmi_smi_msg_received(ipmi_smi_t          intf,
                    && (msg->rsp[2] != IPMI_LOST_ARBITRATION_ERR)
                    && (msg->rsp[2] != IPMI_BUS_ERR)
                    && (msg->rsp[2] != IPMI_NAK_ON_WRITE_ERR)) {
-                       int chan = msg->rsp[3] & 0xf;
+                       int ch = msg->rsp[3] & 0xf;
+                       struct ipmi_channel *chans;
 
                        /* Got an error sending the message, handle it. */
-                       if (chan >= IPMI_MAX_CHANNELS)
-                               ; /* This shouldn't happen */
-                       else if ((intf->channels[chan].medium
-                                 == IPMI_CHANNEL_MEDIUM_8023LAN)
-                                || (intf->channels[chan].medium
-                                    == IPMI_CHANNEL_MEDIUM_ASYNC))
+
+                       chans = READ_ONCE(intf->channel_list)->c;
+                       if ((chans[ch].medium == IPMI_CHANNEL_MEDIUM_8023LAN)
+                           || (chans[ch].medium == IPMI_CHANNEL_MEDIUM_ASYNC))
                                ipmi_inc_stat(intf, sent_lan_command_errs);
                        else
                                ipmi_inc_stat(intf, sent_ipmb_command_errs);