aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorK. Y. Srinivasan <kys@microsoft.com>2014-08-28 21:29:52 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-09-24 02:31:21 -0400
commitb29ef3546aecb253a5552b198cef23750d56e1e4 (patch)
treebf19352ffb8926cada04df65ba7c1222797e67bb
parent98d731bb064a9d1817a6ca9bf8b97051334a7cfe (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.c27
-rw-r--r--drivers/hv/hyperv_vmbus.h4
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
313void hv_synic_free(void) 316void 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
520extern struct hv_context hv_context; 524extern struct hv_context hv_context;