Drivers: hv: Make portions of Hyper-V init code be arch neutral
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>
This commit is contained in:
@@ -39,48 +39,17 @@ EXPORT_SYMBOL_GPL(hv_hypercall_pg);
|
|||||||
/* Storage to save the hypercall page temporarily for hibernation */
|
/* Storage to save the hypercall page temporarily for hibernation */
|
||||||
static void *hv_hypercall_pg_saved;
|
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;
|
struct hv_vp_assist_page **hv_vp_assist_page;
|
||||||
EXPORT_SYMBOL_GPL(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)
|
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()];
|
struct hv_vp_assist_page **hvp = &hv_vp_assist_page[smp_processor_id()];
|
||||||
void **input_arg;
|
int ret;
|
||||||
struct page *pg;
|
|
||||||
|
|
||||||
/* hv_cpu_init() can be called with IRQs disabled from hv_resume() */
|
ret = hv_common_cpu_init(cpu);
|
||||||
pg = alloc_pages(irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL, hv_root_partition ? 1 : 0);
|
if (ret)
|
||||||
if (unlikely(!pg))
|
return ret;
|
||||||
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;
|
|
||||||
|
|
||||||
if (msr_vp_index > hv_max_vp_index)
|
|
||||||
hv_max_vp_index = msr_vp_index;
|
|
||||||
|
|
||||||
if (!hv_vp_assist_page)
|
if (!hv_vp_assist_page)
|
||||||
return 0;
|
return 0;
|
||||||
@@ -198,25 +167,8 @@ static int hv_cpu_die(unsigned int cpu)
|
|||||||
{
|
{
|
||||||
struct hv_reenlightenment_control re_ctrl;
|
struct hv_reenlightenment_control re_ctrl;
|
||||||
unsigned int new_cpu;
|
unsigned int new_cpu;
|
||||||
unsigned long flags;
|
|
||||||
void **input_arg;
|
|
||||||
void *pg;
|
|
||||||
|
|
||||||
local_irq_save(flags);
|
hv_common_cpu_die(cpu);
|
||||||
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);
|
|
||||||
|
|
||||||
if (hv_vp_assist_page && hv_vp_assist_page[cpu])
|
if (hv_vp_assist_page && hv_vp_assist_page[cpu])
|
||||||
wrmsrl(HV_X64_MSR_VP_ASSIST_PAGE, 0);
|
wrmsrl(HV_X64_MSR_VP_ASSIST_PAGE, 0);
|
||||||
@@ -368,7 +320,7 @@ void __init hyperv_init(void)
|
|||||||
{
|
{
|
||||||
u64 guest_id, required_msrs;
|
u64 guest_id, required_msrs;
|
||||||
union hv_x64_msr_hypercall_contents hypercall_msr;
|
union hv_x64_msr_hypercall_contents hypercall_msr;
|
||||||
int cpuhp, i;
|
int cpuhp;
|
||||||
|
|
||||||
if (x86_hyper_type != X86_HYPER_MS_HYPERV)
|
if (x86_hyper_type != X86_HYPER_MS_HYPERV)
|
||||||
return;
|
return;
|
||||||
@@ -380,36 +332,14 @@ void __init hyperv_init(void)
|
|||||||
if ((ms_hyperv.features & required_msrs) != required_msrs)
|
if ((ms_hyperv.features & required_msrs) != required_msrs)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/*
|
if (hv_common_init())
|
||||||
* 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)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (i = 0; i < num_possible_cpus(); i++)
|
|
||||||
hv_vp_index[i] = VP_INVAL;
|
|
||||||
|
|
||||||
hv_vp_assist_page = kcalloc(num_possible_cpus(),
|
hv_vp_assist_page = kcalloc(num_possible_cpus(),
|
||||||
sizeof(*hv_vp_assist_page), GFP_KERNEL);
|
sizeof(*hv_vp_assist_page), GFP_KERNEL);
|
||||||
if (!hv_vp_assist_page) {
|
if (!hv_vp_assist_page) {
|
||||||
ms_hyperv.hints &= ~HV_X64_ENLIGHTENED_VMCS_RECOMMENDED;
|
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",
|
cpuhp = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "x86/hyperv_init:online",
|
||||||
@@ -507,9 +437,8 @@ remove_cpuhp_state:
|
|||||||
free_vp_assist_page:
|
free_vp_assist_page:
|
||||||
kfree(hv_vp_assist_page);
|
kfree(hv_vp_assist_page);
|
||||||
hv_vp_assist_page = NULL;
|
hv_vp_assist_page = NULL;
|
||||||
free_vp_index:
|
common_free:
|
||||||
kfree(hv_vp_index);
|
hv_common_free();
|
||||||
hv_vp_index = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -36,8 +36,6 @@ void hyperv_vector_handler(struct pt_regs *regs);
|
|||||||
extern int hyperv_init_cpuhp;
|
extern int hyperv_init_cpuhp;
|
||||||
|
|
||||||
extern void *hv_hypercall_pg;
|
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;
|
extern u64 hv_current_partition_id;
|
||||||
|
|
||||||
@@ -170,8 +168,6 @@ int hyperv_fill_flush_guest_mapping_list(
|
|||||||
struct hv_guest_mapping_flush_list *flush,
|
struct hv_guest_mapping_flush_list *flush,
|
||||||
u64 start_gfn, u64 end_gfn);
|
u64 start_gfn, u64 end_gfn);
|
||||||
|
|
||||||
extern bool hv_root_partition;
|
|
||||||
|
|
||||||
#ifdef CONFIG_X86_64
|
#ifdef CONFIG_X86_64
|
||||||
void hv_apic_init(void);
|
void hv_apic_init(void);
|
||||||
void __init hv_init_spinlocks(void);
|
void __init hv_init_spinlocks(void);
|
||||||
|
|||||||
@@ -36,10 +36,7 @@
|
|||||||
|
|
||||||
/* Is Linux running as the root partition? */
|
/* Is Linux running as the root partition? */
|
||||||
bool hv_root_partition;
|
bool hv_root_partition;
|
||||||
EXPORT_SYMBOL_GPL(hv_root_partition);
|
|
||||||
|
|
||||||
struct ms_hyperv_info ms_hyperv;
|
struct ms_hyperv_info ms_hyperv;
|
||||||
EXPORT_SYMBOL_GPL(ms_hyperv);
|
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_HYPERV)
|
#if IS_ENABLED(CONFIG_HYPERV)
|
||||||
static void (*vmbus_handler)(void);
|
static void (*vmbus_handler)(void);
|
||||||
|
|||||||
@@ -15,9 +15,147 @@
|
|||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
#include <linux/bitfield.h>
|
#include <linux/bitfield.h>
|
||||||
|
#include <linux/cpumask.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
#include <asm/hyperv-tlfs.h>
|
#include <asm/hyperv-tlfs.h>
|
||||||
#include <asm/mshyperv.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 */
|
/* Bit mask of the extended capability to query: see HV_EXT_CAPABILITY_xxx */
|
||||||
bool hv_query_ext_cap(u64 cap_query)
|
bool hv_query_ext_cap(u64 cap_query)
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ struct ms_hyperv_info {
|
|||||||
};
|
};
|
||||||
extern struct ms_hyperv_info ms_hyperv;
|
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_hypercall(u64 control, void *inputaddr, void *outputaddr);
|
||||||
extern u64 hv_do_fast_hypercall8(u16 control, u64 input8);
|
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_interrupt;
|
||||||
extern int vmbus_irq;
|
extern int vmbus_irq;
|
||||||
|
|
||||||
|
extern bool hv_root_partition;
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_HYPERV)
|
#if IS_ENABLED(CONFIG_HYPERV)
|
||||||
/*
|
/*
|
||||||
* Hypervisor's notion of virtual processor ID is different from
|
* 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 */
|
/* Sentinel value for an uninitialized entry in hv_vp_index array */
|
||||||
#define VP_INVAL U32_MAX
|
#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_page(void);
|
||||||
void *hv_alloc_hyperv_zeroed_page(void);
|
void *hv_alloc_hyperv_zeroed_page(void);
|
||||||
void hv_free_hyperv_page(unsigned long addr);
|
void hv_free_hyperv_page(unsigned long addr);
|
||||||
|
|||||||
Reference in New Issue
Block a user