aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/acpi_ipmi.c
diff options
context:
space:
mode:
authorLv Zheng <lv.zheng@intel.com>2013-09-13 01:13:47 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-09-30 13:46:11 -0400
commit8584ec6ae9cc386de344e0d33b60f76368bb73ab (patch)
tree46ad6a2073010c20981b8b16e58b35e689c29603 /drivers/acpi/acpi_ipmi.c
parent5ac557ef4951ea4b131ae45b08434546cb386ac5 (diff)
ACPI / IPMI: Fix race caused by the timed out ACPI IPMI transfers
This patch fixes races caused by timed out ACPI IPMI transfers. This patch uses timeout mechanism provided by ipmi_si to avoid the race that the msg_done flag is set but without any protection, its content can be invalid. Thanks for the suggestion of Corey Minyard. Signed-off-by: Lv Zheng <lv.zheng@intel.com> Reviewed-by: Huang Ying <ying.huang@intel.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/acpi/acpi_ipmi.c')
-rw-r--r--drivers/acpi/acpi_ipmi.c49
1 files changed, 27 insertions, 22 deletions
diff --git a/drivers/acpi/acpi_ipmi.c b/drivers/acpi/acpi_ipmi.c
index 87307baeafab..9171a1a668f2 100644
--- a/drivers/acpi/acpi_ipmi.c
+++ b/drivers/acpi/acpi_ipmi.c
@@ -51,7 +51,7 @@ MODULE_LICENSE("GPL");
51#define ACPI_IPMI_TIMEOUT 0x10 51#define ACPI_IPMI_TIMEOUT 0x10
52#define ACPI_IPMI_UNKNOWN 0x07 52#define ACPI_IPMI_UNKNOWN 0x07
53/* the IPMI timeout is 5s */ 53/* the IPMI timeout is 5s */
54#define IPMI_TIMEOUT (5 * HZ) 54#define IPMI_TIMEOUT (5000)
55#define ACPI_IPMI_MAX_MSG_LENGTH 64 55#define ACPI_IPMI_MAX_MSG_LENGTH 64
56 56
57struct acpi_ipmi_device { 57struct acpi_ipmi_device {
@@ -135,6 +135,7 @@ static struct acpi_ipmi_msg *acpi_alloc_ipmi_msg(struct acpi_ipmi_device *ipmi)
135 init_completion(&ipmi_msg->tx_complete); 135 init_completion(&ipmi_msg->tx_complete);
136 INIT_LIST_HEAD(&ipmi_msg->head); 136 INIT_LIST_HEAD(&ipmi_msg->head);
137 ipmi_msg->device = ipmi; 137 ipmi_msg->device = ipmi;
138 ipmi_msg->msg_done = ACPI_IPMI_UNKNOWN;
138 return ipmi_msg; 139 return ipmi_msg;
139} 140}
140 141
@@ -192,7 +193,7 @@ static int acpi_format_ipmi_request(struct acpi_ipmi_msg *tx_msg,
192} 193}
193 194
194static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg, 195static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
195 acpi_integer *value, int rem_time) 196 acpi_integer *value)
196{ 197{
197 struct acpi_ipmi_buffer *buffer; 198 struct acpi_ipmi_buffer *buffer;
198 199
@@ -201,24 +202,17 @@ static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
201 * IPMI message returned by IPMI command. 202 * IPMI message returned by IPMI command.
202 */ 203 */
203 buffer = (struct acpi_ipmi_buffer *)value; 204 buffer = (struct acpi_ipmi_buffer *)value;
204 if (!rem_time && !msg->msg_done) {
205 buffer->status = ACPI_IPMI_TIMEOUT;
206 return;
207 }
208 /* 205 /*
209 * If the flag of msg_done is not set or the recv length is zero, it 206 * If the flag of msg_done is not set, it means that the IPMI command is
210 * means that the IPMI command is not executed correctly. 207 * not executed correctly.
211 * The status code will be ACPI_IPMI_UNKNOWN.
212 */ 208 */
213 if (!msg->msg_done || !msg->rx_len) { 209 buffer->status = msg->msg_done;
214 buffer->status = ACPI_IPMI_UNKNOWN; 210 if (msg->msg_done != ACPI_IPMI_OK)
215 return; 211 return;
216 }
217 /* 212 /*
218 * If the IPMI response message is obtained correctly, the status code 213 * If the IPMI response message is obtained correctly, the status code
219 * will be ACPI_IPMI_OK 214 * will be ACPI_IPMI_OK
220 */ 215 */
221 buffer->status = ACPI_IPMI_OK;
222 buffer->length = msg->rx_len; 216 buffer->length = msg->rx_len;
223 memcpy(buffer->data, msg->data, msg->rx_len); 217 memcpy(buffer->data, msg->data, msg->rx_len);
224} 218}
@@ -280,11 +274,23 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
280 dev_WARN_ONCE(&pnp_dev->dev, true, 274 dev_WARN_ONCE(&pnp_dev->dev, true,
281 "Unexpected response (msg len %d).\n", 275 "Unexpected response (msg len %d).\n",
282 msg->msg.data_len); 276 msg->msg.data_len);
283 } else { 277 goto out_comp;
284 tx_msg->rx_len = msg->msg.data_len;
285 memcpy(tx_msg->data, msg->msg.data, tx_msg->rx_len);
286 tx_msg->msg_done = 1;
287 } 278 }
279 /* response msg is an error msg */
280 msg->recv_type = IPMI_RESPONSE_RECV_TYPE;
281 if (msg->recv_type == IPMI_RESPONSE_RECV_TYPE &&
282 msg->msg.data_len == 1) {
283 if (msg->msg.data[0] == IPMI_TIMEOUT_COMPLETION_CODE) {
284 dev_WARN_ONCE(&pnp_dev->dev, true,
285 "Unexpected response (timeout).\n");
286 tx_msg->msg_done = ACPI_IPMI_TIMEOUT;
287 }
288 goto out_comp;
289 }
290 tx_msg->rx_len = msg->msg.data_len;
291 memcpy(tx_msg->data, msg->msg.data, tx_msg->rx_len);
292 tx_msg->msg_done = ACPI_IPMI_OK;
293out_comp:
288 complete(&tx_msg->tx_complete); 294 complete(&tx_msg->tx_complete);
289out_lock: 295out_lock:
290 spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags); 296 spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
@@ -392,7 +398,7 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
392{ 398{
393 struct acpi_ipmi_msg *tx_msg; 399 struct acpi_ipmi_msg *tx_msg;
394 struct acpi_ipmi_device *ipmi_device = handler_context; 400 struct acpi_ipmi_device *ipmi_device = handler_context;
395 int err, rem_time; 401 int err;
396 acpi_status status; 402 acpi_status status;
397 unsigned long flags; 403 unsigned long flags;
398 /* 404 /*
@@ -422,14 +428,13 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
422 &tx_msg->addr, 428 &tx_msg->addr,
423 tx_msg->tx_msgid, 429 tx_msg->tx_msgid,
424 &tx_msg->tx_message, 430 &tx_msg->tx_message,
425 NULL, 0, 0, 0); 431 NULL, 0, 0, IPMI_TIMEOUT);
426 if (err) { 432 if (err) {
427 status = AE_ERROR; 433 status = AE_ERROR;
428 goto out_list; 434 goto out_list;
429 } 435 }
430 rem_time = wait_for_completion_timeout(&tx_msg->tx_complete, 436 wait_for_completion(&tx_msg->tx_complete);
431 IPMI_TIMEOUT); 437 acpi_format_ipmi_response(tx_msg, value);
432 acpi_format_ipmi_response(tx_msg, value, rem_time);
433 status = AE_OK; 438 status = AE_OK;
434 439
435out_list: 440out_list: