-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
x86/hyperv: provide a bunch of helper functions
They are used to deposit pages into Microsoft Hypervisor and bring up logical and virtual processors. Signed-off-by: Lillian Grassin-Drake <ligrassi@microsoft.com> Signed-off-by: Sunil Muthuswamy <sunilmut@microsoft.com> Signed-off-by: Nuno Das Neves <nunodasneves@linux.microsoft.com> Co-Developed-by: Lillian Grassin-Drake <ligrassi@microsoft.com> Co-Developed-by: Sunil Muthuswamy <sunilmut@microsoft.com> Co-Developed-by: Nuno Das Neves <nunodasneves@linux.microsoft.com> Signed-off-by: Wei Liu <wei.liu@kernel.org> Reviewed-by: Michael Kelley <mikelley@microsoft.com> Link: https://lore.kernel.org/r/20210203150435.27941-10-wei.liu@kernel.org
- Loading branch information
Wei Liu
committed
Feb 11, 2021
1 parent
4f0455c
commit 86b5ec3
Showing
4 changed files
with
291 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,219 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
#include <linux/types.h> | ||
#include <linux/version.h> | ||
#include <linux/vmalloc.h> | ||
#include <linux/mm.h> | ||
#include <linux/clockchips.h> | ||
#include <linux/acpi.h> | ||
#include <linux/hyperv.h> | ||
#include <linux/slab.h> | ||
#include <linux/cpuhotplug.h> | ||
#include <linux/minmax.h> | ||
#include <asm/hypervisor.h> | ||
#include <asm/mshyperv.h> | ||
#include <asm/apic.h> | ||
|
||
#include <asm/trace/hyperv.h> | ||
|
||
/* | ||
* See struct hv_deposit_memory. The first u64 is partition ID, the rest | ||
* are GPAs. | ||
*/ | ||
#define HV_DEPOSIT_MAX (HV_HYP_PAGE_SIZE / sizeof(u64) - 1) | ||
|
||
/* Deposits exact number of pages. Must be called with interrupts enabled. */ | ||
int hv_call_deposit_pages(int node, u64 partition_id, u32 num_pages) | ||
{ | ||
struct page **pages, *page; | ||
int *counts; | ||
int num_allocations; | ||
int i, j, page_count; | ||
int order; | ||
u64 status; | ||
int ret; | ||
u64 base_pfn; | ||
struct hv_deposit_memory *input_page; | ||
unsigned long flags; | ||
|
||
if (num_pages > HV_DEPOSIT_MAX) | ||
return -E2BIG; | ||
if (!num_pages) | ||
return 0; | ||
|
||
/* One buffer for page pointers and counts */ | ||
page = alloc_page(GFP_KERNEL); | ||
if (!page) | ||
return -ENOMEM; | ||
pages = page_address(page); | ||
|
||
counts = kcalloc(HV_DEPOSIT_MAX, sizeof(int), GFP_KERNEL); | ||
if (!counts) { | ||
free_page((unsigned long)pages); | ||
return -ENOMEM; | ||
} | ||
|
||
/* Allocate all the pages before disabling interrupts */ | ||
i = 0; | ||
|
||
while (num_pages) { | ||
/* Find highest order we can actually allocate */ | ||
order = 31 - __builtin_clz(num_pages); | ||
|
||
while (1) { | ||
pages[i] = alloc_pages_node(node, GFP_KERNEL, order); | ||
if (pages[i]) | ||
break; | ||
if (!order) { | ||
ret = -ENOMEM; | ||
num_allocations = i; | ||
goto err_free_allocations; | ||
} | ||
--order; | ||
} | ||
|
||
split_page(pages[i], order); | ||
counts[i] = 1 << order; | ||
num_pages -= counts[i]; | ||
i++; | ||
} | ||
num_allocations = i; | ||
|
||
local_irq_save(flags); | ||
|
||
input_page = *this_cpu_ptr(hyperv_pcpu_input_arg); | ||
|
||
input_page->partition_id = partition_id; | ||
|
||
/* Populate gpa_page_list - these will fit on the input page */ | ||
for (i = 0, page_count = 0; i < num_allocations; ++i) { | ||
base_pfn = page_to_pfn(pages[i]); | ||
for (j = 0; j < counts[i]; ++j, ++page_count) | ||
input_page->gpa_page_list[page_count] = base_pfn + j; | ||
} | ||
status = hv_do_rep_hypercall(HVCALL_DEPOSIT_MEMORY, | ||
page_count, 0, input_page, NULL); | ||
local_irq_restore(flags); | ||
|
||
if ((status & HV_HYPERCALL_RESULT_MASK) != HV_STATUS_SUCCESS) { | ||
pr_err("Failed to deposit pages: %lld\n", status); | ||
ret = status; | ||
goto err_free_allocations; | ||
} | ||
|
||
ret = 0; | ||
goto free_buf; | ||
|
||
err_free_allocations: | ||
for (i = 0; i < num_allocations; ++i) { | ||
base_pfn = page_to_pfn(pages[i]); | ||
for (j = 0; j < counts[i]; ++j) | ||
__free_page(pfn_to_page(base_pfn + j)); | ||
} | ||
|
||
free_buf: | ||
free_page((unsigned long)pages); | ||
kfree(counts); | ||
return ret; | ||
} | ||
|
||
int hv_call_add_logical_proc(int node, u32 lp_index, u32 apic_id) | ||
{ | ||
struct hv_add_logical_processor_in *input; | ||
struct hv_add_logical_processor_out *output; | ||
u64 status; | ||
unsigned long flags; | ||
int ret = 0; | ||
int pxm = node_to_pxm(node); | ||
|
||
/* | ||
* When adding a logical processor, the hypervisor may return | ||
* HV_STATUS_INSUFFICIENT_MEMORY. When that happens, we deposit more | ||
* pages and retry. | ||
*/ | ||
do { | ||
local_irq_save(flags); | ||
|
||
input = *this_cpu_ptr(hyperv_pcpu_input_arg); | ||
/* We don't do anything with the output right now */ | ||
output = *this_cpu_ptr(hyperv_pcpu_output_arg); | ||
|
||
input->lp_index = lp_index; | ||
input->apic_id = apic_id; | ||
input->flags = 0; | ||
input->proximity_domain_info.domain_id = pxm; | ||
input->proximity_domain_info.flags.reserved = 0; | ||
input->proximity_domain_info.flags.proximity_info_valid = 1; | ||
input->proximity_domain_info.flags.proximity_preferred = 1; | ||
status = hv_do_hypercall(HVCALL_ADD_LOGICAL_PROCESSOR, | ||
input, output); | ||
local_irq_restore(flags); | ||
|
||
status &= HV_HYPERCALL_RESULT_MASK; | ||
|
||
if (status != HV_STATUS_INSUFFICIENT_MEMORY) { | ||
if (status != HV_STATUS_SUCCESS) { | ||
pr_err("%s: cpu %u apic ID %u, %lld\n", __func__, | ||
lp_index, apic_id, status); | ||
ret = status; | ||
} | ||
break; | ||
} | ||
ret = hv_call_deposit_pages(node, hv_current_partition_id, 1); | ||
} while (!ret); | ||
|
||
return ret; | ||
} | ||
|
||
int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags) | ||
{ | ||
struct hv_create_vp *input; | ||
u64 status; | ||
unsigned long irq_flags; | ||
int ret = 0; | ||
int pxm = node_to_pxm(node); | ||
|
||
/* Root VPs don't seem to need pages deposited */ | ||
if (partition_id != hv_current_partition_id) { | ||
/* The value 90 is empirically determined. It may change. */ | ||
ret = hv_call_deposit_pages(node, partition_id, 90); | ||
if (ret) | ||
return ret; | ||
} | ||
|
||
do { | ||
local_irq_save(irq_flags); | ||
|
||
input = *this_cpu_ptr(hyperv_pcpu_input_arg); | ||
|
||
input->partition_id = partition_id; | ||
input->vp_index = vp_index; | ||
input->flags = flags; | ||
input->subnode_type = HvSubnodeAny; | ||
if (node != NUMA_NO_NODE) { | ||
input->proximity_domain_info.domain_id = pxm; | ||
input->proximity_domain_info.flags.reserved = 0; | ||
input->proximity_domain_info.flags.proximity_info_valid = 1; | ||
input->proximity_domain_info.flags.proximity_preferred = 1; | ||
} else { | ||
input->proximity_domain_info.as_uint64 = 0; | ||
} | ||
status = hv_do_hypercall(HVCALL_CREATE_VP, input, NULL); | ||
local_irq_restore(irq_flags); | ||
|
||
status &= HV_HYPERCALL_RESULT_MASK; | ||
|
||
if (status != HV_STATUS_INSUFFICIENT_MEMORY) { | ||
if (status != HV_STATUS_SUCCESS) { | ||
pr_err("%s: vcpu %u, lp %u, %lld\n", __func__, | ||
vp_index, flags, status); | ||
ret = status; | ||
} | ||
break; | ||
} | ||
ret = hv_call_deposit_pages(node, partition_id, 1); | ||
|
||
} while (!ret); | ||
|
||
return ret; | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters