|
@@ -1703,6 +1703,332 @@ static bool is_maintenance_mode_cmd(struct kernel_ipmi_msg *msg)
|
|
|
|| (msg->netfn == IPMI_NETFN_FIRMWARE_REQUEST));
|
|
|
}
|
|
|
|
|
|
+static int i_ipmi_req_sysintf(ipmi_smi_t intf,
|
|
|
+ struct ipmi_addr *addr,
|
|
|
+ long msgid,
|
|
|
+ struct kernel_ipmi_msg *msg,
|
|
|
+ struct ipmi_smi_msg *smi_msg,
|
|
|
+ struct ipmi_recv_msg *recv_msg,
|
|
|
+ int retries,
|
|
|
+ unsigned int retry_time_ms)
|
|
|
+{
|
|
|
+ struct ipmi_system_interface_addr *smi_addr;
|
|
|
+
|
|
|
+ if (msg->netfn & 1)
|
|
|
+ /* Responses are not allowed to the SMI. */
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ smi_addr = (struct ipmi_system_interface_addr *) addr;
|
|
|
+ if (smi_addr->lun > 3) {
|
|
|
+ ipmi_inc_stat(intf, sent_invalid_commands);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ memcpy(&recv_msg->addr, smi_addr, sizeof(*smi_addr));
|
|
|
+
|
|
|
+ if ((msg->netfn == IPMI_NETFN_APP_REQUEST)
|
|
|
+ && ((msg->cmd == IPMI_SEND_MSG_CMD)
|
|
|
+ || (msg->cmd == IPMI_GET_MSG_CMD)
|
|
|
+ || (msg->cmd == IPMI_READ_EVENT_MSG_BUFFER_CMD))) {
|
|
|
+ /*
|
|
|
+ * We don't let the user do these, since we manage
|
|
|
+ * the sequence numbers.
|
|
|
+ */
|
|
|
+ ipmi_inc_stat(intf, sent_invalid_commands);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (is_maintenance_mode_cmd(msg)) {
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&intf->maintenance_mode_lock, flags);
|
|
|
+ intf->auto_maintenance_timeout
|
|
|
+ = maintenance_mode_timeout_ms;
|
|
|
+ if (!intf->maintenance_mode
|
|
|
+ && !intf->maintenance_mode_enable) {
|
|
|
+ intf->maintenance_mode_enable = true;
|
|
|
+ maintenance_mode_update(intf);
|
|
|
+ }
|
|
|
+ spin_unlock_irqrestore(&intf->maintenance_mode_lock,
|
|
|
+ flags);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (msg->data_len + 2 > IPMI_MAX_MSG_LENGTH) {
|
|
|
+ ipmi_inc_stat(intf, sent_invalid_commands);
|
|
|
+ return -EMSGSIZE;
|
|
|
+ }
|
|
|
+
|
|
|
+ smi_msg->data[0] = (msg->netfn << 2) | (smi_addr->lun & 0x3);
|
|
|
+ smi_msg->data[1] = msg->cmd;
|
|
|
+ smi_msg->msgid = msgid;
|
|
|
+ smi_msg->user_data = recv_msg;
|
|
|
+ if (msg->data_len > 0)
|
|
|
+ memcpy(&smi_msg->data[2], msg->data, msg->data_len);
|
|
|
+ smi_msg->data_size = msg->data_len + 2;
|
|
|
+ ipmi_inc_stat(intf, sent_local_commands);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int i_ipmi_req_ipmb(ipmi_smi_t intf,
|
|
|
+ struct ipmi_addr *addr,
|
|
|
+ long msgid,
|
|
|
+ struct kernel_ipmi_msg *msg,
|
|
|
+ struct ipmi_smi_msg *smi_msg,
|
|
|
+ struct ipmi_recv_msg *recv_msg,
|
|
|
+ unsigned char source_address,
|
|
|
+ unsigned char source_lun,
|
|
|
+ int retries,
|
|
|
+ unsigned int retry_time_ms)
|
|
|
+{
|
|
|
+ struct ipmi_ipmb_addr *ipmb_addr;
|
|
|
+ unsigned char ipmb_seq;
|
|
|
+ long seqid;
|
|
|
+ int broadcast = 0;
|
|
|
+ struct ipmi_channel *chans;
|
|
|
+ int rv = 0;
|
|
|
+
|
|
|
+ if (addr->channel >= IPMI_MAX_CHANNELS) {
|
|
|
+ ipmi_inc_stat(intf, sent_invalid_commands);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ chans = READ_ONCE(intf->channel_list)->c;
|
|
|
+
|
|
|
+ if (chans[addr->channel].medium != IPMI_CHANNEL_MEDIUM_IPMB) {
|
|
|
+ ipmi_inc_stat(intf, sent_invalid_commands);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE) {
|
|
|
+ /*
|
|
|
+ * Broadcasts add a zero at the beginning of the
|
|
|
+ * message, but otherwise is the same as an IPMB
|
|
|
+ * address.
|
|
|
+ */
|
|
|
+ addr->addr_type = IPMI_IPMB_ADDR_TYPE;
|
|
|
+ broadcast = 1;
|
|
|
+ retries = 0; /* Don't retry broadcasts. */
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * 9 for the header and 1 for the checksum, plus
|
|
|
+ * possibly one for the broadcast.
|
|
|
+ */
|
|
|
+ if ((msg->data_len + 10 + broadcast) > IPMI_MAX_MSG_LENGTH) {
|
|
|
+ ipmi_inc_stat(intf, sent_invalid_commands);
|
|
|
+ return -EMSGSIZE;
|
|
|
+ }
|
|
|
+
|
|
|
+ ipmb_addr = (struct ipmi_ipmb_addr *) addr;
|
|
|
+ if (ipmb_addr->lun > 3) {
|
|
|
+ ipmi_inc_stat(intf, sent_invalid_commands);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ memcpy(&recv_msg->addr, ipmb_addr, sizeof(*ipmb_addr));
|
|
|
+
|
|
|
+ if (recv_msg->msg.netfn & 0x1) {
|
|
|
+ /*
|
|
|
+ * It's a response, so use the user's sequence
|
|
|
+ * from msgid.
|
|
|
+ */
|
|
|
+ ipmi_inc_stat(intf, sent_ipmb_responses);
|
|
|
+ format_ipmb_msg(smi_msg, msg, ipmb_addr, msgid,
|
|
|
+ msgid, broadcast,
|
|
|
+ source_address, source_lun);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Save the receive message so we can use it
|
|
|
+ * to deliver the response.
|
|
|
+ */
|
|
|
+ smi_msg->user_data = recv_msg;
|
|
|
+ } else {
|
|
|
+ /* It's a command, so get a sequence for it. */
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&intf->seq_lock, flags);
|
|
|
+
|
|
|
+ if (is_maintenance_mode_cmd(msg))
|
|
|
+ intf->ipmb_maintenance_mode_timeout =
|
|
|
+ maintenance_mode_timeout_ms;
|
|
|
+
|
|
|
+ if (intf->ipmb_maintenance_mode_timeout && retry_time_ms == 0)
|
|
|
+ /* Different default in maintenance mode */
|
|
|
+ retry_time_ms = default_maintenance_retry_ms;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Create a sequence number with a 1 second
|
|
|
+ * timeout and 4 retries.
|
|
|
+ */
|
|
|
+ rv = intf_next_seq(intf,
|
|
|
+ recv_msg,
|
|
|
+ retry_time_ms,
|
|
|
+ retries,
|
|
|
+ broadcast,
|
|
|
+ &ipmb_seq,
|
|
|
+ &seqid);
|
|
|
+ if (rv)
|
|
|
+ /*
|
|
|
+ * We have used up all the sequence numbers,
|
|
|
+ * probably, so abort.
|
|
|
+ */
|
|
|
+ goto out_err;
|
|
|
+
|
|
|
+ ipmi_inc_stat(intf, sent_ipmb_commands);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Store the sequence number in the message,
|
|
|
+ * so that when the send message response
|
|
|
+ * comes back we can start the timer.
|
|
|
+ */
|
|
|
+ format_ipmb_msg(smi_msg, msg, ipmb_addr,
|
|
|
+ STORE_SEQ_IN_MSGID(ipmb_seq, seqid),
|
|
|
+ ipmb_seq, broadcast,
|
|
|
+ source_address, source_lun);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Copy the message into the recv message data, so we
|
|
|
+ * can retransmit it later if necessary.
|
|
|
+ */
|
|
|
+ memcpy(recv_msg->msg_data, smi_msg->data,
|
|
|
+ smi_msg->data_size);
|
|
|
+ recv_msg->msg.data = recv_msg->msg_data;
|
|
|
+ recv_msg->msg.data_len = smi_msg->data_size;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We don't unlock until here, because we need
|
|
|
+ * to copy the completed message into the
|
|
|
+ * recv_msg before we release the lock.
|
|
|
+ * Otherwise, race conditions may bite us. I
|
|
|
+ * know that's pretty paranoid, but I prefer
|
|
|
+ * to be correct.
|
|
|
+ */
|
|
|
+out_err:
|
|
|
+ spin_unlock_irqrestore(&intf->seq_lock, flags);
|
|
|
+ }
|
|
|
+
|
|
|
+ return rv;
|
|
|
+}
|
|
|
+
|
|
|
+static int i_ipmi_req_lan(ipmi_smi_t intf,
|
|
|
+ struct ipmi_addr *addr,
|
|
|
+ long msgid,
|
|
|
+ struct kernel_ipmi_msg *msg,
|
|
|
+ struct ipmi_smi_msg *smi_msg,
|
|
|
+ struct ipmi_recv_msg *recv_msg,
|
|
|
+ unsigned char source_lun,
|
|
|
+ int retries,
|
|
|
+ unsigned int retry_time_ms)
|
|
|
+{
|
|
|
+ struct ipmi_lan_addr *lan_addr;
|
|
|
+ unsigned char ipmb_seq;
|
|
|
+ long seqid;
|
|
|
+ struct ipmi_channel *chans;
|
|
|
+ int rv = 0;
|
|
|
+
|
|
|
+ if (addr->channel >= IPMI_MAX_CHANNELS) {
|
|
|
+ ipmi_inc_stat(intf, sent_invalid_commands);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ chans = READ_ONCE(intf->channel_list)->c;
|
|
|
+
|
|
|
+ if ((chans[addr->channel].medium
|
|
|
+ != IPMI_CHANNEL_MEDIUM_8023LAN)
|
|
|
+ && (chans[addr->channel].medium
|
|
|
+ != IPMI_CHANNEL_MEDIUM_ASYNC)) {
|
|
|
+ ipmi_inc_stat(intf, sent_invalid_commands);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 11 for the header and 1 for the checksum. */
|
|
|
+ if ((msg->data_len + 12) > IPMI_MAX_MSG_LENGTH) {
|
|
|
+ ipmi_inc_stat(intf, sent_invalid_commands);
|
|
|
+ return -EMSGSIZE;
|
|
|
+ }
|
|
|
+
|
|
|
+ lan_addr = (struct ipmi_lan_addr *) addr;
|
|
|
+ if (lan_addr->lun > 3) {
|
|
|
+ ipmi_inc_stat(intf, sent_invalid_commands);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ memcpy(&recv_msg->addr, lan_addr, sizeof(*lan_addr));
|
|
|
+
|
|
|
+ if (recv_msg->msg.netfn & 0x1) {
|
|
|
+ /*
|
|
|
+ * It's a response, so use the user's sequence
|
|
|
+ * from msgid.
|
|
|
+ */
|
|
|
+ ipmi_inc_stat(intf, sent_lan_responses);
|
|
|
+ format_lan_msg(smi_msg, msg, lan_addr, msgid,
|
|
|
+ msgid, source_lun);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Save the receive message so we can use it
|
|
|
+ * to deliver the response.
|
|
|
+ */
|
|
|
+ smi_msg->user_data = recv_msg;
|
|
|
+ } else {
|
|
|
+ /* It's a command, so get a sequence for it. */
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&intf->seq_lock, flags);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Create a sequence number with a 1 second
|
|
|
+ * timeout and 4 retries.
|
|
|
+ */
|
|
|
+ rv = intf_next_seq(intf,
|
|
|
+ recv_msg,
|
|
|
+ retry_time_ms,
|
|
|
+ retries,
|
|
|
+ 0,
|
|
|
+ &ipmb_seq,
|
|
|
+ &seqid);
|
|
|
+ if (rv)
|
|
|
+ /*
|
|
|
+ * We have used up all the sequence numbers,
|
|
|
+ * probably, so abort.
|
|
|
+ */
|
|
|
+ goto out_err;
|
|
|
+
|
|
|
+ ipmi_inc_stat(intf, sent_lan_commands);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Store the sequence number in the message,
|
|
|
+ * so that when the send message response
|
|
|
+ * comes back we can start the timer.
|
|
|
+ */
|
|
|
+ format_lan_msg(smi_msg, msg, lan_addr,
|
|
|
+ STORE_SEQ_IN_MSGID(ipmb_seq, seqid),
|
|
|
+ ipmb_seq, source_lun);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Copy the message into the recv message data, so we
|
|
|
+ * can retransmit it later if necessary.
|
|
|
+ */
|
|
|
+ memcpy(recv_msg->msg_data, smi_msg->data,
|
|
|
+ smi_msg->data_size);
|
|
|
+ recv_msg->msg.data = recv_msg->msg_data;
|
|
|
+ recv_msg->msg.data_len = smi_msg->data_size;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We don't unlock until here, because we need
|
|
|
+ * to copy the completed message into the
|
|
|
+ * recv_msg before we release the lock.
|
|
|
+ * Otherwise, race conditions may bite us. I
|
|
|
+ * know that's pretty paranoid, but I prefer
|
|
|
+ * to be correct.
|
|
|
+ */
|
|
|
+out_err:
|
|
|
+ spin_unlock_irqrestore(&intf->seq_lock, flags);
|
|
|
+ }
|
|
|
+
|
|
|
+ return rv;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Separate from ipmi_request so that the user does not have to be
|
|
|
* supplied in certain circumstances (mainly at panic time). If
|
|
@@ -1723,11 +2049,9 @@ static int i_ipmi_request(ipmi_user_t user,
|
|
|
int retries,
|
|
|
unsigned int retry_time_ms)
|
|
|
{
|
|
|
- int rv = 0;
|
|
|
- struct ipmi_smi_msg *smi_msg;
|
|
|
- struct ipmi_recv_msg *recv_msg;
|
|
|
- unsigned long flags;
|
|
|
-
|
|
|
+ struct ipmi_smi_msg *smi_msg;
|
|
|
+ struct ipmi_recv_msg *recv_msg;
|
|
|
+ int rv = 0;
|
|
|
|
|
|
if (supplied_recv)
|
|
|
recv_msg = supplied_recv;
|
|
@@ -1765,322 +2089,32 @@ static int i_ipmi_request(ipmi_user_t user,
|
|
|
recv_msg->msg = *msg;
|
|
|
|
|
|
if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) {
|
|
|
- struct ipmi_system_interface_addr *smi_addr;
|
|
|
-
|
|
|
- if (msg->netfn & 1) {
|
|
|
- /* Responses are not allowed to the SMI. */
|
|
|
- rv = -EINVAL;
|
|
|
- goto out_err;
|
|
|
- }
|
|
|
-
|
|
|
- smi_addr = (struct ipmi_system_interface_addr *) addr;
|
|
|
- if (smi_addr->lun > 3) {
|
|
|
- ipmi_inc_stat(intf, sent_invalid_commands);
|
|
|
- rv = -EINVAL;
|
|
|
- goto out_err;
|
|
|
- }
|
|
|
-
|
|
|
- memcpy(&recv_msg->addr, smi_addr, sizeof(*smi_addr));
|
|
|
-
|
|
|
- if ((msg->netfn == IPMI_NETFN_APP_REQUEST)
|
|
|
- && ((msg->cmd == IPMI_SEND_MSG_CMD)
|
|
|
- || (msg->cmd == IPMI_GET_MSG_CMD)
|
|
|
- || (msg->cmd == IPMI_READ_EVENT_MSG_BUFFER_CMD))) {
|
|
|
- /*
|
|
|
- * We don't let the user do these, since we manage
|
|
|
- * the sequence numbers.
|
|
|
- */
|
|
|
- ipmi_inc_stat(intf, sent_invalid_commands);
|
|
|
- rv = -EINVAL;
|
|
|
- goto out_err;
|
|
|
- }
|
|
|
-
|
|
|
- if (is_maintenance_mode_cmd(msg)) {
|
|
|
- spin_lock_irqsave(&intf->maintenance_mode_lock, flags);
|
|
|
- intf->auto_maintenance_timeout
|
|
|
- = maintenance_mode_timeout_ms;
|
|
|
- if (!intf->maintenance_mode
|
|
|
- && !intf->maintenance_mode_enable) {
|
|
|
- intf->maintenance_mode_enable = true;
|
|
|
- maintenance_mode_update(intf);
|
|
|
- }
|
|
|
- spin_unlock_irqrestore(&intf->maintenance_mode_lock,
|
|
|
- flags);
|
|
|
- }
|
|
|
-
|
|
|
- if ((msg->data_len + 2) > IPMI_MAX_MSG_LENGTH) {
|
|
|
- ipmi_inc_stat(intf, sent_invalid_commands);
|
|
|
- rv = -EMSGSIZE;
|
|
|
- goto out_err;
|
|
|
- }
|
|
|
-
|
|
|
- smi_msg->data[0] = (msg->netfn << 2) | (smi_addr->lun & 0x3);
|
|
|
- smi_msg->data[1] = msg->cmd;
|
|
|
- smi_msg->msgid = msgid;
|
|
|
- smi_msg->user_data = recv_msg;
|
|
|
- if (msg->data_len > 0)
|
|
|
- memcpy(&(smi_msg->data[2]), msg->data, msg->data_len);
|
|
|
- smi_msg->data_size = msg->data_len + 2;
|
|
|
- ipmi_inc_stat(intf, sent_local_commands);
|
|
|
+ rv = i_ipmi_req_sysintf(intf, addr, msgid, msg, smi_msg,
|
|
|
+ recv_msg, retries, retry_time_ms);
|
|
|
} else if (is_ipmb_addr(addr) || is_ipmb_bcast_addr(addr)) {
|
|
|
- struct ipmi_ipmb_addr *ipmb_addr;
|
|
|
- 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);
|
|
|
- rv = -EINVAL;
|
|
|
- goto out_err;
|
|
|
- }
|
|
|
-
|
|
|
- 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;
|
|
|
- }
|
|
|
-
|
|
|
- if (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE) {
|
|
|
- /*
|
|
|
- * Broadcasts add a zero at the beginning of the
|
|
|
- * message, but otherwise is the same as an IPMB
|
|
|
- * address.
|
|
|
- */
|
|
|
- addr->addr_type = IPMI_IPMB_ADDR_TYPE;
|
|
|
- broadcast = 1;
|
|
|
- retries = 0; /* Don't retry broadcasts. */
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * 9 for the header and 1 for the checksum, plus
|
|
|
- * possibly one for the broadcast.
|
|
|
- */
|
|
|
- if ((msg->data_len + 10 + broadcast) > IPMI_MAX_MSG_LENGTH) {
|
|
|
- ipmi_inc_stat(intf, sent_invalid_commands);
|
|
|
- rv = -EMSGSIZE;
|
|
|
- goto out_err;
|
|
|
- }
|
|
|
-
|
|
|
- ipmb_addr = (struct ipmi_ipmb_addr *) addr;
|
|
|
- if (ipmb_addr->lun > 3) {
|
|
|
- ipmi_inc_stat(intf, sent_invalid_commands);
|
|
|
- rv = -EINVAL;
|
|
|
- goto out_err;
|
|
|
- }
|
|
|
-
|
|
|
- memcpy(&recv_msg->addr, ipmb_addr, sizeof(*ipmb_addr));
|
|
|
-
|
|
|
- if (recv_msg->msg.netfn & 0x1) {
|
|
|
- /*
|
|
|
- * It's a response, so use the user's sequence
|
|
|
- * from msgid.
|
|
|
- */
|
|
|
- ipmi_inc_stat(intf, sent_ipmb_responses);
|
|
|
- format_ipmb_msg(smi_msg, msg, ipmb_addr, msgid,
|
|
|
- msgid, broadcast,
|
|
|
- source_address, source_lun);
|
|
|
-
|
|
|
- /*
|
|
|
- * Save the receive message so we can use it
|
|
|
- * to deliver the response.
|
|
|
- */
|
|
|
- smi_msg->user_data = recv_msg;
|
|
|
- } else {
|
|
|
- /* It's a command, so get a sequence for it. */
|
|
|
-
|
|
|
- spin_lock_irqsave(&(intf->seq_lock), flags);
|
|
|
-
|
|
|
- if (is_maintenance_mode_cmd(msg))
|
|
|
- intf->ipmb_maintenance_mode_timeout =
|
|
|
- maintenance_mode_timeout_ms;
|
|
|
-
|
|
|
- if (intf->ipmb_maintenance_mode_timeout &&
|
|
|
- retry_time_ms == 0)
|
|
|
- /* Different default in maintenance mode */
|
|
|
- retry_time_ms = default_maintenance_retry_ms;
|
|
|
-
|
|
|
- /*
|
|
|
- * Create a sequence number with a 1 second
|
|
|
- * timeout and 4 retries.
|
|
|
- */
|
|
|
- rv = intf_next_seq(intf,
|
|
|
- recv_msg,
|
|
|
- retry_time_ms,
|
|
|
- retries,
|
|
|
- broadcast,
|
|
|
- &ipmb_seq,
|
|
|
- &seqid);
|
|
|
- if (rv) {
|
|
|
- /*
|
|
|
- * We have used up all the sequence numbers,
|
|
|
- * probably, so abort.
|
|
|
- */
|
|
|
- spin_unlock_irqrestore(&(intf->seq_lock),
|
|
|
- flags);
|
|
|
- goto out_err;
|
|
|
- }
|
|
|
-
|
|
|
- ipmi_inc_stat(intf, sent_ipmb_commands);
|
|
|
-
|
|
|
- /*
|
|
|
- * Store the sequence number in the message,
|
|
|
- * so that when the send message response
|
|
|
- * comes back we can start the timer.
|
|
|
- */
|
|
|
- format_ipmb_msg(smi_msg, msg, ipmb_addr,
|
|
|
- STORE_SEQ_IN_MSGID(ipmb_seq, seqid),
|
|
|
- ipmb_seq, broadcast,
|
|
|
- source_address, source_lun);
|
|
|
-
|
|
|
- /*
|
|
|
- * Copy the message into the recv message data, so we
|
|
|
- * can retransmit it later if necessary.
|
|
|
- */
|
|
|
- memcpy(recv_msg->msg_data, smi_msg->data,
|
|
|
- smi_msg->data_size);
|
|
|
- recv_msg->msg.data = recv_msg->msg_data;
|
|
|
- recv_msg->msg.data_len = smi_msg->data_size;
|
|
|
-
|
|
|
- /*
|
|
|
- * We don't unlock until here, because we need
|
|
|
- * to copy the completed message into the
|
|
|
- * recv_msg before we release the lock.
|
|
|
- * Otherwise, race conditions may bite us. I
|
|
|
- * know that's pretty paranoid, but I prefer
|
|
|
- * to be correct.
|
|
|
- */
|
|
|
- spin_unlock_irqrestore(&(intf->seq_lock), flags);
|
|
|
- }
|
|
|
+ rv = i_ipmi_req_ipmb(intf, addr, msgid, msg, smi_msg, recv_msg,
|
|
|
+ source_address, source_lun,
|
|
|
+ retries, retry_time_ms);
|
|
|
} else if (is_lan_addr(addr)) {
|
|
|
- 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);
|
|
|
- rv = -EINVAL;
|
|
|
- goto out_err;
|
|
|
- }
|
|
|
-
|
|
|
- chans = READ_ONCE(intf->channel_list)->c;
|
|
|
-
|
|
|
- if ((chans[addr->channel].medium
|
|
|
- != IPMI_CHANNEL_MEDIUM_8023LAN)
|
|
|
- && (chans[addr->channel].medium
|
|
|
- != IPMI_CHANNEL_MEDIUM_ASYNC)) {
|
|
|
- ipmi_inc_stat(intf, sent_invalid_commands);
|
|
|
- rv = -EINVAL;
|
|
|
- goto out_err;
|
|
|
- }
|
|
|
-
|
|
|
- /* 11 for the header and 1 for the checksum. */
|
|
|
- if ((msg->data_len + 12) > IPMI_MAX_MSG_LENGTH) {
|
|
|
- ipmi_inc_stat(intf, sent_invalid_commands);
|
|
|
- rv = -EMSGSIZE;
|
|
|
- goto out_err;
|
|
|
- }
|
|
|
-
|
|
|
- lan_addr = (struct ipmi_lan_addr *) addr;
|
|
|
- if (lan_addr->lun > 3) {
|
|
|
- ipmi_inc_stat(intf, sent_invalid_commands);
|
|
|
- rv = -EINVAL;
|
|
|
- goto out_err;
|
|
|
- }
|
|
|
-
|
|
|
- memcpy(&recv_msg->addr, lan_addr, sizeof(*lan_addr));
|
|
|
-
|
|
|
- if (recv_msg->msg.netfn & 0x1) {
|
|
|
- /*
|
|
|
- * It's a response, so use the user's sequence
|
|
|
- * from msgid.
|
|
|
- */
|
|
|
- ipmi_inc_stat(intf, sent_lan_responses);
|
|
|
- format_lan_msg(smi_msg, msg, lan_addr, msgid,
|
|
|
- msgid, source_lun);
|
|
|
-
|
|
|
- /*
|
|
|
- * Save the receive message so we can use it
|
|
|
- * to deliver the response.
|
|
|
- */
|
|
|
- smi_msg->user_data = recv_msg;
|
|
|
- } else {
|
|
|
- /* It's a command, so get a sequence for it. */
|
|
|
-
|
|
|
- spin_lock_irqsave(&(intf->seq_lock), flags);
|
|
|
-
|
|
|
- /*
|
|
|
- * Create a sequence number with a 1 second
|
|
|
- * timeout and 4 retries.
|
|
|
- */
|
|
|
- rv = intf_next_seq(intf,
|
|
|
- recv_msg,
|
|
|
- retry_time_ms,
|
|
|
- retries,
|
|
|
- 0,
|
|
|
- &ipmb_seq,
|
|
|
- &seqid);
|
|
|
- if (rv) {
|
|
|
- /*
|
|
|
- * We have used up all the sequence numbers,
|
|
|
- * probably, so abort.
|
|
|
- */
|
|
|
- spin_unlock_irqrestore(&(intf->seq_lock),
|
|
|
- flags);
|
|
|
- goto out_err;
|
|
|
- }
|
|
|
-
|
|
|
- ipmi_inc_stat(intf, sent_lan_commands);
|
|
|
-
|
|
|
- /*
|
|
|
- * Store the sequence number in the message,
|
|
|
- * so that when the send message response
|
|
|
- * comes back we can start the timer.
|
|
|
- */
|
|
|
- format_lan_msg(smi_msg, msg, lan_addr,
|
|
|
- STORE_SEQ_IN_MSGID(ipmb_seq, seqid),
|
|
|
- ipmb_seq, source_lun);
|
|
|
-
|
|
|
- /*
|
|
|
- * Copy the message into the recv message data, so we
|
|
|
- * can retransmit it later if necessary.
|
|
|
- */
|
|
|
- memcpy(recv_msg->msg_data, smi_msg->data,
|
|
|
- smi_msg->data_size);
|
|
|
- recv_msg->msg.data = recv_msg->msg_data;
|
|
|
- recv_msg->msg.data_len = smi_msg->data_size;
|
|
|
-
|
|
|
- /*
|
|
|
- * We don't unlock until here, because we need
|
|
|
- * to copy the completed message into the
|
|
|
- * recv_msg before we release the lock.
|
|
|
- * Otherwise, race conditions may bite us. I
|
|
|
- * know that's pretty paranoid, but I prefer
|
|
|
- * to be correct.
|
|
|
- */
|
|
|
- spin_unlock_irqrestore(&(intf->seq_lock), flags);
|
|
|
- }
|
|
|
+ rv = i_ipmi_req_lan(intf, addr, msgid, msg, smi_msg, recv_msg,
|
|
|
+ source_lun, retries, retry_time_ms);
|
|
|
} else {
|
|
|
/* Unknown address type. */
|
|
|
ipmi_inc_stat(intf, sent_invalid_commands);
|
|
|
rv = -EINVAL;
|
|
|
- goto out_err;
|
|
|
}
|
|
|
|
|
|
- ipmi_debug_msg("Send", smi_msg->data, smi_msg->data_size);
|
|
|
+ if (rv) {
|
|
|
+out_err:
|
|
|
+ ipmi_free_smi_msg(smi_msg);
|
|
|
+ ipmi_free_recv_msg(recv_msg);
|
|
|
+ } else {
|
|
|
+ ipmi_debug_msg("Send", smi_msg->data, smi_msg->data_size);
|
|
|
|
|
|
- smi_send(intf, intf->handlers, smi_msg, priority);
|
|
|
+ smi_send(intf, intf->handlers, smi_msg, priority);
|
|
|
+ }
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
- return 0;
|
|
|
-
|
|
|
- out_err:
|
|
|
- rcu_read_unlock();
|
|
|
- ipmi_free_smi_msg(smi_msg);
|
|
|
- ipmi_free_recv_msg(recv_msg);
|
|
|
return rv;
|
|
|
}
|
|
|
|