Drivers: hv: Make portions of Hyper-V init code be arch neutral
authorMichael Kelley <mikelley@microsoft.com>
Wed, 14 Jul 2021 18:34:45 +0000 (11:34 -0700)
committerWei Liu <wei.liu@kernel.org>
Thu, 15 Jul 2021 12:59:45 +0000 (12:59 +0000)
The code to allocate and initialize the hv_vp_index array is
architecture neutral. Similarly, the code to allocate and
populate the hypercall input and output arg pages is architecture
neutral.  Move both sets of code out from arch/x86 and into
utility functions in drivers/hv/hv_common.c that can be shared
by Hyper-V initialization on ARM64.

No functional changes. However, the allocation of the hypercall
input and output arg pages is done differently so that the
size is always the Hyper-V page size, even if not the same as
the guest page size (such as with ARM64's 64K page size).

Signed-off-by: Michael Kelley <mikelley@microsoft.com>
Link: https://lore.kernel.org/r/1626287687-2045-2-git-send-email-mikelley@microsoft.com
Signed-off-by: Wei Liu <wei.liu@kernel.org>
arch/x86/hyperv/hv_init.c
arch/x86/include/asm/mshyperv.h
arch/x86/kernel/cpu/mshyperv.c
drivers/hv/hv_common.c
include/asm-generic/mshyperv.h

index 6952e219cba36b6da255c883a774c846184a3a42..5cc0c0f30e75b3d44c27d13a98c2207b7e31d76d 100644 (file)
@@ -39,48 +39,17 @@ 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;
        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;
@@ -198,25 +167,8 @@ 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;
-       }
-
-       local_irq_restore(flags);
-
-       free_pages((unsigned long)pg, hv_root_partition ? 1 : 0);
+       hv_common_cpu_die(cpu);
 
        if (hv_vp_assist_page && hv_vp_assist_page[cpu])
                wrmsrl(HV_X64_MSR_VP_ASSIST_PAGE, 0);
@@ -368,7 +320,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 +332,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 +437,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();
 }
 
 /*
index 67ff0d637e554129454977d71ca03fc98badbd27..adccbc209169aa11a2ac8d8f005c61f116bc5cfd 100644 (file)
@@ -36,8 +36,6 @@ void hyperv_vector_handler(struct pt_regs *regs);
 extern int hyperv_init_cpuhp;
 
 extern void *hv_hypercall_pg;
-extern void  __percpu  **hyperv_pcpu_input_arg;
-extern void  __percpu  **hyperv_pcpu_output_arg;
 
 extern u64 hv_current_partition_id;
 
@@ -170,8 +168,6 @@ int hyperv_fill_flush_guest_mapping_list(
                struct hv_guest_mapping_flush_list *flush,
                u64 start_gfn, u64 end_gfn);
 
-extern bool hv_root_partition;
-
 #ifdef CONFIG_X86_64
 void hv_apic_init(void);
 void __init hv_init_spinlocks(void);
index 8bd1c01c3310244002db515a650b703dec75b564..40d3656d546165cef9495368621e128addbd5d2e 100644 (file)
 
 /* Is Linux running as the root partition? */
 bool hv_root_partition;
-EXPORT_SYMBOL_GPL(hv_root_partition);
-
 struct ms_hyperv_info ms_hyperv;
-EXPORT_SYMBOL_GPL(ms_hyperv);
 
 #if IS_ENABLED(CONFIG_HYPERV)
 static void (*vmbus_handler)(void);
index 7f42da98d377e0f369ad3661368cf0c65cd57356..e836002bc0ce15feeb2c208a66b6d4b5b1f935af 100644 (file)
 #include <linux/types.h>
 #include <linux/export.h>
 #include <linux/bitfield.h>
+#include <linux/cpumask.h>
+#include <linux/slab.h>
 #include <asm/hyperv-tlfs.h>
 #include <asm/mshyperv.h>
 
