diff options
author | K. Y. Srinivasan <kys@microsoft.com> | 2014-08-28 21:29:52 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-09-24 02:31:21 -0400 |
commit | b29ef3546aecb253a5552b198cef23750d56e1e4 (patch) | |
tree | bf19352ffb8926cada04df65ba7c1222797e67bb | |
parent | 98d731bb064a9d1817a6ca9bf8b97051334a7cfe (diff) |
Drivers: hv: vmbus: Cleanup hv_post_message()
Minimize failures in this function by pre-allocating the buffer
for posting messages. The hypercall for posting the message can fail
for a number of reasons:
1. Transient resource related issues
2. Buffer alignment
3. Buffer cannot span a page boundry
We address issues 2 and 3 by preallocating a per-cpu page for the buffer.
Transient resource related failures are handled by retrying by the callers
of this function.
This patch is based on the investigation
done by Dexuan Cui <decui@microsoft.com>.
I would like to thank Sitsofe Wheeler <sitsofe@yahoo.com>
for reporting the issue and helping in debuggging.
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Reported-by: Sitsofe Wheeler <sitsofe@yahoo.com>
Cc: <stable@vger.kernel.org>
Tested-by: Sitsofe Wheeler <sitsofe@yahoo.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/hv/hv.c | 27 | ||||
-rw-r--r-- | drivers/hv/hyperv_vmbus.h | 4 |
2 files changed, 19 insertions, 12 deletions
diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index edfc8488cb03..3e4235c7a47f 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c | |||
@@ -138,6 +138,8 @@ int hv_init(void) | |||
138 | memset(hv_context.synic_event_page, 0, sizeof(void *) * NR_CPUS); | 138 | memset(hv_context.synic_event_page, 0, sizeof(void *) * NR_CPUS); |
139 | memset(hv_context.synic_message_page, 0, | 139 | memset(hv_context.synic_message_page, 0, |
140 | sizeof(void *) * NR_CPUS); | 140 | sizeof(void *) * NR_CPUS); |
141 | memset(hv_context.post_msg_page, 0, | ||
142 | sizeof(void *) * NR_CPUS); | ||
141 | memset(hv_context.vp_index, 0, | 143 | memset(hv_context.vp_index, 0, |
142 | sizeof(int) * NR_CPUS); | 144 | sizeof(int) * NR_CPUS); |
143 | memset(hv_context.event_dpc, 0, | 145 | memset(hv_context.event_dpc, 0, |
@@ -217,26 +219,18 @@ int hv_post_message(union hv_connection_id connection_id, | |||
217 | enum hv_message_type message_type, | 219 | enum hv_message_type message_type, |
218 | void *payload, size_t payload_size) | 220 | void *payload, size_t payload_size) |
219 | { | 221 | { |
220 | struct aligned_input { | ||
221 | u64 alignment8; | ||
222 | struct hv_input_post_message msg; | ||
223 | }; | ||
224 | 222 | ||
225 | struct hv_input_post_message *aligned_msg; | 223 | struct hv_input_post_message *aligned_msg; |
226 | u16 status; | 224 | u16 status; |
227 | unsigned long addr; | ||
228 | 225 | ||
229 | if (payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT) | 226 | if (payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT) |
230 | return -EMSGSIZE; | 227 | return -EMSGSIZE; |
231 | 228 | ||
232 | addr = (unsigned long)kmalloc(sizeof(struct aligned_input), GFP_ATOMIC); | ||
233 | if (!addr) | ||
234 | return -ENOMEM; | ||
235 | |||
236 | aligned_msg = (struct hv_input_post_message *) | 229 | aligned_msg = (struct hv_input_post_message *) |
237 | (ALIGN(addr, HV_HYPERCALL_PARAM_ALIGN)); | 230 | hv_context.post_msg_page[get_cpu()]; |
238 | 231 | ||
239 | aligned_msg->connectionid = connection_id; | 232 | aligned_msg->connectionid = connection_id; |
233 | aligned_msg->reserved = 0; | ||
240 | aligned_msg->message_type = message_type; | 234 | aligned_msg->message_type = message_type; |
241 | aligned_msg->payload_size = payload_size; | 235 | aligned_msg->payload_size = payload_size; |
242 | memcpy((void *)aligned_msg->payload, payload, payload_size); | 236 | memcpy((void *)aligned_msg->payload, payload, payload_size); |
@@ -244,8 +238,7 @@ int hv_post_message(union hv_connection_id connection_id, | |||
244 | status = do_hypercall(HVCALL_POST_MESSAGE, aligned_msg, NULL) | 238 | status = do_hypercall(HVCALL_POST_MESSAGE, aligned_msg, NULL) |
245 | & 0xFFFF; | 239 | & 0xFFFF; |
246 | 240 | ||
247 | kfree((void *)addr); | 241 | put_cpu(); |
248 | |||
249 | return status; | 242 | return status; |
250 | } | 243 | } |
251 | 244 | ||
@@ -294,6 +287,14 @@ int hv_synic_alloc(void) | |||
294 | pr_err("Unable to allocate SYNIC event page\n"); | 287 | pr_err("Unable to allocate SYNIC event page\n"); |
295 | goto err; | 288 | goto err; |
296 | } | 289 | } |
290 | |||
291 | hv_context.post_msg_page[cpu] = | ||
292 | (void *)get_zeroed_page(GFP_ATOMIC); | ||
293 | |||
294 | if (hv_context.post_msg_page[cpu] == NULL) { | ||
295 | pr_err("Unable to allocate post msg page\n"); | ||
296 | goto err; | ||
297 | } | ||
297 | } | 298 | } |
298 | 299 | ||
299 | return 0; | 300 | return 0; |
@@ -308,6 +309,8 @@ static void hv_synic_free_cpu(int cpu) | |||
308 | free_page((unsigned long)hv_context.synic_event_page[cpu]); | 309 | free_page((unsigned long)hv_context.synic_event_page[cpu]); |
309 | if (hv_context.synic_message_page[cpu]) | 310 | if (hv_context.synic_message_page[cpu]) |
310 | free_page((unsigned long)hv_context.synic_message_page[cpu]); | 311 | free_page((unsigned long)hv_context.synic_message_page[cpu]); |
312 | if (hv_context.post_msg_page[cpu]) | ||
313 | free_page((unsigned long)hv_context.post_msg_page[cpu]); | ||
311 | } | 314 | } |
312 | 315 | ||
313 | void hv_synic_free(void) | 316 | void hv_synic_free(void) |
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 22b750749a39..c386d8dc7223 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h | |||
@@ -515,6 +515,10 @@ struct hv_context { | |||
515 | * per-cpu list of the channels based on their CPU affinity. | 515 | * per-cpu list of the channels based on their CPU affinity. |
516 | */ | 516 | */ |
517 | struct list_head percpu_list[NR_CPUS]; | 517 | struct list_head percpu_list[NR_CPUS]; |
518 | /* | ||
519 | * buffer to post messages to the host. | ||
520 | */ | ||
521 | void *post_msg_page[NR_CPUS]; | ||
518 | }; | 522 | }; |
519 | 523 | ||
520 | extern struct hv_context hv_context; | 524 | extern struct hv_context hv_context; |