|
@@ -242,11 +242,16 @@ struct seq_table {
|
|
|
|
|
|
#define NEXT_SEQID(seqid) (((seqid) + 1) & 0x3ffffff)
|
|
#define NEXT_SEQID(seqid) (((seqid) + 1) & 0x3ffffff)
|
|
|
|
|
|
|
|
+#define IPMI_MAX_CHANNELS 16
|
|
struct ipmi_channel {
|
|
struct ipmi_channel {
|
|
unsigned char medium;
|
|
unsigned char medium;
|
|
unsigned char protocol;
|
|
unsigned char protocol;
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+struct ipmi_channel_set {
|
|
|
|
+ struct ipmi_channel c[IPMI_MAX_CHANNELS];
|
|
|
|
+};
|
|
|
|
+
|
|
struct ipmi_my_addrinfo {
|
|
struct ipmi_my_addrinfo {
|
|
/*
|
|
/*
|
|
* My slave address. This is initialized to IPMI_BMC_SLAVE_ADDR,
|
|
* 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_IPMB_NUM_SEQ 64
|
|
-#define IPMI_MAX_CHANNELS 16
|
|
|
|
struct ipmi_smi {
|
|
struct ipmi_smi {
|
|
/* What interface number are we? */
|
|
/* What interface number are we? */
|
|
int intf_num;
|
|
int intf_num;
|
|
@@ -531,8 +535,11 @@ struct ipmi_smi {
|
|
int curr_channel;
|
|
int curr_channel;
|
|
|
|
|
|
/* Channel information */
|
|
/* 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];
|
|
struct ipmi_my_addrinfo addrinfo[IPMI_MAX_CHANNELS];
|
|
|
|
+ bool channels_ready;
|
|
|
|
|
|
/* Proc FS stuff. */
|
|
/* Proc FS stuff. */
|
|
struct proc_dir_entry *proc_dir;
|
|
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,
|
|
static int __ipmi_bmc_register(ipmi_smi_t intf,
|
|
struct ipmi_device_id *id,
|
|
struct ipmi_device_id *id,
|
|
bool guid_set, u8 *guid, int intf_num);
|
|
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;
|
|
unsigned char ipmb_seq;
|
|
long seqid;
|
|
long seqid;
|
|
int broadcast = 0;
|
|
int broadcast = 0;
|
|
|
|
+ struct ipmi_channel *chans;
|
|
|
|
|
|
if (addr->channel >= IPMI_MAX_CHANNELS) {
|
|
if (addr->channel >= IPMI_MAX_CHANNELS) {
|
|
ipmi_inc_stat(intf, sent_invalid_commands);
|
|
ipmi_inc_stat(intf, sent_invalid_commands);
|
|
@@ -1782,8 +1791,9 @@ static int i_ipmi_request(ipmi_user_t user,
|
|
goto out_err;
|
|
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);
|
|
ipmi_inc_stat(intf, sent_invalid_commands);
|
|
rv = -EINVAL;
|
|
rv = -EINVAL;
|
|
goto out_err;
|
|
goto out_err;
|
|
@@ -1905,6 +1915,7 @@ static int i_ipmi_request(ipmi_user_t user,
|
|
struct ipmi_lan_addr *lan_addr;
|
|
struct ipmi_lan_addr *lan_addr;
|
|
unsigned char ipmb_seq;
|
|
unsigned char ipmb_seq;
|
|
long seqid;
|
|
long seqid;
|
|
|
|
+ struct ipmi_channel *chans;
|
|
|
|
|
|
if (addr->channel >= IPMI_MAX_CHANNELS) {
|
|
if (addr->channel >= IPMI_MAX_CHANNELS) {
|
|
ipmi_inc_stat(intf, sent_invalid_commands);
|
|
ipmi_inc_stat(intf, sent_invalid_commands);
|
|
@@ -1912,9 +1923,11 @@ static int i_ipmi_request(ipmi_user_t user,
|
|
goto out_err;
|
|
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)
|
|
!= IPMI_CHANNEL_MEDIUM_8023LAN)
|
|
- && (intf->channels[addr->channel].medium
|
|
|
|
|
|
+ && (chans[addr->channel].medium
|
|
!= IPMI_CHANNEL_MEDIUM_ASYNC)) {
|
|
!= IPMI_CHANNEL_MEDIUM_ASYNC)) {
|
|
ipmi_inc_stat(intf, sent_invalid_commands);
|
|
ipmi_inc_stat(intf, sent_invalid_commands);
|
|
rv = -EINVAL;
|
|
rv = -EINVAL;
|
|
@@ -2282,6 +2295,9 @@ retry_bmc_lock:
|
|
memcpy(intf->bmc->guid, guid, 16);
|
|
memcpy(intf->bmc->guid, guid, 16);
|
|
if (__ipmi_bmc_register(intf, &id, guid_set, guid, intf_num))
|
|
if (__ipmi_bmc_register(intf, &id, guid_set, guid, intf_num))
|
|
need_waiter(intf); /* Retry later on an error. */
|
|
need_waiter(intf); /* Retry later on an error. */
|
|
|
|
+ else
|
|
|
|
+ __scan_channels(intf, &id);
|
|
|
|
+
|
|
|
|
|
|
if (!intf_set) {
|
|
if (!intf_set) {
|
|
/*
|
|
/*
|
|
@@ -2298,7 +2314,9 @@ retry_bmc_lock:
|
|
bmc = intf->bmc;
|
|
bmc = intf->bmc;
|
|
mutex_lock(&bmc->dyn_mutex);
|
|
mutex_lock(&bmc->dyn_mutex);
|
|
goto out_noprocessing;
|
|
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;
|
|
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)
|
|
channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
|
|
{
|
|
{
|
|
int rv = 0;
|
|
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)
|
|
if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE)
|
|
&& (msg->msg.netfn == IPMI_NETFN_APP_RESPONSE)
|
|
&& (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
|
|
* assume it has one IPMB at channel
|
|
* zero.
|
|
* zero.
|
|
*/
|
|
*/
|
|
- intf->channels[0].medium
|
|
|
|
|
|
+ intf->wchannels[set].c[0].medium
|
|
= IPMI_CHANNEL_MEDIUM_IPMB;
|
|
= IPMI_CHANNEL_MEDIUM_IPMB;
|
|
- intf->channels[0].protocol
|
|
|
|
|
|
+ intf->wchannels[set].c[0].protocol
|
|
= IPMI_CHANNEL_PROTOCOL_IPMB;
|
|
= IPMI_CHANNEL_PROTOCOL_IPMB;
|
|
|
|
|
|
- intf->curr_channel = IPMI_MAX_CHANNELS;
|
|
|
|
|
|
+ intf->channel_list = intf->wchannels + set;
|
|
|
|
+ intf->channels_ready = true;
|
|
wake_up(&intf->waitq);
|
|
wake_up(&intf->waitq);
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
@@ -3243,16 +3264,22 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
|
|
/* Message not big enough, just go on. */
|
|
/* Message not big enough, just go on. */
|
|
goto next_channel;
|
|
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:
|
|
next_channel:
|
|
intf->curr_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);
|
|
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);
|
|
rv = send_channel_info_cmd(intf, intf->curr_channel);
|
|
|
|
+ }
|
|
|
|
|
|
if (rv) {
|
|
if (rv) {
|
|
/* Got an error somehow, just give up. */
|
|
/* 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"
|
|
"Error sending channel information for channel"
|
|
" %d: %d\n", intf->curr_channel, rv);
|
|
" %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);
|
|
wake_up(&intf->waitq);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -3268,6 +3296,53 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
|
|
return;
|
|
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)
|
|
static void ipmi_poll(ipmi_smi_t intf)
|
|
{
|
|
{
|
|
if (intf->handlers->poll)
|
|
if (intf->handlers->poll)
|
|
@@ -3402,35 +3477,11 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers,
|
|
goto out;
|
|
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);
|
|
rv = add_proc_entries(intf, i);
|
|
|
|
|
|
@@ -4259,6 +4310,8 @@ static int handle_one_recv_msg(ipmi_smi_t intf,
|
|
deliver_response(recv_msg);
|
|
deliver_response(recv_msg);
|
|
} else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2))
|
|
} else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2))
|
|
&& (msg->rsp[1] == IPMI_GET_MSG_CMD)) {
|
|
&& (msg->rsp[1] == IPMI_GET_MSG_CMD)) {
|
|
|
|
+ struct ipmi_channel *chans;
|
|
|
|
+
|
|
/* It's from the receive queue. */
|
|
/* It's from the receive queue. */
|
|
chan = msg->rsp[3] & 0xf;
|
|
chan = msg->rsp[3] & 0xf;
|
|
if (chan >= IPMI_MAX_CHANNELS) {
|
|
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
|
|
* equal to or greater than IPMI_MAX_CHANNELS when all the
|
|
* channels for this interface have been initialized.
|
|
* channels for this interface have been initialized.
|
|
*/
|
|
*/
|
|
- if (intf->curr_channel < IPMI_MAX_CHANNELS) {
|
|
|
|
|
|
+ if (!intf->channels_ready) {
|
|
requeue = 0; /* Throw the message away */
|
|
requeue = 0; /* Throw the message away */
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
|
|
- switch (intf->channels[chan].medium) {
|
|
|
|
|
|
+ chans = READ_ONCE(intf->channel_list)->c;
|
|
|
|
+
|
|
|
|
+ switch (chans[chan].medium) {
|
|
case IPMI_CHANNEL_MEDIUM_IPMB:
|
|
case IPMI_CHANNEL_MEDIUM_IPMB:
|
|
if (msg->rsp[4] & 0x04) {
|
|
if (msg->rsp[4] & 0x04) {
|
|
/*
|
|
/*
|
|
@@ -4315,9 +4370,8 @@ static int handle_one_recv_msg(ipmi_smi_t intf,
|
|
default:
|
|
default:
|
|
/* Check for OEM Channels. Clients had better
|
|
/* Check for OEM Channels. Clients had better
|
|
register for these commands. */
|
|
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)) {
|
|
<= IPMI_CHANNEL_MEDIUM_OEM_MAX)) {
|
|
requeue = handle_oem_get_msg_cmd(intf, msg);
|
|
requeue = handle_oem_get_msg_cmd(intf, msg);
|
|
} else {
|
|
} 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_LOST_ARBITRATION_ERR)
|
|
&& (msg->rsp[2] != IPMI_BUS_ERR)
|
|
&& (msg->rsp[2] != IPMI_BUS_ERR)
|
|
&& (msg->rsp[2] != IPMI_NAK_ON_WRITE_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. */
|
|
/* 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);
|
|
ipmi_inc_stat(intf, sent_lan_command_errs);
|
|
else
|
|
else
|
|
ipmi_inc_stat(intf, sent_ipmb_command_errs);
|
|
ipmi_inc_stat(intf, sent_ipmb_command_errs);
|