diff options
Diffstat (limited to 'arch/x86/hyperv/hv_init.c')
-rw-r--r-- | arch/x86/hyperv/hv_init.c | 165 |
1 files changed, 55 insertions, 110 deletions
diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c index 6952e219cba3..708a2712a516 100644 --- a/arch/x86/hyperv/hv_init.c +++ b/arch/x86/hyperv/hv_init.c @@ -7,10 +7,10 @@ * Author : K. Y. Srinivasan <kys@microsoft.com> */ -#include <linux/acpi.h> #include <linux/efi.h> #include <linux/types.h> #include <linux/bitfield.h> +#include <linux/io.h> #include <asm/apic.h> #include <asm/desc.h> #include <asm/hypervisor.h> @@ -39,71 +39,50 @@ EXPORT_SYMBOL_GPL(hv_hypercall_pg); /* Storage to save the hypercall page temporarily for hibernation */ static void *hv_hypercall_pg_saved; -u32 *hv_vp_index; -EXPORT_SYMBOL_GPL(hv_vp_index); - struct hv_vp_assist_page **hv_vp_assist_page; EXPORT_SYMBOL_GPL(hv_vp_assist_page); -void __percpu **hyperv_pcpu_input_arg; -EXPORT_SYMBOL_GPL(hyperv_pcpu_input_arg); - -void __percpu **hyperv_pcpu_output_arg; -EXPORT_SYMBOL_GPL(hyperv_pcpu_output_arg); - -u32 hv_max_vp_index; -EXPORT_SYMBOL_GPL(hv_max_vp_index); - static int hv_cpu_init(unsigned int cpu) { - u64 msr_vp_index; + union hv_vp_assist_msr_contents msr = { 0 }; struct hv_vp_assist_page **hvp = &hv_vp_assist_page[smp_processor_id()]; - void **input_arg; - struct page *pg; - - /* hv_cpu_init() can be called with IRQs disabled from hv_resume() */ - pg = alloc_pages(irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL, hv_root_partition ? 1 : 0); - if (unlikely(!pg)) - return -ENOMEM; - - input_arg = (void **)this_cpu_ptr(hyperv_pcpu_input_arg); - *input_arg = page_address(pg); - if (hv_root_partition) { - void **output_arg; - - output_arg = (void **)this_cpu_ptr(hyperv_pcpu_output_arg); - *output_arg = page_address(pg + 1); - } - - msr_vp_index = hv_get_register(HV_REGISTER_VP_INDEX); - - hv_vp_index[smp_processor_id()] = msr_vp_index; + int ret; - if (msr_vp_index > hv_max_vp_index) - hv_max_vp_index = msr_vp_index; + ret = hv_common_cpu_init(cpu); + if (ret) + return ret; if (!hv_vp_assist_page) return 0; - /* - * The VP ASSIST PAGE is an "overlay" page (see Hyper-V TLFS's Section - * 5.2.1 "GPA Overlay Pages"). Here it must be zeroed out to make sure - * we always write the EOI MSR in hv_apic_eoi_write() *after* the - * EOI optimization is disabled in hv_cpu_die(), otherwise a CPU may - * not be stopped in the case of CPU offlining and the VM will hang. - */ if (!*hvp) { - *hvp = __vmalloc(PAGE_SIZE, GFP_KERNEL | __GFP_ZERO); - } - - if (*hvp) { - u64 val; - - val = vmalloc_to_pfn(*hvp); - val = (val << HV_X64_MSR_VP_ASSIST_PAGE_ADDRESS_SHIFT) | - HV_X64_MSR_VP_ASSIST_PAGE_ENABLE; - - wrmsrl(HV_X64_MSR_VP_ASSIST_PAGE, val); + if (hv_root_partition) { + /* + * For root partition we get the hypervisor provided VP assist + * page, instead of allocating a new page. + */ + rdmsrl(HV_X64_MSR_VP_ASSIST_PAGE, msr.as_uint64); + *hvp = memremap(msr.pfn << + HV_X64_MSR_VP_ASSIST_PAGE_ADDRESS_SHIFT, + PAGE_SIZE, MEMREMAP_WB); + } else { + /* + * The VP assist page is an "overlay" page (see Hyper-V TLFS's + * Section 5.2.1 "GPA Overlay Pages"). Here it must be zeroed + * out to make sure we always write the EOI MSR in + * hv_apic_eoi_write() *after* the EOI optimization is disabled + * in hv_cpu_die(), otherwise a CPU may not be stopped in the + * case of CPU offlining and the VM will hang. + */ + *hvp = __vmalloc(PAGE_SIZE, GFP_KERNEL | __GFP_ZERO); + if (*hvp) + msr.pfn = vmalloc_to_pfn(*hvp); + } + WARN_ON(!(*hvp)); + if (*hvp) { + msr.enable = 1; + wrmsrl(HV_X64_MSR_VP_ASSIST_PAGE, msr.as_uint64); + } } return 0; @@ -198,29 +177,26 @@ static int hv_cpu_die(unsigned int cpu) { struct hv_reenlightenment_control re_ctrl; unsigned int new_cpu; - unsigned long flags; - void **input_arg; - void *pg; - local_irq_save(flags); - input_arg = (void **)this_cpu_ptr(hyperv_pcpu_input_arg); - pg = *input_arg; - *input_arg = NULL; - - if (hv_root_partition) { - void **output_arg; - - output_arg = (void **)this_cpu_ptr(hyperv_pcpu_output_arg); - *output_arg = NULL; + hv_common_cpu_die(cpu); + + if (hv_vp_assist_page && hv_vp_assist_page[cpu]) { + union hv_vp_assist_msr_contents msr = { 0 }; + if (hv_root_partition) { + /* + * For root partition the VP assist page is mapped to + * hypervisor provided page, and thus we unmap the + * page here and nullify it, so that in future we have + * correct page address mapped in hv_cpu_init. + */ + memunmap(hv_vp_assist_page[cpu]); + hv_vp_assist_page[cpu] = NULL; + rdmsrl(HV_X64_MSR_VP_ASSIST_PAGE, msr.as_uint64); + msr.enable = 0; + } + wrmsrl(HV_X64_MSR_VP_ASSIST_PAGE, msr.as_uint64); } - local_irq_restore(flags); - - free_pages((unsigned long)pg, hv_root_partition ? 1 : 0); - - if (hv_vp_assist_page && hv_vp_assist_page[cpu]) - wrmsrl(HV_X64_MSR_VP_ASSIST_PAGE, 0); - if (hv_reenlightenment_cb == NULL) return 0; @@ -368,7 +344,7 @@ void __init hyperv_init(void) { u64 guest_id, required_msrs; union hv_x64_msr_hypercall_contents hypercall_msr; - int cpuhp, i; + int cpuhp; if (x86_hyper_type != X86_HYPER_MS_HYPERV) return; @@ -380,36 +356,14 @@ void __init hyperv_init(void) if ((ms_hyperv.features & required_msrs) != required_msrs) return; - /* - * Allocate the per-CPU state for the hypercall input arg. - * If this allocation fails, we will not be able to setup - * (per-CPU) hypercall input page and thus this failure is - * fatal on Hyper-V. - */ - hyperv_pcpu_input_arg = alloc_percpu(void *); - - BUG_ON(hyperv_pcpu_input_arg == NULL); - - /* Allocate the per-CPU state for output arg for root */ - if (hv_root_partition) { - hyperv_pcpu_output_arg = alloc_percpu(void *); - BUG_ON(hyperv_pcpu_output_arg == NULL); - } - - /* Allocate percpu VP index */ - hv_vp_index = kmalloc_array(num_possible_cpus(), sizeof(*hv_vp_index), - GFP_KERNEL); - if (!hv_vp_index) + if (hv_common_init()) return; - for (i = 0; i < num_possible_cpus(); i++) - hv_vp_index[i] = VP_INVAL; - hv_vp_assist_page = kcalloc(num_possible_cpus(), sizeof(*hv_vp_assist_page), GFP_KERNEL); if (!hv_vp_assist_page) { ms_hyperv.hints &= ~HV_X64_ENLIGHTENED_VMCS_RECOMMENDED; - goto free_vp_index; + goto common_free; } cpuhp = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "x86/hyperv_init:online", @@ -507,9 +461,8 @@ remove_cpuhp_state: free_vp_assist_page: kfree(hv_vp_assist_page); hv_vp_assist_page = NULL; -free_vp_index: - kfree(hv_vp_index); - hv_vp_index = NULL; +common_free: + hv_common_free(); } /* @@ -539,7 +492,6 @@ void hyperv_cleanup(void) hypercall_msr.as_uint64 = 0; wrmsrl(HV_X64_MSR_REFERENCE_TSC, hypercall_msr.as_uint64); } -EXPORT_SYMBOL_GPL(hyperv_cleanup); void hyperv_report_panic(struct pt_regs *regs, long err, bool in_die) { @@ -595,12 +547,6 @@ bool hv_is_hyperv_initialized(void) } EXPORT_SYMBOL_GPL(hv_is_hyperv_initialized); -bool hv_is_hibernation_supported(void) -{ - return !hv_root_partition && acpi_sleep_state_supported(ACPI_STATE_S4); -} -EXPORT_SYMBOL_GPL(hv_is_hibernation_supported); - enum hv_isolation_type hv_get_isolation_type(void) { if (!(ms_hyperv.priv_high & HV_ISOLATION)) @@ -613,4 +559,3 @@ bool hv_is_isolation_supported(void) { return hv_get_isolation_type() != HV_ISOLATION_TYPE_NONE; } -EXPORT_SYMBOL_GPL(hv_is_isolation_supported); |