+/*
+ * hv_root_partition and ms_hyperv are defined here with other Hyper-V
+ * specific globals so they are shared across all architectures and are
+ * built only when CONFIG_HYPERV is defined.  But on x86,
+ * ms_hyperv_init_platform() is built even when CONFIG_HYPERV is not
+ * defined, and it uses these two variables.  So mark them as __weak
+ * here, allowing for an overriding definition in the module containing
+ * ms_hyperv_init_platform().
+ */
+bool __weak hv_root_partition;
+EXPORT_SYMBOL_GPL(hv_root_partition);
+
+struct ms_hyperv_info __weak ms_hyperv;
+EXPORT_SYMBOL_GPL(ms_hyperv);
+
+u32 *hv_vp_index;
+EXPORT_SYMBOL_GPL(hv_vp_index);
+
+u32 hv_max_vp_index;
+EXPORT_SYMBOL_GPL(hv_max_vp_index);
+
+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);
+
+/*
+ * Hyper-V specific initialization and shutdown code that is
+ * common across all architectures.  Called from architecture
+ * specific initialization functions.
+ */
+
+void __init hv_common_free(void)
+{
+       kfree(hv_vp_index);
+       hv_vp_index = NULL;
+
+       free_percpu(hyperv_pcpu_output_arg);
+       hyperv_pcpu_output_arg = NULL;
+
+       free_percpu(hyperv_pcpu_input_arg);
+       hyperv_pcpu_input_arg = NULL;
+}
+
+int __init hv_common_init(void)
+{
+       int i;
+
+       /*
+        * 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);
+
+       /* 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);
+       }
+
+       hv_vp_index = kmalloc_array(num_possible_cpus(), sizeof(*hv_vp_index),
+                                   GFP_KERNEL);
+       if (!hv_vp_index) {
+               hv_common_free();
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < num_possible_cpus(); i++)
+               hv_vp_index[i] = VP_INVAL;
+
+       return 0;
+}
+
+/*
+ * Hyper-V specific initialization and die code for
+ * individual CPUs that is common across all architectures.
+ * Called by the CPU hotplug mechanism.
+ */
+
+int hv_common_cpu_init(unsigned int cpu)
+{
+       void **inputarg, **outputarg;
+       u64 msr_vp_index;
+       gfp_t flags;
+       int pgcount = hv_root_partition ? 2 : 1;
+
+       /* hv_cpu_init() can be called with IRQs disabled from hv_resume() */
+       flags = irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL;
+
+       inputarg = (void **)this_cpu_ptr(hyperv_pcpu_input_arg);
+       *inputarg = kmalloc(pgcount * HV_HYP_PAGE_SIZE, flags);
+       if (!(*inputarg))
+               return -ENOMEM;
+
+       if (hv_root_partition) {
+               outputarg = (void **)this_cpu_ptr(hyperv_pcpu_output_arg);
+               *outputarg = (char *)(*inputarg) + HV_HYP_PAGE_SIZE;
+       }
+
+       msr_vp_index = hv_get_register(HV_REGISTER_VP_INDEX);
+
+       hv_vp_index[cpu] = msr_vp_index;
+
+       if (msr_vp_index > hv_max_vp_index)
+               hv_max_vp_index = msr_vp_index;
+
+       return 0;
+}
+
+int hv_common_cpu_die(unsigned int cpu)
+{
+       unsigned long flags;
+       void **inputarg, **outputarg;
+       void *mem;
+
+       local_irq_save(flags);
+
+       inputarg = (void **)this_cpu_ptr(hyperv_pcpu_input_arg);
+       mem = *inputarg;
+       *inputarg = NULL;
+
+       if (hv_root_partition) {
+               outputarg = (void **)this_cpu_ptr(hyperv_pcpu_output_arg);
+               *outputarg = NULL;
+       }
+
+       local_irq_restore(flags);
+
+       kfree(mem);
+
+       return 0;
+}
 
 /* Bit mask of the extended capability to query: see HV_EXT_CAPABILITY_xxx */
 bool hv_query_ext_cap(u64 cap_query)
index 2ccb40670552a56d8006a56623565576ce9246c0..60cdff3e22520a98f0dcdff8759b646ba6d5da56 100644 (file)
@@ -39,6 +39,9 @@ struct ms_hyperv_info {
 };
 extern struct ms_hyperv_info ms_hyperv;
 
+extern void  __percpu  **hyperv_pcpu_input_arg;
+extern void  __percpu  **hyperv_pcpu_output_arg;
+
 extern u64 hv_do_hypercall(u64 control, void *inputaddr, void *outputaddr);
 extern u64 hv_do_fast_hypercall8(u16 control, u64 input8);
 
@@ -152,6 +155,8 @@ void hv_remove_crash_handler(void);
 extern int vmbus_interrupt;
 extern int vmbus_irq;
 
+extern bool hv_root_partition;
+
 #if IS_ENABLED(CONFIG_HYPERV)
 /*
  * Hypervisor's notion of virtual processor ID is different from
@@ -165,6 +170,11 @@ extern u32 hv_max_vp_index;
 /* Sentinel value for an uninitialized entry in hv_vp_index array */
 #define VP_INVAL       U32_MAX
 
+int __init hv_common_init(void);
+void __init hv_common_free(void);
+int hv_common_cpu_init(unsigned int cpu);
+int hv_common_cpu_die(unsigned int cpu);
+
 void *hv_alloc_hyperv_page(void);
 void *hv_alloc_hyperv_zeroed_page(void);
 void hv_free_hyperv_page(unsigned long addr);