From f9149ce369d9aeef39194f2738b8e53ab73df406 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 28 Aug 2014 18:29:52 -0700 Subject: [PATCH] Drivers: hv: vmbus: Cleanup hv_post_message() commit b29ef3546aecb253a5552b198cef23750d56e1e4 upstream. 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 . I would like to thank Sitsofe Wheeler for reporting the issue and helping in debuggging. Signed-off-by: K. Y. Srinivasan Reported-by: Sitsofe Wheeler Tested-by: Sitsofe Wheeler Signed-off-by: Greg Kroah-Hartman [bwh: Backported to 3.2: - s/NR_CPUS/MAX_NUM_CPUS/ - Adjust context, indentation - Also free the page in hv_synic_init() error path] Signed-off-by: Ben Hutchings --- drivers/hv/hv.c | 28 ++++++++++++++++------------ drivers/hv/hyperv_vmbus.h | 4 ++++ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 0fb100ed91a3..17ed6fbb9b56 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -158,6 +158,8 @@ int hv_init(void) memset(hv_context.synic_event_page, 0, sizeof(void *) * MAX_NUM_CPUS); memset(hv_context.synic_message_page, 0, sizeof(void *) * MAX_NUM_CPUS); + memset(hv_context.post_msg_page, 0, + sizeof(void *) * MAX_NUM_CPUS); if (!query_hypervisor_presence()) goto cleanup; @@ -258,26 +260,18 @@ u16 hv_post_message(union hv_connection_id connection_id, enum hv_message_type message_type, void *payload, size_t payload_size) { - struct aligned_input { - u64 alignment8; - struct hv_input_post_message msg; - }; struct hv_input_post_message *aligned_msg; u16 status; - unsigned long addr; if (payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT) return -EMSGSIZE; - addr = (unsigned long)kmalloc(sizeof(struct aligned_input), GFP_ATOMIC); - if (!addr) - return -ENOMEM; - aligned_msg = (struct hv_input_post_message *) - (ALIGN(addr, HV_HYPERCALL_PARAM_ALIGN)); + hv_context.post_msg_page[get_cpu()]; aligned_msg->connectionid = connection_id; + aligned_msg->reserved = 0; aligned_msg->message_type = message_type; aligned_msg->payload_size = payload_size; memcpy((void *)aligned_msg->payload, payload, payload_size); @@ -285,8 +279,7 @@ u16 hv_post_message(union hv_connection_id connection_id, status = do_hypercall(HVCALL_POST_MESSAGE, aligned_msg, NULL) & 0xFFFF; - kfree((void *)addr); - + put_cpu(); return status; } @@ -347,6 +340,14 @@ void hv_synic_init(void *irqarg) goto cleanup; } + hv_context.post_msg_page[cpu] = + (void *)get_zeroed_page(GFP_ATOMIC); + + if (hv_context.post_msg_page[cpu] == NULL) { + pr_err("Unable to allocate post msg page\n"); + goto cleanup; + } + /* Setup the Synic's message page */ rdmsrl(HV_X64_MSR_SIMP, simp.as_uint64); simp.simp_enabled = 1; @@ -388,6 +389,8 @@ cleanup: if (hv_context.synic_message_page[cpu]) free_page((unsigned long)hv_context.synic_message_page[cpu]); + if (hv_context.post_msg_page[cpu]) + free_page((unsigned long)hv_context.post_msg_page[cpu]); return; } @@ -426,4 +429,5 @@ void hv_synic_cleanup(void *arg) free_page((unsigned long)hv_context.synic_message_page[cpu]); free_page((unsigned long)hv_context.synic_event_page[cpu]); + free_page((unsigned long)hv_context.post_msg_page[cpu]); } diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 0aee1122734c..be2f3af70097 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -485,6 +485,10 @@ struct hv_context { void *synic_message_page[MAX_NUM_CPUS]; void *synic_event_page[MAX_NUM_CPUS]; + /* + * buffer to post messages to the host. + */ + void *post_msg_page[MAX_NUM_CPUS]; }; extern struct hv_context hv_context; -- 2.39.2