diff options
author | Lv Zheng <lv.zheng@intel.com> | 2013-09-13 01:13:47 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-09-30 13:46:11 -0400 |
commit | 8584ec6ae9cc386de344e0d33b60f76368bb73ab (patch) | |
tree | 46ad6a2073010c20981b8b16e58b35e689c29603 /drivers/acpi/acpi_ipmi.c | |
parent | 5ac557ef4951ea4b131ae45b08434546cb386ac5 (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.c | 49 |
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 | ||
57 | struct acpi_ipmi_device { | 57 | struct 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 | ||
194 | static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg, | 195 | static 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; | ||
293 | out_comp: | ||
288 | complete(&tx_msg->tx_complete); | 294 | complete(&tx_msg->tx_complete); |
289 | out_lock: | 295 | out_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 | ||
435 | out_list: | 440 | out_list: |