From 1f42248d724a413baaafd5f83a8f4746bc6f51a5 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:42 -0800 Subject: Drivers: hv: Change the signature for hv_signal_event() In preparation for implementing a per-connection signaling framework, change the signature of the function hv_signal_event(). The current code uses a global handle for signaling the host. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/hv/hv.c') diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 3648f8f0f368..dd0af8940657 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -273,13 +273,12 @@ int hv_post_message(union hv_connection_id connection_id, * * This involves a hypercall. */ -u16 hv_signal_event(void) +u16 hv_signal_event(void *con_id) { u16 status; - status = do_hypercall(HVCALL_SIGNAL_EVENT, - hv_context.signal_event_param, - NULL) & 0xFFFF; + status = (do_hypercall(HVCALL_SIGNAL_EVENT, con_id, NULL) & 0xFFFF); + return status; } -- cgit v1.2.3 From 917ea427c78670958488f7f304e4629c325969a4 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:47 -0800 Subject: Drivers: hv: Setup a mapping for Hyper-V's notion cpu ID On win8 (ws2012), incoming vmbus interrupt load can be spread across all available VCPUs in the guest. On a per-channel basis, the interrupts can be bound to specific CPUs. The Linux notion of cpu ID may be different from that of the hypervisor's. Setup a mapping structure. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv.c | 11 +++++++++++ drivers/hv/hyperv_vmbus.h | 10 ++++++++++ 2 files changed, 21 insertions(+) (limited to 'drivers/hv/hv.c') diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index dd0af8940657..76304a622140 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -137,6 +137,8 @@ int hv_init(void) memset(hv_context.synic_event_page, 0, sizeof(void *) * NR_CPUS); memset(hv_context.synic_message_page, 0, sizeof(void *) * NR_CPUS); + memset(hv_context.vp_index, 0, + sizeof(int) * NR_CPUS); max_leaf = query_hypervisor_info(); @@ -296,6 +298,7 @@ void hv_synic_init(void *irqarg) union hv_synic_siefp siefp; union hv_synic_sint shared_sint; union hv_synic_scontrol sctrl; + u64 vp_index; u32 irq_vector = *((u32 *)(irqarg)); int cpu = smp_processor_id(); @@ -355,6 +358,14 @@ void hv_synic_init(void *irqarg) wrmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64); hv_context.synic_initialized = true; + + /* + * Setup the mapping between Hyper-V's notion + * of cpuid and Linux' notion of cpuid. + * This array will be indexed using Linux cpuid. + */ + rdmsrl(HV_X64_MSR_VP_INDEX, vp_index); + hv_context.vp_index[cpu] = (u32)vp_index; return; cleanup: diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 1bc7500fb84c..6bbc197cbdfa 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -502,6 +502,16 @@ struct hv_context { void *synic_message_page[NR_CPUS]; void *synic_event_page[NR_CPUS]; + /* + * Hypervisor's notion of virtual processor ID is different from + * Linux' notion of CPU ID. This information can only be retrieved + * in the context of the calling CPU. Setup a map for easy access + * to this information: + * + * vp_index[a] is the Hyper-V's processor ID corresponding to + * Linux cpuid 'a'. + */ + u32 vp_index[NR_CPUS]; }; extern struct hv_context hv_context; -- cgit v1.2.3 From 9acd6442c6839d404c1f47dc0f3ff3e0fb13e44c Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:51 -0800 Subject: Drivers: hv: Get rid of the unused global signaling state Now that we have implemented a per-connection signaling mechanism, get rid of the global signaling state. For hosts that don't support per-connection signaling handle, we have moved the global state to be a per-channel state. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv.c | 24 ------------------------ drivers/hv/hyperv_vmbus.h | 8 -------- 2 files changed, 32 deletions(-) (limited to 'drivers/hv/hv.c') diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 76304a622140..e989c6fd1f8f 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -34,8 +34,6 @@ struct hv_context hv_context = { .synic_initialized = false, .hypercall_page = NULL, - .signal_event_param = NULL, - .signal_event_buffer = NULL, }; /* @@ -170,24 +168,6 @@ int hv_init(void) hv_context.hypercall_page = virtaddr; - /* Setup the global signal event param for the signal event hypercall */ - hv_context.signal_event_buffer = - kmalloc(sizeof(struct hv_input_signal_event_buffer), - GFP_KERNEL); - if (!hv_context.signal_event_buffer) - goto cleanup; - - hv_context.signal_event_param = - (struct hv_input_signal_event *) - (ALIGN((unsigned long) - hv_context.signal_event_buffer, - HV_HYPERCALL_PARAM_ALIGN)); - hv_context.signal_event_param->connectionid.asu32 = 0; - hv_context.signal_event_param->connectionid.u.id = - VMBUS_EVENT_CONNECTION_ID; - hv_context.signal_event_param->flag_number = 0; - hv_context.signal_event_param->rsvdz = 0; - return 0; cleanup: @@ -215,10 +195,6 @@ void hv_cleanup(void) /* Reset our OS id */ wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0); - kfree(hv_context.signal_event_buffer); - hv_context.signal_event_buffer = NULL; - hv_context.signal_event_param = NULL; - if (hv_context.hypercall_page) { hypercall_msr.as_uint64 = 0; wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 6bbc197cbdfa..9135a6fcf3bd 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -492,14 +492,6 @@ struct hv_context { bool synic_initialized; - /* - * This is used as an input param to HvCallSignalEvent hypercall. The - * input param is immutable in our usage and must be dynamic mem (vs - * stack or global). */ - struct hv_input_signal_event_buffer *signal_event_buffer; - /* 8-bytes aligned of the buffer above */ - struct hv_input_signal_event *signal_event_param; - void *synic_message_page[NR_CPUS]; void *synic_event_page[NR_CPUS]; /* -- cgit v1.2.3 From db11f12a11c9f04d504510e1cc20775209b0e509 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:53 -0800 Subject: Drivers: hv: Manage event tasklets on per-cpu basis Now that we can potentially take vmbus interrupts on any CPU, make the tasklets per-CPU. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv.c | 12 ++++++++++++ drivers/hv/hyperv_vmbus.h | 6 ++++++ drivers/hv/vmbus_drv.c | 4 +--- 3 files changed, 19 insertions(+), 3 deletions(-) (limited to 'drivers/hv/hv.c') diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index e989c6fd1f8f..363532add4c7 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include "hyperv_vmbus.h" @@ -137,6 +138,8 @@ int hv_init(void) sizeof(void *) * NR_CPUS); memset(hv_context.vp_index, 0, sizeof(int) * NR_CPUS); + memset(hv_context.event_dpc, 0, + sizeof(void *) * NR_CPUS); max_leaf = query_hypervisor_info(); @@ -285,6 +288,15 @@ void hv_synic_init(void *irqarg) /* Check the version */ rdmsrl(HV_X64_MSR_SVERSION, version); + hv_context.event_dpc[cpu] = (struct tasklet_struct *) + kmalloc(sizeof(struct tasklet_struct), + GFP_ATOMIC); + if (hv_context.event_dpc[cpu] == NULL) { + pr_err("Unable to allocate event dpc\n"); + goto cleanup; + } + tasklet_init(hv_context.event_dpc[cpu], vmbus_on_event, cpu); + hv_context.synic_message_page[cpu] = (void *)get_zeroed_page(GFP_ATOMIC); diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 9135a6fcf3bd..becb106918d6 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -504,6 +504,12 @@ struct hv_context { * Linux cpuid 'a'. */ u32 vp_index[NR_CPUS]; + /* + * Starting with win8, we can take channel interrupts on any CPU; + * we will manage the tasklet that handles events on a per CPU + * basis. + */ + struct tasklet_struct *event_dpc[NR_CPUS]; }; extern struct hv_context hv_context; diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 4c92337ddae6..6e4f85720f26 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -41,7 +41,6 @@ static struct acpi_device *hv_acpi_dev; static struct tasklet_struct msg_dpc; -static struct tasklet_struct event_dpc; static struct completion probe_event; static int irq; @@ -483,7 +482,7 @@ static irqreturn_t vmbus_isr(int irq, void *dev_id) } if (handled) - tasklet_schedule(&event_dpc); + tasklet_schedule(hv_context.event_dpc[cpu]); page_addr = hv_context.synic_message_page[cpu]; @@ -523,7 +522,6 @@ static int vmbus_bus_init(int irq) } tasklet_init(&msg_dpc, vmbus_on_msg_dpc, 0); - tasklet_init(&event_dpc, vmbus_on_event, 0); ret = bus_register(&hv_bus); if (ret) -- cgit v1.2.3 From b0209501dc7586cbfbf6d023f2dd3ce4621aff2c Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:54 -0800 Subject: Drivers: hv: Handle vmbus interrupts concurrently on all cpus Vmbus interrupts are unique in that while the interrupt is delivered on a given vector, these can be handled concurrently on different CPUs. Handle the vmbus interrupts concurrently on all the CPUs. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv.c | 2 +- drivers/hv/vmbus_drv.c | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) (limited to 'drivers/hv/hv.c') diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 363532add4c7..03e6a1eb1145 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -335,7 +335,7 @@ void hv_synic_init(void *irqarg) shared_sint.as_uint64 = 0; shared_sint.vector = irq_vector; /* HV_SHARED_SINT_IDT_VECTOR + 0x20; */ shared_sint.masked = false; - shared_sint.auto_eoi = false; + shared_sint.auto_eoi = true; wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 6e4f85720f26..e066d418be97 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include "hyperv_vmbus.h" @@ -500,6 +501,19 @@ static irqreturn_t vmbus_isr(int irq, void *dev_id) return IRQ_NONE; } +/* + * vmbus interrupt flow handler: + * vmbus interrupts can concurrently occur on multiple CPUs and + * can be handled concurrently. + */ + +void vmbus_flow_handler(unsigned int irq, struct irq_desc *desc) +{ + kstat_incr_irqs_this_cpu(irq, desc); + + desc->action->handler(irq, desc->action->dev_id); +} + /* * vmbus_bus_init -Main vmbus driver initialization routine. * @@ -535,6 +549,13 @@ static int vmbus_bus_init(int irq) goto err_unregister; } + /* + * Vmbus interrupts can be handled concurrently on + * different CPUs. Establish an appropriate interrupt flow + * handler that can support this model. + */ + irq_set_handler(irq, vmbus_flow_handler); + vector = IRQ0_VECTOR + irq; /* -- cgit v1.2.3 From 5fbebb2d2095e5c7d289d5f4ffecc2f2661c584a Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:58 -0800 Subject: Drivers: hv: Capture the host build information Capture the host build information so it can be presented along with the negotiated vmbus version information. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv.c | 9 +++++++++ drivers/hv/hyperv_vmbus.h | 7 +++++++ 2 files changed, 16 insertions(+) (limited to 'drivers/hv/hv.c') diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 03e6a1eb1145..0cd2da3b22f5 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -40,6 +40,11 @@ struct hv_context hv_context = { /* * query_hypervisor_info - Get version info of the windows hypervisor */ +unsigned int host_info_eax; +unsigned int host_info_ebx; +unsigned int host_info_ecx; +unsigned int host_info_edx; + static int query_hypervisor_info(void) { unsigned int eax; @@ -76,6 +81,10 @@ static int query_hypervisor_info(void) ecx, edx >> 24, edx & 0xFFFFFF); + host_info_eax = eax; + host_info_ebx = ebx; + host_info_ecx = ecx; + host_info_edx = edx; } return max_leaf; } diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index ac111f223821..12f2f9e989f7 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -531,6 +531,13 @@ extern void hv_synic_init(void *irqarg); extern void hv_synic_cleanup(void *arg); +/* + * Host version information. + */ +extern unsigned int host_info_eax; +extern unsigned int host_info_ebx; +extern unsigned int host_info_ecx; +extern unsigned int host_info_edx; /* Interface */ -- cgit v1.2.3 From 3bacaf0ce106d9caca75f64a58f3b938b070df29 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:59 -0800 Subject: Drivers: hv: Cleanup and consolidate reporting of build/version info Now, cleanup and consolidate reporting of host and vmbus version numbers. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/connection.c | 11 +++++++---- drivers/hv/hv.c | 7 ------- 2 files changed, 7 insertions(+), 11 deletions(-) (limited to 'drivers/hv/hv.c') diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index 3965537a659d..253a74ba245c 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -120,9 +120,6 @@ static int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, if (msginfo->response.version_response.version_supported) { vmbus_connection.conn_state = CONNECTED; } else { - pr_err("Unable to connect, " - "Version %d not supported by Hyper-V\n", - version); return -ECONNREFUSED; } @@ -208,11 +205,17 @@ int vmbus_connect(void) goto cleanup; vmbus_proto_version = version; - pr_info("Negotiated host information %d\n", version); + pr_info("Hyper-V Host Build:%d-%d.%d-%d-%d.%d; Vmbus version:%d.%d\n", + host_info_eax, host_info_ebx >> 16, + host_info_ebx & 0xFFFF, host_info_ecx, + host_info_edx >> 24, host_info_edx & 0xFFFFFF, + version >> 16, version & 0xFFFF); + kfree(msginfo); return 0; cleanup: + pr_err("Unable to connect to host\n"); vmbus_connection.conn_state = DISCONNECTED; if (vmbus_connection.work_queue) diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 0cd2da3b22f5..1c5481da6e4a 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -74,13 +74,6 @@ static int query_hypervisor_info(void) edx = 0; op = HVCPUID_VERSION; cpuid(op, &eax, &ebx, &ecx, &edx); - pr_info("Hyper-V Host OS Build:%d-%d.%d-%d-%d.%d\n", - eax, - ebx >> 16, - ebx & 0xFFFF, - ecx, - edx >> 24, - edx & 0xFFFFFF); host_info_eax = eax; host_info_ebx = ebx; host_info_ecx = ecx; -- cgit v1.2.3 From 302a3c0f2757ae1a3e7df3f9fa1f20e2214ab945 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sun, 17 Feb 2013 11:30:44 -0800 Subject: Drivers: hv: vmbus: Use the new infrastructure for delivering VMBUS interrupts Use the infrastructure for delivering VMBUS interrupts using a special vector. With this patch, we can now properly handle the VMBUS interrupts that can be delivered on any CPU. Also, turn on interrupt load balancing as well. This patch requires the infrastructure that was implemented in the patch: X86: Handle Hyper-V vmbus interrupts as special hypervisor interrupts Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 2 +- drivers/hv/hv.c | 5 ++--- drivers/hv/vmbus_drv.c | 11 +++++++---- 3 files changed, 10 insertions(+), 8 deletions(-) (limited to 'drivers/hv/hv.c') diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 53a8600162a5..ff1be167eb04 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -318,7 +318,7 @@ static u32 get_vp_index(uuid_le *type_guid) return 0; } cur_cpu = (++next_vp % max_cpus); - return 0; + return cur_cpu; } /* diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 1c5481da6e4a..731158910c1e 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -272,7 +272,7 @@ u16 hv_signal_event(void *con_id) * retrieve the initialized message and event pages. Otherwise, we create and * initialize the message and event pages. */ -void hv_synic_init(void *irqarg) +void hv_synic_init(void *arg) { u64 version; union hv_synic_simp simp; @@ -281,7 +281,6 @@ void hv_synic_init(void *irqarg) union hv_synic_scontrol sctrl; u64 vp_index; - u32 irq_vector = *((u32 *)(irqarg)); int cpu = smp_processor_id(); if (!hv_context.hypercall_page) @@ -335,7 +334,7 @@ void hv_synic_init(void *irqarg) rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); shared_sint.as_uint64 = 0; - shared_sint.vector = irq_vector; /* HV_SHARED_SINT_IDT_VECTOR + 0x20; */ + shared_sint.vector = HYPERVISOR_CALLBACK_VECTOR; shared_sint.masked = false; shared_sint.auto_eoi = true; diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index cf19dfa5ead1..bf421e0efa1e 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "hyperv_vmbus.h" @@ -528,7 +529,6 @@ static void vmbus_flow_handler(unsigned int irq, struct irq_desc *desc) static int vmbus_bus_init(int irq) { int ret; - unsigned int vector; /* Hypervisor initialization...setup hypercall page..etc */ ret = hv_init(); @@ -558,13 +558,16 @@ static int vmbus_bus_init(int irq) */ irq_set_handler(irq, vmbus_flow_handler); - vector = IRQ0_VECTOR + irq; + /* + * Register our interrupt handler. + */ + hv_register_vmbus_handler(irq, vmbus_isr); /* - * Notify the hypervisor of our irq and + * Initialize the per-cpu interrupt state and * connect to the host. */ - on_each_cpu(hv_synic_init, (void *)&vector, 1); + on_each_cpu(hv_synic_init, NULL, 1); ret = vmbus_connect(); if (ret) goto err_irq; -- cgit v1.2.3