Commit 4d480dbf authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'hyperv-next-signed-20210426' of...

Merge tag 'hyperv-next-signed-20210426' of git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux

Pull Hyper-V updates from Wei Liu:

 - VMBus enhancement

 - Free page reporting support for Hyper-V balloon driver

 - Some patches for running Linux as Arm64 Hyper-V guest

 - A few misc clean-up patches

* tag 'hyperv-next-signed-20210426' of git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux: (30 commits)
  drivers: hv: Create a consistent pattern for checking Hyper-V hypercall status
  x86/hyperv: Move hv_do_rep_hypercall to asm-generic
  video: hyperv_fb: Add ratelimit on error message
  Drivers: hv: vmbus: Increase wait time for VMbus unload
  Drivers: hv: vmbus: Initialize unload_event statically
  Drivers: hv: vmbus: Check for pending channel interrupts before taking a CPU offline
  Drivers: hv: vmbus: Drivers: hv: vmbus: Introduce CHANNELMSG_MODIFYCHANNEL_RESPONSE
  Drivers: hv: vmbus: Introduce and negotiate VMBus protocol version 5.3
  Drivers: hv: vmbus: Use after free in __vmbus_open()
  Drivers: hv: vmbus: remove unused function
  Drivers: hv: vmbus: Remove unused linux/version.h header
  x86/hyperv: remove unused linux/version.h header
  x86/Hyper-V: Support for free page reporting
  x86/hyperv: Fix unused variable 'hi' warning in hv_apic_read
  x86/hyperv: Fix unused variable 'msr_val' warning in hv_qlock_wait
  hv: hyperv.h: a few mundane typo fixes
  drivers: hv: Fix EXPORT_SYMBOL and tab spaces issue
  Drivers: hv: vmbus: Drop error message when 'No request id available'
  asm-generic/hyperv: Add missing function prototypes per -W1 warnings
  clocksource/drivers/hyper-v: Move handling of STIMER0 interrupts
  ...
parents 8e47c5f0 753ed9c9
...@@ -60,9 +60,11 @@ static u32 hv_apic_read(u32 reg) ...@@ -60,9 +60,11 @@ static u32 hv_apic_read(u32 reg)
switch (reg) { switch (reg) {
case APIC_EOI: case APIC_EOI:
rdmsr(HV_X64_MSR_EOI, reg_val, hi); rdmsr(HV_X64_MSR_EOI, reg_val, hi);
(void)hi;
return reg_val; return reg_val;
case APIC_TASKPRI: case APIC_TASKPRI:
rdmsr(HV_X64_MSR_TPR, reg_val, hi); rdmsr(HV_X64_MSR_TPR, reg_val, hi);
(void)hi;
return reg_val; return reg_val;
default: default:
...@@ -103,7 +105,7 @@ static bool __send_ipi_mask_ex(const struct cpumask *mask, int vector) ...@@ -103,7 +105,7 @@ static bool __send_ipi_mask_ex(const struct cpumask *mask, int vector)
struct hv_send_ipi_ex *ipi_arg; struct hv_send_ipi_ex *ipi_arg;
unsigned long flags; unsigned long flags;
int nr_bank = 0; int nr_bank = 0;
int ret = 1; u64 status = HV_STATUS_INVALID_PARAMETER;
if (!(ms_hyperv.hints & HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED)) if (!(ms_hyperv.hints & HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED))
return false; return false;
...@@ -128,19 +130,19 @@ static bool __send_ipi_mask_ex(const struct cpumask *mask, int vector) ...@@ -128,19 +130,19 @@ static bool __send_ipi_mask_ex(const struct cpumask *mask, int vector)
if (!nr_bank) if (!nr_bank)
ipi_arg->vp_set.format = HV_GENERIC_SET_ALL; ipi_arg->vp_set.format = HV_GENERIC_SET_ALL;
ret = hv_do_rep_hypercall(HVCALL_SEND_IPI_EX, 0, nr_bank, status = hv_do_rep_hypercall(HVCALL_SEND_IPI_EX, 0, nr_bank,
ipi_arg, NULL); ipi_arg, NULL);
ipi_mask_ex_done: ipi_mask_ex_done:
local_irq_restore(flags); local_irq_restore(flags);
return ((ret == 0) ? true : false); return hv_result_success(status);
} }
static bool __send_ipi_mask(const struct cpumask *mask, int vector) static bool __send_ipi_mask(const struct cpumask *mask, int vector)
{ {
int cur_cpu, vcpu; int cur_cpu, vcpu;
struct hv_send_ipi ipi_arg; struct hv_send_ipi ipi_arg;
int ret = 1; u64 status;
trace_hyperv_send_ipi_mask(mask, vector); trace_hyperv_send_ipi_mask(mask, vector);
...@@ -184,9 +186,9 @@ static bool __send_ipi_mask(const struct cpumask *mask, int vector) ...@@ -184,9 +186,9 @@ static bool __send_ipi_mask(const struct cpumask *mask, int vector)
__set_bit(vcpu, (unsigned long *)&ipi_arg.cpu_mask); __set_bit(vcpu, (unsigned long *)&ipi_arg.cpu_mask);
} }
ret = hv_do_fast_hypercall16(HVCALL_SEND_IPI, ipi_arg.vector, status = hv_do_fast_hypercall16(HVCALL_SEND_IPI, ipi_arg.vector,
ipi_arg.cpu_mask); ipi_arg.cpu_mask);
return ((ret == 0) ? true : false); return hv_result_success(status);
do_ex_hypercall: do_ex_hypercall:
return __send_ipi_mask_ex(mask, vector); return __send_ipi_mask_ex(mask, vector);
...@@ -195,6 +197,7 @@ static bool __send_ipi_mask(const struct cpumask *mask, int vector) ...@@ -195,6 +197,7 @@ static bool __send_ipi_mask(const struct cpumask *mask, int vector)
static bool __send_ipi_one(int cpu, int vector) static bool __send_ipi_one(int cpu, int vector)
{ {
int vp = hv_cpu_number_to_vp_number(cpu); int vp = hv_cpu_number_to_vp_number(cpu);
u64 status;
trace_hyperv_send_ipi_one(cpu, vector); trace_hyperv_send_ipi_one(cpu, vector);
...@@ -207,7 +210,8 @@ static bool __send_ipi_one(int cpu, int vector) ...@@ -207,7 +210,8 @@ static bool __send_ipi_one(int cpu, int vector)
if (vp >= 64) if (vp >= 64)
return __send_ipi_mask_ex(cpumask_of(cpu), vector); return __send_ipi_mask_ex(cpumask_of(cpu), vector);
return !hv_do_fast_hypercall16(HVCALL_SEND_IPI, vector, BIT_ULL(vp)); status = hv_do_fast_hypercall16(HVCALL_SEND_IPI, vector, BIT_ULL(vp));
return hv_result_success(status);
} }
static void hv_send_ipi(int cpu, int vector) static void hv_send_ipi(int cpu, int vector)
......
...@@ -54,28 +54,6 @@ EXPORT_SYMBOL_GPL(hyperv_pcpu_output_arg); ...@@ -54,28 +54,6 @@ EXPORT_SYMBOL_GPL(hyperv_pcpu_output_arg);
u32 hv_max_vp_index; u32 hv_max_vp_index;
EXPORT_SYMBOL_GPL(hv_max_vp_index); EXPORT_SYMBOL_GPL(hv_max_vp_index);
void *hv_alloc_hyperv_page(void)
{
BUILD_BUG_ON(PAGE_SIZE != HV_HYP_PAGE_SIZE);
return (void *)__get_free_page(GFP_KERNEL);
}
EXPORT_SYMBOL_GPL(hv_alloc_hyperv_page);
void *hv_alloc_hyperv_zeroed_page(void)
{
BUILD_BUG_ON(PAGE_SIZE != HV_HYP_PAGE_SIZE);
return (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
}
EXPORT_SYMBOL_GPL(hv_alloc_hyperv_zeroed_page);
void hv_free_hyperv_page(unsigned long addr)
{
free_page(addr);
}
EXPORT_SYMBOL_GPL(hv_free_hyperv_page);
static int hv_cpu_init(unsigned int cpu) static int hv_cpu_init(unsigned int cpu)
{ {
u64 msr_vp_index; u64 msr_vp_index;
...@@ -97,7 +75,7 @@ static int hv_cpu_init(unsigned int cpu) ...@@ -97,7 +75,7 @@ static int hv_cpu_init(unsigned int cpu)
*output_arg = page_address(pg + 1); *output_arg = page_address(pg + 1);
} }
hv_get_vp_index(msr_vp_index); msr_vp_index = hv_get_register(HV_REGISTER_VP_INDEX);
hv_vp_index[smp_processor_id()] = msr_vp_index; hv_vp_index[smp_processor_id()] = msr_vp_index;
...@@ -349,7 +327,7 @@ static void __init hv_stimer_setup_percpu_clockev(void) ...@@ -349,7 +327,7 @@ static void __init hv_stimer_setup_percpu_clockev(void)
* Ignore any errors in setting up stimer clockevents * Ignore any errors in setting up stimer clockevents
* as we can run with the LAPIC timer as a fallback. * as we can run with the LAPIC timer as a fallback.
*/ */
(void)hv_stimer_alloc(); (void)hv_stimer_alloc(false);
/* /*
* Still register the LAPIC timer, because the direct-mode STIMER is * Still register the LAPIC timer, because the direct-mode STIMER is
...@@ -369,7 +347,7 @@ static void __init hv_get_partition_id(void) ...@@ -369,7 +347,7 @@ static void __init hv_get_partition_id(void)
local_irq_save(flags); local_irq_save(flags);
output_page = *this_cpu_ptr(hyperv_pcpu_output_arg); output_page = *this_cpu_ptr(hyperv_pcpu_output_arg);
status = hv_do_hypercall(HVCALL_GET_PARTITION_ID, NULL, output_page); status = hv_do_hypercall(HVCALL_GET_PARTITION_ID, NULL, output_page);
if ((status & HV_HYPERCALL_RESULT_MASK) != HV_STATUS_SUCCESS) { if (!hv_result_success(status)) {
/* No point in proceeding if this failed */ /* No point in proceeding if this failed */
pr_err("Failed to get partition ID: %lld\n", status); pr_err("Failed to get partition ID: %lld\n", status);
BUG(); BUG();
...@@ -520,6 +498,8 @@ void __init hyperv_init(void) ...@@ -520,6 +498,8 @@ void __init hyperv_init(void)
x86_init.irqs.create_pci_msi_domain = hv_create_pci_msi_domain; x86_init.irqs.create_pci_msi_domain = hv_create_pci_msi_domain;
#endif #endif
/* Query the VMs extended capability once, so that it can be cached. */
hv_query_ext_cap(0);
return; return;
remove_cpuhp_state: remove_cpuhp_state:
...@@ -593,33 +573,6 @@ void hyperv_report_panic(struct pt_regs *regs, long err, bool in_die) ...@@ -593,33 +573,6 @@ void hyperv_report_panic(struct pt_regs *regs, long err, bool in_die)
} }
EXPORT_SYMBOL_GPL(hyperv_report_panic); EXPORT_SYMBOL_GPL(hyperv_report_panic);
/**
* hyperv_report_panic_msg - report panic message to Hyper-V
* @pa: physical address of the panic page containing the message
* @size: size of the message in the page
*/
void hyperv_report_panic_msg(phys_addr_t pa, size_t size)
{
/*
* P3 to contain the physical address of the panic page & P4 to
* contain the size of the panic data in that page. Rest of the
* registers are no-op when the NOTIFY_MSG flag is set.
*/
wrmsrl(HV_X64_MSR_CRASH_P0, 0);
wrmsrl(HV_X64_MSR_CRASH_P1, 0);
wrmsrl(HV_X64_MSR_CRASH_P2, 0);
wrmsrl(HV_X64_MSR_CRASH_P3, pa);
wrmsrl(HV_X64_MSR_CRASH_P4, size);
/*
* Let Hyper-V know there is crash data available along with
* the panic message.
*/
wrmsrl(HV_X64_MSR_CRASH_CTL,
(HV_CRASH_CTL_CRASH_NOTIFY | HV_CRASH_CTL_CRASH_NOTIFY_MSG));
}
EXPORT_SYMBOL_GPL(hyperv_report_panic_msg);
bool hv_is_hyperv_initialized(void) bool hv_is_hyperv_initialized(void)
{ {
union hv_x64_msr_hypercall_contents hypercall_msr; union hv_x64_msr_hypercall_contents hypercall_msr;
...@@ -650,7 +603,7 @@ EXPORT_SYMBOL_GPL(hv_is_hibernation_supported); ...@@ -650,7 +603,7 @@ EXPORT_SYMBOL_GPL(hv_is_hibernation_supported);
enum hv_isolation_type hv_get_isolation_type(void) enum hv_isolation_type hv_get_isolation_type(void)
{ {
if (!(ms_hyperv.features_b & HV_ISOLATION)) if (!(ms_hyperv.priv_high & HV_ISOLATION))
return HV_ISOLATION_TYPE_NONE; return HV_ISOLATION_TYPE_NONE;
return FIELD_GET(HV_ISOLATION_TYPE, ms_hyperv.isolation_config_b); return FIELD_GET(HV_ISOLATION_TYPE, ms_hyperv.isolation_config_b);
} }
...@@ -661,3 +614,50 @@ bool hv_is_isolation_supported(void) ...@@ -661,3 +614,50 @@ bool hv_is_isolation_supported(void)
return hv_get_isolation_type() != HV_ISOLATION_TYPE_NONE; return hv_get_isolation_type() != HV_ISOLATION_TYPE_NONE;
} }
EXPORT_SYMBOL_GPL(hv_is_isolation_supported); EXPORT_SYMBOL_GPL(hv_is_isolation_supported);
/* Bit mask of the extended capability to query: see HV_EXT_CAPABILITY_xxx */
bool hv_query_ext_cap(u64 cap_query)
{
/*
* The address of the 'hv_extended_cap' variable will be used as an
* output parameter to the hypercall below and so it should be
* compatible with 'virt_to_phys'. Which means, it's address should be
* directly mapped. Use 'static' to keep it compatible; stack variables
* can be virtually mapped, making them imcompatible with
* 'virt_to_phys'.
* Hypercall input/output addresses should also be 8-byte aligned.
*/
static u64 hv_extended_cap __aligned(8);
static bool hv_extended_cap_queried;
u64 status;
/*
* Querying extended capabilities is an extended hypercall. Check if the
* partition supports extended hypercall, first.
*/
if (!(ms_hyperv.priv_high & HV_ENABLE_EXTENDED_HYPERCALLS))
return false;
/* Extended capabilities do not change at runtime. */
if (hv_extended_cap_queried)
return hv_extended_cap & cap_query;
status = hv_do_hypercall(HV_EXT_CALL_QUERY_CAPABILITIES, NULL,
&hv_extended_cap);
/*
* The query extended capabilities hypercall should not fail under
* any normal circumstances. Avoid repeatedly making the hypercall, on
* error.
*/
hv_extended_cap_queried = true;
status &= HV_HYPERCALL_RESULT_MASK;
if (status != HV_STATUS_SUCCESS) {
pr_err("Hyper-V: Extended query capabilities hypercall failed 0x%llx\n",
status);
return false;
}
return hv_extended_cap & cap_query;
}
EXPORT_SYMBOL_GPL(hv_query_ext_cap);
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
#include <linux/types.h> #include <linux/types.h>
#include <linux/version.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/clockchips.h> #include <linux/clockchips.h>
...@@ -93,10 +92,9 @@ int hv_call_deposit_pages(int node, u64 partition_id, u32 num_pages) ...@@ -93,10 +92,9 @@ int hv_call_deposit_pages(int node, u64 partition_id, u32 num_pages)
status = hv_do_rep_hypercall(HVCALL_DEPOSIT_MEMORY, status = hv_do_rep_hypercall(HVCALL_DEPOSIT_MEMORY,
page_count, 0, input_page, NULL); page_count, 0, input_page, NULL);
local_irq_restore(flags); local_irq_restore(flags);
if (!hv_result_success(status)) {
if ((status & HV_HYPERCALL_RESULT_MASK) != HV_STATUS_SUCCESS) {
pr_err("Failed to deposit pages: %lld\n", status); pr_err("Failed to deposit pages: %lld\n", status);
ret = status; ret = hv_result(status);
goto err_free_allocations; goto err_free_allocations;
} }
...@@ -122,7 +120,7 @@ int hv_call_add_logical_proc(int node, u32 lp_index, u32 apic_id) ...@@ -122,7 +120,7 @@ int hv_call_add_logical_proc(int node, u32 lp_index, u32 apic_id)
struct hv_add_logical_processor_out *output; struct hv_add_logical_processor_out *output;
u64 status; u64 status;
unsigned long flags; unsigned long flags;
int ret = 0; int ret = HV_STATUS_SUCCESS;
int pxm = node_to_pxm(node); int pxm = node_to_pxm(node);
/* /*
...@@ -148,13 +146,11 @@ int hv_call_add_logical_proc(int node, u32 lp_index, u32 apic_id) ...@@ -148,13 +146,11 @@ int hv_call_add_logical_proc(int node, u32 lp_index, u32 apic_id)
input, output); input, output);
local_irq_restore(flags); local_irq_restore(flags);
status &= HV_HYPERCALL_RESULT_MASK; if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) {
if (!hv_result_success(status)) {
if (status != HV_STATUS_INSUFFICIENT_MEMORY) {
if (status != HV_STATUS_SUCCESS) {
pr_err("%s: cpu %u apic ID %u, %lld\n", __func__, pr_err("%s: cpu %u apic ID %u, %lld\n", __func__,
lp_index, apic_id, status); lp_index, apic_id, status);
ret = status; ret = hv_result(status);
} }
break; break;
} }
...@@ -169,7 +165,7 @@ int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags) ...@@ -169,7 +165,7 @@ int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags)
struct hv_create_vp *input; struct hv_create_vp *input;
u64 status; u64 status;
unsigned long irq_flags; unsigned long irq_flags;
int ret = 0; int ret = HV_STATUS_SUCCESS;
int pxm = node_to_pxm(node); int pxm = node_to_pxm(node);
/* Root VPs don't seem to need pages deposited */ /* Root VPs don't seem to need pages deposited */
...@@ -200,13 +196,11 @@ int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags) ...@@ -200,13 +196,11 @@ int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags)
status = hv_do_hypercall(HVCALL_CREATE_VP, input, NULL); status = hv_do_hypercall(HVCALL_CREATE_VP, input, NULL);
local_irq_restore(irq_flags); local_irq_restore(irq_flags);
status &= HV_HYPERCALL_RESULT_MASK; if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) {
if (!hv_result_success(status)) {
if (status != HV_STATUS_INSUFFICIENT_MEMORY) {
if (status != HV_STATUS_SUCCESS) {
pr_err("%s: vcpu %u, lp %u, %lld\n", __func__, pr_err("%s: vcpu %u, lp %u, %lld\n", __func__,
vp_index, flags, status); vp_index, flags, status);
ret = status; ret = hv_result(status);
} }
break; break;
} }
......
...@@ -25,7 +25,6 @@ static void hv_qlock_kick(int cpu) ...@@ -25,7 +25,6 @@ static void hv_qlock_kick(int cpu)
static void hv_qlock_wait(u8 *byte, u8 val) static void hv_qlock_wait(u8 *byte, u8 val)
{ {
unsigned long msr_val;
unsigned long flags; unsigned long flags;
if (in_nmi()) if (in_nmi())
...@@ -48,8 +47,13 @@ static void hv_qlock_wait(u8 *byte, u8 val) ...@@ -48,8 +47,13 @@ static void hv_qlock_wait(u8 *byte, u8 val)
/* /*
* Only issue the rdmsrl() when the lock state has not changed. * Only issue the rdmsrl() when the lock state has not changed.
*/ */
if (READ_ONCE(*byte) == val) if (READ_ONCE(*byte) == val) {
unsigned long msr_val;
rdmsrl(HV_X64_MSR_GUEST_IDLE, msr_val); rdmsrl(HV_X64_MSR_GUEST_IDLE, msr_val);
(void)msr_val;
}
local_irq_restore(flags); local_irq_restore(flags);
} }
......
...@@ -63,10 +63,10 @@ static int hv_map_interrupt(union hv_device_id device_id, bool level, ...@@ -63,10 +63,10 @@ static int hv_map_interrupt(union hv_device_id device_id, bool level,
local_irq_restore(flags); local_irq_restore(flags);
if ((status & HV_HYPERCALL_RESULT_MASK) != HV_STATUS_SUCCESS) if (!hv_result_success(status))
pr_err("%s: hypercall failed, status %lld\n", __func__, status); pr_err("%s: hypercall failed, status %lld\n", __func__, status);
return status & HV_HYPERCALL_RESULT_MASK; return hv_result(status);
} }
static int hv_unmap_interrupt(u64 id, struct hv_interrupt_entry *old_entry) static int hv_unmap_interrupt(u64 id, struct hv_interrupt_entry *old_entry)
...@@ -88,7 +88,7 @@ static int hv_unmap_interrupt(u64 id, struct hv_interrupt_entry *old_entry) ...@@ -88,7 +88,7 @@ static int hv_unmap_interrupt(u64 id, struct hv_interrupt_entry *old_entry)
status = hv_do_hypercall(HVCALL_UNMAP_DEVICE_INTERRUPT, input, NULL); status = hv_do_hypercall(HVCALL_UNMAP_DEVICE_INTERRUPT, input, NULL);
local_irq_restore(flags); local_irq_restore(flags);
return status & HV_HYPERCALL_RESULT_MASK; return hv_result(status);
} }
#ifdef CONFIG_PCI_MSI #ifdef CONFIG_PCI_MSI
......
...@@ -58,7 +58,7 @@ static void hyperv_flush_tlb_others(const struct cpumask *cpus, ...@@ -58,7 +58,7 @@ static void hyperv_flush_tlb_others(const struct cpumask *cpus,
int cpu, vcpu, gva_n, max_gvas; int cpu, vcpu, gva_n, max_gvas;
struct hv_tlb_flush **flush_pcpu; struct hv_tlb_flush **flush_pcpu;
struct hv_tlb_flush *flush; struct hv_tlb_flush *flush;
u64 status = U64_MAX; u64 status;
unsigned long flags; unsigned long flags;
trace_hyperv_mmu_flush_tlb_others(cpus, info); trace_hyperv_mmu_flush_tlb_others(cpus, info);
...@@ -161,7 +161,7 @@ static void hyperv_flush_tlb_others(const struct cpumask *cpus, ...@@ -161,7 +161,7 @@ static void hyperv_flush_tlb_others(const struct cpumask *cpus,
check_status: check_status:
local_irq_restore(flags); local_irq_restore(flags);
if (!(status & HV_HYPERCALL_RESULT_MASK)) if (hv_result_success(status))
return; return;
do_native: do_native:
native_flush_tlb_others(cpus, info); native_flush_tlb_others(cpus, info);
...@@ -176,7 +176,7 @@ static u64 hyperv_flush_tlb_others_ex(const struct cpumask *cpus, ...@@ -176,7 +176,7 @@ static u64 hyperv_flush_tlb_others_ex(const struct cpumask *cpus,
u64 status; u64 status;
if (!(ms_hyperv.hints & HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED)) if (!(ms_hyperv.hints & HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED))
return U64_MAX; return HV_STATUS_INVALID_PARAMETER;
flush_pcpu = (struct hv_tlb_flush_ex **) flush_pcpu = (struct hv_tlb_flush_ex **)
this_cpu_ptr(hyperv_pcpu_input_arg); this_cpu_ptr(hyperv_pcpu_input_arg);
...@@ -201,7 +201,7 @@ static u64 hyperv_flush_tlb_others_ex(const struct cpumask *cpus, ...@@ -201,7 +201,7 @@ static u64 hyperv_flush_tlb_others_ex(const struct cpumask *cpus,
flush->hv_vp_set.format = HV_GENERIC_SET_SPARSE_4K; flush->hv_vp_set.format = HV_GENERIC_SET_SPARSE_4K;
nr_bank = cpumask_to_vpset(&(flush->hv_vp_set), cpus); nr_bank = cpumask_to_vpset(&(flush->hv_vp_set), cpus);
if (nr_bank < 0) if (nr_bank < 0)
return U64_MAX; return HV_STATUS_INVALID_PARAMETER;
/* /*
* We can flush not more than max_gvas with one hypercall. Flush the * We can flush not more than max_gvas with one hypercall. Flush the
......
...@@ -47,7 +47,7 @@ int hyperv_flush_guest_mapping(u64 as) ...@@ -47,7 +47,7 @@ int hyperv_flush_guest_mapping(u64 as)
flush, NULL); flush, NULL);
local_irq_restore(flags); local_irq_restore(flags);
if (!(status & HV_HYPERCALL_RESULT_MASK)) if (hv_result_success(status))
ret = 0; ret = 0;
fault: fault:
...@@ -92,7 +92,7 @@ int hyperv_flush_guest_mapping_range(u64 as, ...@@ -92,7 +92,7 @@ int hyperv_flush_guest_mapping_range(u64 as,
{ {
struct hv_guest_mapping_flush_list **flush_pcpu; struct hv_guest_mapping_flush_list **flush_pcpu;
struct hv_guest_mapping_flush_list *flush; struct hv_guest_mapping_flush_list *flush;
u64 status = 0; u64 status;
unsigned long flags; unsigned long flags;
int ret = -ENOTSUPP; int ret = -ENOTSUPP;
int gpa_n = 0; int gpa_n = 0;
...@@ -125,10 +125,10 @@ int hyperv_flush_guest_mapping_range(u64 as, ...@@ -125,10 +125,10 @@ int hyperv_flush_guest_mapping_range(u64 as,
local_irq_restore(flags); local_irq_restore(flags);
if (!(status & HV_HYPERCALL_RESULT_MASK)) if (hv_result_success(status))
ret = 0; ret = 0;
else else
ret = status; ret = hv_result(status);
fault: fault:
trace_hyperv_nested_flush_guest_mapping_range(as, ret); trace_hyperv_nested_flush_guest_mapping_range(as, ret);
return ret; return ret;
......
...@@ -156,7 +156,7 @@ enum hv_isolation_type { ...@@ -156,7 +156,7 @@ enum hv_isolation_type {
#define HV_X64_MSR_HYPERCALL 0x40000001 #define HV_X64_MSR_HYPERCALL 0x40000001
/* MSR used to provide vcpu index */ /* MSR used to provide vcpu index */
#define HV_X64_MSR_VP_INDEX 0x40000002 #define HV_REGISTER_VP_INDEX 0x40000002
/* MSR used to reset the guest OS. */ /* MSR used to reset the guest OS. */
#define HV_X64_MSR_RESET 0x40000003 #define HV_X64_MSR_RESET 0x40000003
...@@ -165,10 +165,10 @@ enum hv_isolation_type { ...@@ -165,10 +165,10 @@ enum hv_isolation_type {
#define HV_X64_MSR_VP_RUNTIME 0x40000010 #define HV_X64_MSR_VP_RUNTIME 0x40000010
/* MSR used to read the per-partition time reference counter */ /* MSR used to read the per-partition time reference counter */
#define HV_X64_MSR_TIME_REF_COUNT 0x40000020 #define HV_REGISTER_TIME_REF_COUNT 0x40000020
/* A partition's reference time stamp counter (TSC) page */ /* A partition's reference time stamp counter (TSC) page */
#define HV_X64_MSR_REFERENCE_TSC 0x40000021 #define HV_REGISTER_REFERENCE_TSC 0x40000021
/* MSR used to retrieve the TSC frequency */ /* MSR used to retrieve the TSC frequency */
#define HV_X64_MSR_TSC_FREQUENCY 0x40000022 #define HV_X64_MSR_TSC_FREQUENCY 0x40000022
...@@ -183,50 +183,50 @@ enum hv_isolation_type { ...@@ -183,50 +183,50 @@ enum hv_isolation_type {
#define HV_X64_MSR_VP_ASSIST_PAGE 0x40000073 #define HV_X64_MSR_VP_ASSIST_PAGE 0x40000073
/* Define synthetic interrupt controller model specific registers. */ /* Define synthetic interrupt controller model specific registers. */
#define HV_X64_MSR_SCONTROL 0x40000080 #define HV_REGISTER_SCONTROL 0x40000080
#define HV_X64_MSR_SVERSION 0x40000081 #define HV_REGISTER_SVERSION 0x40000081
#define HV_X64_MSR_SIEFP 0x40000082 #define HV_REGISTER_SIEFP 0x40000082
#define HV_X64_MSR_SIMP 0x40000083 #define HV_REGISTER_SIMP 0x40000083
#define HV_X64_MSR_EOM 0x40000084 #define HV_REGISTER_EOM 0x40000084
#define HV_X64_MSR_SINT0 0x40000090 #define HV_REGISTER_SINT0 0x40000090
#define HV_X64_MSR_SINT1 0x40000091 #define HV_REGISTER_SINT1 0x40000091
#define HV_X64_MSR_SINT2 0x40000092 #define HV_REGISTER_SINT2 0x40000092
#define HV_X64_MSR_SINT3 0x40000093 #define HV_REGISTER_SINT3 0x40000093
#define HV_X64_MSR_SINT4 0x40000094 #define HV_REGISTER_SINT4 0x40000094
#define HV_X64_MSR_SINT5 0x40000095 #define HV_REGISTER_SINT5 0x40000095
#define HV_X64_MSR_SINT6 0x40000096 #define HV_REGISTER_SINT6 0x40000096
#define HV_X64_MSR_SINT7 0x40000097 #define HV_REGISTER_SINT7 0x40000097
#define HV_X64_MSR_SINT8 0x40000098 #define HV_REGISTER_SINT8 0x40000098
#define HV_X64_MSR_SINT9 0x40000099 #define HV_REGISTER_SINT9 0x40000099
#define HV_X64_MSR_SINT10 0x4000009A #define HV_REGISTER_SINT10 0x4000009A
#define HV_X64_MSR_SINT11 0x4000009B #define HV_REGISTER_SINT11 0x4000009B
#define HV_X64_MSR_SINT12 0x4000009C #define HV_REGISTER_SINT12 0x4000009C
#define HV_X64_MSR_SINT13 0x4000009D #define HV_REGISTER_SINT13 0x4000009D
#define HV_X64_MSR_SINT14 0x4000009E #define HV_REGISTER_SINT14 0x4000009E
#define HV_X64_MSR_SINT15 0x4000009F #define HV_REGISTER_SINT15 0x4000009F
/* /*
* Synthetic Timer MSRs. Four timers per vcpu. * Synthetic Timer MSRs. Four timers per vcpu.
*/ */
#define HV_X64_MSR_STIMER0_CONFIG 0x400000B0 #define HV_REGISTER_STIMER0_CONFIG 0x400000B0
#define HV_X64_MSR_STIMER0_COUNT 0x400000B1 #define HV_REGISTER_STIMER0_COUNT 0x400000B1
#define HV_X64_MSR_STIMER1_CONFIG 0x400000B2 #define HV_REGISTER_STIMER1_CONFIG 0x400000B2
#define HV_X64_MSR_STIMER1_COUNT 0x400000B3 #define HV_REGISTER_STIMER1_COUNT 0x400000B3
#define HV_X64_MSR_STIMER2_CONFIG 0x400000B4 #define HV_REGISTER_STIMER2_CONFIG 0x400000B4
#define HV_X64_MSR_STIMER2_COUNT 0x400000B5 #define HV_REGISTER_STIMER2_COUNT 0x400000B5
#define HV_X64_MSR_STIMER3_CONFIG 0x400000B6 #define HV_REGISTER_STIMER3_CONFIG 0x400000B6
#define HV_X64_MSR_STIMER3_COUNT 0x400000B7 #define HV_REGISTER_STIMER3_COUNT 0x400000B7
/* Hyper-V guest idle MSR */ /* Hyper-V guest idle MSR */
#define HV_X64_MSR_GUEST_IDLE 0x400000F0 #define HV_X64_MSR_GUEST_IDLE 0x400000F0
/* Hyper-V guest crash notification MSR's */ /* Hyper-V guest crash notification MSR's */
#define HV_X64_MSR_CRASH_P0 0x40000100 #define HV_REGISTER_CRASH_P0 0x40000100
#define HV_X64_MSR_CRASH_P1 0x40000101 #define HV_REGISTER_CRASH_P1 0x40000101
#define HV_X64_MSR_CRASH_P2 0x40000102 #define HV_REGISTER_CRASH_P2 0x40000102
#define HV_X64_MSR_CRASH_P3 0x40000103 #define HV_REGISTER_CRASH_P3 0x40000103
#define HV_X64_MSR_CRASH_P4 0x40000104 #define HV_REGISTER_CRASH_P4 0x40000104
#define HV_X64_MSR_CRASH_CTL 0x40000105 #define HV_REGISTER_CRASH_CTL 0x40000105
/* TSC emulation after migration */ /* TSC emulation after migration */
#define HV_X64_MSR_REENLIGHTENMENT_CONTROL 0x40000106 #define HV_X64_MSR_REENLIGHTENMENT_CONTROL 0x40000106
...@@ -236,6 +236,32 @@ enum hv_isolation_type { ...@@ -236,6 +236,32 @@ enum hv_isolation_type {
/* TSC invariant control */ /* TSC invariant control */
#define HV_X64_MSR_TSC_INVARIANT_CONTROL 0x40000118 #define HV_X64_MSR_TSC_INVARIANT_CONTROL 0x40000118
/* Register name aliases for temporary compatibility */
#define HV_X64_MSR_STIMER0_COUNT HV_REGISTER_STIMER0_COUNT
#define HV_X64_MSR_STIMER0_CONFIG HV_REGISTER_STIMER0_CONFIG
#define HV_X64_MSR_STIMER1_COUNT HV_REGISTER_STIMER1_COUNT
#define HV_X64_MSR_STIMER1_CONFIG HV_REGISTER_STIMER1_CONFIG
#define HV_X64_MSR_STIMER2_COUNT HV_REGISTER_STIMER2_COUNT
#define HV_X64_MSR_STIMER2_CONFIG HV_REGISTER_STIMER2_CONFIG
#define HV_X64_MSR_STIMER3_COUNT HV_REGISTER_STIMER3_COUNT
#define HV_X64_MSR_STIMER3_CONFIG HV_REGISTER_STIMER3_CONFIG
#define HV_X64_MSR_SCONTROL HV_REGISTER_SCONTROL
#define HV_X64_MSR_SVERSION HV_REGISTER_SVERSION
#define HV_X64_MSR_SIMP HV_REGISTER_SIMP
#define HV_X64_MSR_SIEFP HV_REGISTER_SIEFP
#define HV_X64_MSR_VP_INDEX HV_REGISTER_VP_INDEX
#define HV_X64_MSR_EOM HV_REGISTER_EOM
#define HV_X64_MSR_SINT0 HV_REGISTER_SINT0
#define HV_X64_MSR_SINT15 HV_REGISTER_SINT15
#define HV_X64_MSR_CRASH_P0 HV_REGISTER_CRASH_P0
#define HV_X64_MSR_CRASH_P1 HV_REGISTER_CRASH_P1
#define HV_X64_MSR_CRASH_P2 HV_REGISTER_CRASH_P2
#define HV_X64_MSR_CRASH_P3 HV_REGISTER_CRASH_P3
#define HV_X64_MSR_CRASH_P4 HV_REGISTER_CRASH_P4
#define HV_X64_MSR_CRASH_CTL HV_REGISTER_CRASH_CTL
#define HV_X64_MSR_TIME_REF_COUNT HV_REGISTER_TIME_REF_COUNT
#define HV_X64_MSR_REFERENCE_TSC HV_REGISTER_REFERENCE_TSC
/* /*
* Declare the MSR used to setup pages used to communicate with the hypervisor. * Declare the MSR used to setup pages used to communicate with the hypervisor.
*/ */
...@@ -288,35 +314,6 @@ struct hv_tsc_emulation_status { ...@@ -288,35 +314,6 @@ struct hv_tsc_emulation_status {
#define HV_X64_MSR_TSC_REFERENCE_ENABLE 0x00000001 #define HV_X64_MSR_TSC_REFERENCE_ENABLE 0x00000001
#define HV_X64_MSR_TSC_REFERENCE_ADDRESS_SHIFT 12 #define HV_X64_MSR_TSC_REFERENCE_ADDRESS_SHIFT 12
/* Define hypervisor message types. */
enum hv_message_type {
HVMSG_NONE = 0x00000000,
/* Memory access messages. */
HVMSG_UNMAPPED_GPA = 0x80000000,
HVMSG_GPA_INTERCEPT = 0x80000001,
/* Timer notification messages. */
HVMSG_TIMER_EXPIRED = 0x80000010,
/* Error messages. */
HVMSG_INVALID_VP_REGISTER_VALUE = 0x80000020,
HVMSG_UNRECOVERABLE_EXCEPTION = 0x80000021,
HVMSG_UNSUPPORTED_FEATURE = 0x80000022,
/* Trace buffer complete messages. */
HVMSG_EVENTLOG_BUFFERCOMPLETE = 0x80000040,
/* Platform-specific processor intercept messages. */
HVMSG_X64_IOPORT_INTERCEPT = 0x80010000,
HVMSG_X64_MSR_INTERCEPT = 0x80010001,
HVMSG_X64_CPUID_INTERCEPT = 0x80010002,
HVMSG_X64_EXCEPTION_INTERCEPT = 0x80010003,
HVMSG_X64_APIC_EOI = 0x80010004,
HVMSG_X64_LEGACY_FP_ERROR = 0x80010005
};
struct hv_nested_enlightenments_control { struct hv_nested_enlightenments_control {
struct { struct {
__u32 directhypercall:1; __u32 directhypercall:1;
......
...@@ -9,70 +9,29 @@ ...@@ -9,70 +9,29 @@
#include <asm/hyperv-tlfs.h> #include <asm/hyperv-tlfs.h>
#include <asm/nospec-branch.h> #include <asm/nospec-branch.h>
#include <asm/paravirt.h> #include <asm/paravirt.h>
#include <asm/mshyperv.h>
typedef int (*hyperv_fill_flush_list_func)( typedef int (*hyperv_fill_flush_list_func)(
struct hv_guest_mapping_flush_list *flush, struct hv_guest_mapping_flush_list *flush,
void *data); void *data);
#define hv_init_timer(timer, tick) \ static inline void hv_set_register(unsigned int reg, u64 value)
wrmsrl(HV_X64_MSR_STIMER0_COUNT + (2*timer), tick) {
#define hv_init_timer_config(timer, val) \ wrmsrl(reg, value);
wrmsrl(HV_X64_MSR_STIMER0_CONFIG + (2*timer), val) }
#define hv_get_simp(val) rdmsrl(HV_X64_MSR_SIMP, val)
#define hv_set_simp(val) wrmsrl(HV_X64_MSR_SIMP, val)
#define hv_get_siefp(val) rdmsrl(HV_X64_MSR_SIEFP, val)
#define hv_set_siefp(val) wrmsrl(HV_X64_MSR_SIEFP, val)
#define hv_get_synic_state(val) rdmsrl(HV_X64_MSR_SCONTROL, val)
#define hv_set_synic_state(val) wrmsrl(HV_X64_MSR_SCONTROL, val)
#define hv_get_vp_index(index) rdmsrl(HV_X64_MSR_VP_INDEX, index)
#define hv_signal_eom() wrmsrl(HV_X64_MSR_EOM, 0)
#define hv_get_synint_state(int_num, val) \
rdmsrl(HV_X64_MSR_SINT0 + int_num, val)
#define hv_set_synint_state(int_num, val) \
wrmsrl(HV_X64_MSR_SINT0 + int_num, val)
#define hv_recommend_using_aeoi() \
(!(ms_hyperv.hints & HV_DEPRECATING_AEOI_RECOMMENDED))
#define hv_get_crash_ctl(val) \ static inline u64 hv_get_register(unsigned int reg)
rdmsrl(HV_X64_MSR_CRASH_CTL, val) {
u64 value;
#define hv_get_time_ref_count(val) \ rdmsrl(reg, value);
rdmsrl(HV_X64_MSR_TIME_REF_COUNT, val) return value;
}
#define hv_get_reference_tsc(val) \
rdmsrl(HV_X64_MSR_REFERENCE_TSC, val)
#define hv_set_reference_tsc(val) \
wrmsrl(HV_X64_MSR_REFERENCE_TSC, val)
#define hv_set_clocksource_vdso(val) \
((val).vdso_clock_mode = VDSO_CLOCKMODE_HVCLOCK)
#define hv_enable_vdso_clocksource() \
vclocks_set_used(VDSO_CLOCKMODE_HVCLOCK);
#define hv_get_raw_timer() rdtsc_ordered() #define hv_get_raw_timer() rdtsc_ordered()
#define hv_get_vector() HYPERVISOR_CALLBACK_VECTOR
/*
* Reference to pv_ops must be inline so objtool
* detection of noinstr violations can work correctly.
*/
static __always_inline void hv_setup_sched_clock(void *sched_clock)
{
#ifdef CONFIG_PARAVIRT
paravirt_set_sched_clock(sched_clock);
#endif
}
void hyperv_vector_handler(struct pt_regs *regs); void hyperv_vector_handler(struct pt_regs *regs);
static inline void hv_enable_stimer0_percpu_irq(int irq) {}
static inline void hv_disable_stimer0_percpu_irq(int irq) {}
#if IS_ENABLED(CONFIG_HYPERV) #if IS_ENABLED(CONFIG_HYPERV)
extern int hyperv_init_cpuhp; extern int hyperv_init_cpuhp;
...@@ -189,38 +148,6 @@ static inline u64 hv_do_fast_hypercall16(u16 code, u64 input1, u64 input2) ...@@ -189,38 +148,6 @@ static inline u64 hv_do_fast_hypercall16(u16 code, u64 input1, u64 input2)
return hv_status; return hv_status;
} }
/*
* Rep hypercalls. Callers of this functions are supposed to ensure that
* rep_count and varhead_size comply with Hyper-V hypercall definition.
*/
static inline u64 hv_do_rep_hypercall(u16 code, u16 rep_count, u16 varhead_size,
void *input, void *output)
{
u64 control = code;
u64 status;
u16 rep_comp;
control |= (u64)varhead_size << HV_HYPERCALL_VARHEAD_OFFSET;
control |= (u64)rep_count << HV_HYPERCALL_REP_COMP_OFFSET;
do {
status = hv_do_hypercall(control, input, output);
if ((status & HV_HYPERCALL_RESULT_MASK) != HV_STATUS_SUCCESS)
return status;
/* Bits 32-43 of status have 'Reps completed' data. */
rep_comp = (status & HV_HYPERCALL_REP_COMP_MASK) >>
HV_HYPERCALL_REP_COMP_OFFSET;
control &= ~HV_HYPERCALL_REP_START_MASK;
control |= (u64)rep_comp << HV_HYPERCALL_REP_START_OFFSET;
touch_nmi_watchdog();
} while (rep_comp < rep_count);
return status;
}
extern struct hv_vp_assist_page **hv_vp_assist_page; extern struct hv_vp_assist_page **hv_vp_assist_page;
static inline struct hv_vp_assist_page *hv_get_vp_assist_page(unsigned int cpu) static inline struct hv_vp_assist_page *hv_get_vp_assist_page(unsigned int cpu)
...@@ -233,9 +160,6 @@ static inline struct hv_vp_assist_page *hv_get_vp_assist_page(unsigned int cpu) ...@@ -233,9 +160,6 @@ static inline struct hv_vp_assist_page *hv_get_vp_assist_page(unsigned int cpu)
void __init hyperv_init(void); void __init hyperv_init(void);
void hyperv_setup_mmu_ops(void); void hyperv_setup_mmu_ops(void);
void *hv_alloc_hyperv_page(void);
void *hv_alloc_hyperv_zeroed_page(void);
void hv_free_hyperv_page(unsigned long addr);
void set_hv_tscchange_cb(void (*cb)(void)); void set_hv_tscchange_cb(void (*cb)(void));
void clear_hv_tscchange_cb(void); void clear_hv_tscchange_cb(void);
void hyperv_stop_tsc_emulation(void); void hyperv_stop_tsc_emulation(void);
...@@ -272,8 +196,6 @@ int hv_unmap_ioapic_interrupt(int ioapic_id, struct hv_interrupt_entry *entry); ...@@ -272,8 +196,6 @@ int hv_unmap_ioapic_interrupt(int ioapic_id, struct hv_interrupt_entry *entry);
#else /* CONFIG_HYPERV */ #else /* CONFIG_HYPERV */
static inline void hyperv_init(void) {} static inline void hyperv_init(void) {}
static inline void hyperv_setup_mmu_ops(void) {} static inline void hyperv_setup_mmu_ops(void) {}
static inline void *hv_alloc_hyperv_page(void) { return NULL; }
static inline void hv_free_hyperv_page(unsigned long addr) {}
static inline void set_hv_tscchange_cb(void (*cb)(void)) {} static inline void set_hv_tscchange_cb(void (*cb)(void)) {}
static inline void clear_hv_tscchange_cb(void) {} static inline void clear_hv_tscchange_cb(void) {}
static inline void hyperv_stop_tsc_emulation(void) {}; static inline void hyperv_stop_tsc_emulation(void) {};
......
...@@ -60,23 +60,18 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_hyperv_callback) ...@@ -60,23 +60,18 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_hyperv_callback)
set_irq_regs(old_regs); set_irq_regs(old_regs);
} }
int hv_setup_vmbus_irq(int irq, void (*handler)(void)) void hv_setup_vmbus_handler(void (*handler)(void))
{ {
/*
* The 'irq' argument is ignored on x86/x64 because a hard-coded
* interrupt vector is used for Hyper-V interrupts.
*/
vmbus_handler = handler; vmbus_handler = handler;
return 0;
} }
EXPORT_SYMBOL_GPL(hv_setup_vmbus_handler);
void hv_remove_vmbus_irq(void) void hv_remove_vmbus_handler(void)
{ {
/* We have no way to deallocate the interrupt gate */ /* We have no way to deallocate the interrupt gate */
vmbus_handler = NULL; vmbus_handler = NULL;
} }
EXPORT_SYMBOL_GPL(hv_setup_vmbus_irq); EXPORT_SYMBOL_GPL(hv_remove_vmbus_handler);
EXPORT_SYMBOL_GPL(hv_remove_vmbus_irq);
/* /*
* Routines to do per-architecture handling of stimer0 * Routines to do per-architecture handling of stimer0
...@@ -95,21 +90,17 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_hyperv_stimer0) ...@@ -95,21 +90,17 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_hyperv_stimer0)
set_irq_regs(old_regs); set_irq_regs(old_regs);
} }
int hv_setup_stimer0_irq(int *irq, int *vector, void (*handler)(void)) /* For x86/x64, override weak placeholders in hyperv_timer.c */
void hv_setup_stimer0_handler(void (*handler)(void))
{ {
*vector = HYPERV_STIMER0_VECTOR;
*irq = -1; /* Unused on x86/x64 */
hv_stimer0_handler = handler; hv_stimer0_handler = handler;
return 0;
} }
EXPORT_SYMBOL_GPL(hv_setup_stimer0_irq);
void hv_remove_stimer0_irq(int irq) void hv_remove_stimer0_handler(void)
{ {
/* We have no way to deallocate the interrupt gate */ /* We have no way to deallocate the interrupt gate */
hv_stimer0_handler = NULL; hv_stimer0_handler = NULL;
} }
EXPORT_SYMBOL_GPL(hv_remove_stimer0_irq);
void hv_setup_kexec_handler(void (*handler)(void)) void hv_setup_kexec_handler(void (*handler)(void))
{ {
...@@ -274,12 +265,13 @@ static void __init ms_hyperv_init_platform(void) ...@@ -274,12 +265,13 @@ static void __init ms_hyperv_init_platform(void)
* Extract the features and hints * Extract the features and hints
*/ */
ms_hyperv.features = cpuid_eax(HYPERV_CPUID_FEATURES); ms_hyperv.features = cpuid_eax(HYPERV_CPUID_FEATURES);
ms_hyperv.features_b = cpuid_ebx(HYPERV_CPUID_FEATURES); ms_hyperv.priv_high = cpuid_ebx(HYPERV_CPUID_FEATURES);
ms_hyperv.misc_features = cpuid_edx(HYPERV_CPUID_FEATURES); ms_hyperv.misc_features = cpuid_edx(HYPERV_CPUID_FEATURES);
ms_hyperv.hints = cpuid_eax(HYPERV_CPUID_ENLIGHTMENT_INFO); ms_hyperv.hints = cpuid_eax(HYPERV_CPUID_ENLIGHTMENT_INFO);
pr_info("Hyper-V: features 0x%x, hints 0x%x, misc 0x%x\n", pr_info("Hyper-V: privilege flags low 0x%x, high 0x%x, hints 0x%x, misc 0x%x\n",
ms_hyperv.features, ms_hyperv.hints, ms_hyperv.misc_features); ms_hyperv.features, ms_hyperv.priv_high, ms_hyperv.hints,
ms_hyperv.misc_features);
ms_hyperv.max_vp_index = cpuid_eax(HYPERV_CPUID_IMPLEMENT_LIMITS); ms_hyperv.max_vp_index = cpuid_eax(HYPERV_CPUID_IMPLEMENT_LIMITS);
ms_hyperv.max_lp_index = cpuid_ebx(HYPERV_CPUID_IMPLEMENT_LIMITS); ms_hyperv.max_lp_index = cpuid_ebx(HYPERV_CPUID_IMPLEMENT_LIMITS);
...@@ -325,7 +317,7 @@ static void __init ms_hyperv_init_platform(void) ...@@ -325,7 +317,7 @@ static void __init ms_hyperv_init_platform(void)
x86_platform.calibrate_cpu = hv_get_tsc_khz; x86_platform.calibrate_cpu = hv_get_tsc_khz;
} }
if (ms_hyperv.features_b & HV_ISOLATION) { if (ms_hyperv.priv_high & HV_ISOLATION) {
ms_hyperv.isolation_config_a = cpuid_eax(HYPERV_CPUID_ISOLATION_CONFIG); ms_hyperv.isolation_config_a = cpuid_eax(HYPERV_CPUID_ISOLATION_CONFIG);
ms_hyperv.isolation_config_b = cpuid_ebx(HYPERV_CPUID_ISOLATION_CONFIG); ms_hyperv.isolation_config_b = cpuid_ebx(HYPERV_CPUID_ISOLATION_CONFIG);
......
This diff is collapsed.
...@@ -23,6 +23,7 @@ config HYPERV_UTILS ...@@ -23,6 +23,7 @@ config HYPERV_UTILS
config HYPERV_BALLOON config HYPERV_BALLOON
tristate "Microsoft Hyper-V Balloon driver" tristate "Microsoft Hyper-V Balloon driver"
depends on HYPERV depends on HYPERV
select PAGE_REPORTING
help help
Select this option to enable Hyper-V Balloon driver. Select this option to enable Hyper-V Balloon driver.
......
...@@ -209,31 +209,96 @@ int vmbus_send_tl_connect_request(const guid_t *shv_guest_servie_id, ...@@ -209,31 +209,96 @@ int vmbus_send_tl_connect_request(const guid_t *shv_guest_servie_id,
} }
EXPORT_SYMBOL_GPL(vmbus_send_tl_connect_request); EXPORT_SYMBOL_GPL(vmbus_send_tl_connect_request);
static int send_modifychannel_without_ack(struct vmbus_channel *channel, u32 target_vp)
{
struct vmbus_channel_modifychannel msg;
int ret;
memset(&msg, 0, sizeof(msg));
msg.header.msgtype = CHANNELMSG_MODIFYCHANNEL;
msg.child_relid = channel->offermsg.child_relid;
msg.target_vp = target_vp;
ret = vmbus_post_msg(&msg, sizeof(msg), true);
trace_vmbus_send_modifychannel(&msg, ret);
return ret;
}
static int send_modifychannel_with_ack(struct vmbus_channel *channel, u32 target_vp)
{
struct vmbus_channel_modifychannel *msg;
struct vmbus_channel_msginfo *info;
unsigned long flags;
int ret;
info = kzalloc(sizeof(struct vmbus_channel_msginfo) +
sizeof(struct vmbus_channel_modifychannel),
GFP_KERNEL);
if (!info)
return -ENOMEM;
init_completion(&info->waitevent);
info->waiting_channel = channel;
msg = (struct vmbus_channel_modifychannel *)info->msg;
msg->header.msgtype = CHANNELMSG_MODIFYCHANNEL;
msg->child_relid = channel->offermsg.child_relid;
msg->target_vp = target_vp;
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
list_add_tail(&info->msglistentry, &vmbus_connection.chn_msg_list);
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
ret = vmbus_post_msg(msg, sizeof(*msg), true);
trace_vmbus_send_modifychannel(msg, ret);
if (ret != 0) {
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
list_del(&info->msglistentry);
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
goto free_info;
}
/*
* Release channel_mutex; otherwise, vmbus_onoffer_rescind() could block on
* the mutex and be unable to signal the completion.
*
* See the caller target_cpu_store() for information about the usage of the
* mutex.
*/
mutex_unlock(&vmbus_connection.channel_mutex);
wait_for_completion(&info->waitevent);
mutex_lock(&vmbus_connection.channel_mutex);
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
list_del(&info->msglistentry);
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
if (info->response.modify_response.status)
ret = -EAGAIN;
free_info:
kfree(info);
return ret;
}
/* /*
* Set/change the vCPU (@target_vp) the channel (@child_relid) will interrupt. * Set/change the vCPU (@target_vp) the channel (@child_relid) will interrupt.
* *
* CHANNELMSG_MODIFYCHANNEL messages are aynchronous. Also, Hyper-V does not * CHANNELMSG_MODIFYCHANNEL messages are aynchronous. When VMbus version 5.3
* ACK such messages. IOW we can't know when the host will stop interrupting * or later is negotiated, Hyper-V always sends an ACK in response to such a
* the "old" vCPU and start interrupting the "new" vCPU for the given channel. * message. For VMbus version 5.2 and earlier, it never sends an ACK. With-
* out an ACK, we can not know when the host will stop interrupting the "old"
* vCPU and start interrupting the "new" vCPU for the given channel.
* *
* The CHANNELMSG_MODIFYCHANNEL message type is supported since VMBus version * The CHANNELMSG_MODIFYCHANNEL message type is supported since VMBus version
* VERSION_WIN10_V4_1. * VERSION_WIN10_V4_1.
*/ */
int vmbus_send_modifychannel(u32 child_relid, u32 target_vp) int vmbus_send_modifychannel(struct vmbus_channel *channel, u32 target_vp)
{ {
struct vmbus_channel_modifychannel conn_msg; if (vmbus_proto_version >= VERSION_WIN10_V5_3)
int ret; return send_modifychannel_with_ack(channel, target_vp);
return send_modifychannel_without_ack(channel, target_vp);
memset(&conn_msg, 0, sizeof(conn_msg));
conn_msg.header.msgtype = CHANNELMSG_MODIFYCHANNEL;
conn_msg.child_relid = child_relid;
conn_msg.target_vp = target_vp;
ret = vmbus_post_msg(&conn_msg, sizeof(conn_msg), true);
trace_vmbus_send_modifychannel(&conn_msg, ret);
return ret;
} }
EXPORT_SYMBOL_GPL(vmbus_send_modifychannel); EXPORT_SYMBOL_GPL(vmbus_send_modifychannel);
...@@ -385,7 +450,7 @@ static int create_gpadl_header(enum hv_gpadl_type type, void *kbuffer, ...@@ -385,7 +450,7 @@ static int create_gpadl_header(enum hv_gpadl_type type, void *kbuffer,
* @kbuffer: from kmalloc or vmalloc * @kbuffer: from kmalloc or vmalloc
* @size: page-size multiple * @size: page-size multiple
* @send_offset: the offset (in bytes) where the send ring buffer starts, * @send_offset: the offset (in bytes) where the send ring buffer starts,
* should be 0 for BUFFER type gpadl * should be 0 for BUFFER type gpadl
* @gpadl_handle: some funky thing * @gpadl_handle: some funky thing
*/ */
static int __vmbus_establish_gpadl(struct vmbus_channel *channel, static int __vmbus_establish_gpadl(struct vmbus_channel *channel,
...@@ -653,7 +718,7 @@ static int __vmbus_open(struct vmbus_channel *newchannel, ...@@ -653,7 +718,7 @@ static int __vmbus_open(struct vmbus_channel *newchannel,
if (newchannel->rescind) { if (newchannel->rescind) {
err = -ENODEV; err = -ENODEV;
goto error_free_info; goto error_clean_msglist;
} }
err = vmbus_post_msg(open_msg, err = vmbus_post_msg(open_msg,
......
...@@ -333,7 +333,6 @@ bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, u8 *buf, ...@@ -333,7 +333,6 @@ bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, u8 *buf,
negop->icversion_data[1].minor = icmsg_minor; negop->icversion_data[1].minor = icmsg_minor;
return found_match; return found_match;
} }
EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp); EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp);
/* /*
...@@ -593,10 +592,10 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) ...@@ -593,10 +592,10 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
* CPUS_READ_UNLOCK CPUS_WRITE_UNLOCK * CPUS_READ_UNLOCK CPUS_WRITE_UNLOCK
* *
* Forbids: CPU1's LOAD from *not* seing CPU2's STORE && * Forbids: CPU1's LOAD from *not* seing CPU2's STORE &&
* CPU2's SEARCH from *not* seeing CPU1's INSERT * CPU2's SEARCH from *not* seeing CPU1's INSERT
* *
* Forbids: CPU2's SEARCH from seeing CPU1's INSERT && * Forbids: CPU2's SEARCH from seeing CPU1's INSERT &&
* CPU2's LOAD from *not* seing CPU1's STORE * CPU2's LOAD from *not* seing CPU1's STORE
*/ */
cpus_read_lock(); cpus_read_lock();
...@@ -756,6 +755,12 @@ static void init_vp_index(struct vmbus_channel *channel) ...@@ -756,6 +755,12 @@ static void init_vp_index(struct vmbus_channel *channel)
free_cpumask_var(available_mask); free_cpumask_var(available_mask);
} }
#define UNLOAD_DELAY_UNIT_MS 10 /* 10 milliseconds */
#define UNLOAD_WAIT_MS (100*1000) /* 100 seconds */
#define UNLOAD_WAIT_LOOPS (UNLOAD_WAIT_MS/UNLOAD_DELAY_UNIT_MS)
#define UNLOAD_MSG_MS (5*1000) /* Every 5 seconds */
#define UNLOAD_MSG_LOOPS (UNLOAD_MSG_MS/UNLOAD_DELAY_UNIT_MS)
static void vmbus_wait_for_unload(void) static void vmbus_wait_for_unload(void)
{ {
int cpu; int cpu;
...@@ -773,12 +778,17 @@ static void vmbus_wait_for_unload(void) ...@@ -773,12 +778,17 @@ static void vmbus_wait_for_unload(void)
* vmbus_connection.unload_event. If not, the last thing we can do is * vmbus_connection.unload_event. If not, the last thing we can do is
* read message pages for all CPUs directly. * read message pages for all CPUs directly.
* *
* Wait no more than 10 seconds so that the panic path can't get * Wait up to 100 seconds since an Azure host must writeback any dirty
* hung forever in case the response message isn't seen. * data in its disk cache before the VMbus UNLOAD request will
* complete. This flushing has been empirically observed to take up
* to 50 seconds in cases with a lot of dirty data, so allow additional
* leeway and for inaccuracies in mdelay(). But eventually time out so
* that the panic path can't get hung forever in case the response
* message isn't seen.
*/ */
for (i = 0; i < 1000; i++) { for (i = 1; i <= UNLOAD_WAIT_LOOPS; i++) {
if (completion_done(&vmbus_connection.unload_event)) if (completion_done(&vmbus_connection.unload_event))
break; goto completed;
for_each_online_cpu(cpu) { for_each_online_cpu(cpu) {
struct hv_per_cpu_context *hv_cpu struct hv_per_cpu_context *hv_cpu
...@@ -801,9 +811,18 @@ static void vmbus_wait_for_unload(void) ...@@ -801,9 +811,18 @@ static void vmbus_wait_for_unload(void)
vmbus_signal_eom(msg, message_type); vmbus_signal_eom(msg, message_type);
} }
mdelay(10); /*
* Give a notice periodically so someone watching the
* serial output won't think it is completely hung.
*/
if (!(i % UNLOAD_MSG_LOOPS))
pr_notice("Waiting for VMBus UNLOAD to complete\n");
mdelay(UNLOAD_DELAY_UNIT_MS);
} }
pr_err("Continuing even though VMBus UNLOAD did not complete\n");
completed:
/* /*
* We're crashing and already got the UNLOAD_RESPONSE, cleanup all * We're crashing and already got the UNLOAD_RESPONSE, cleanup all
* maybe-pending messages on all CPUs to be able to receive new * maybe-pending messages on all CPUs to be able to receive new
...@@ -827,6 +846,11 @@ static void vmbus_unload_response(struct vmbus_channel_message_header *hdr) ...@@ -827,6 +846,11 @@ static void vmbus_unload_response(struct vmbus_channel_message_header *hdr)
/* /*
* This is a global event; just wakeup the waiting thread. * This is a global event; just wakeup the waiting thread.
* Once we successfully unload, we can cleanup the monitor state. * Once we successfully unload, we can cleanup the monitor state.
*
* NB. A malicious or compromised Hyper-V could send a spurious
* message of type CHANNELMSG_UNLOAD_RESPONSE, and trigger a call
* of the complete() below. Make sure that unload_event has been
* initialized by the time this complete() is executed.
*/ */
complete(&vmbus_connection.unload_event); complete(&vmbus_connection.unload_event);
} }
...@@ -842,7 +866,7 @@ void vmbus_initiate_unload(bool crash) ...@@ -842,7 +866,7 @@ void vmbus_initiate_unload(bool crash)
if (vmbus_proto_version < VERSION_WIN8_1) if (vmbus_proto_version < VERSION_WIN8_1)
return; return;
init_completion(&vmbus_connection.unload_event); reinit_completion(&vmbus_connection.unload_event);
memset(&hdr, 0, sizeof(struct vmbus_channel_message_header)); memset(&hdr, 0, sizeof(struct vmbus_channel_message_header));
hdr.msgtype = CHANNELMSG_UNLOAD; hdr.msgtype = CHANNELMSG_UNLOAD;
vmbus_post_msg(&hdr, sizeof(struct vmbus_channel_message_header), vmbus_post_msg(&hdr, sizeof(struct vmbus_channel_message_header),
...@@ -980,7 +1004,7 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr) ...@@ -980,7 +1004,7 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
* UNLOCK channel_mutex * UNLOCK channel_mutex
* *
* Forbids: r1 == valid_relid && * Forbids: r1 == valid_relid &&
* channels[valid_relid] == channel * channels[valid_relid] == channel
* *
* Note. r1 can be INVALID_RELID only for an hv_sock channel. * Note. r1 can be INVALID_RELID only for an hv_sock channel.
* None of the hv_sock channels which were present before the * None of the hv_sock channels which were present before the
...@@ -1312,6 +1336,46 @@ static void vmbus_ongpadl_created(struct vmbus_channel_message_header *hdr) ...@@ -1312,6 +1336,46 @@ static void vmbus_ongpadl_created(struct vmbus_channel_message_header *hdr)
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
} }
/*
* vmbus_onmodifychannel_response - Modify Channel response handler.
*
* This is invoked when we received a response to our channel modify request.
* Find the matching request, copy the response and signal the requesting thread.
*/
static void vmbus_onmodifychannel_response(struct vmbus_channel_message_header *hdr)
{
struct vmbus_channel_modifychannel_response *response;
struct vmbus_channel_msginfo *msginfo;
unsigned long flags;
response = (struct vmbus_channel_modifychannel_response *)hdr;
trace_vmbus_onmodifychannel_response(response);
/*
* Find the modify msg, copy the response and signal/unblock the wait event.
*/
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list, msglistentry) {
struct vmbus_channel_message_header *responseheader =
(struct vmbus_channel_message_header *)msginfo->msg;
if (responseheader->msgtype == CHANNELMSG_MODIFYCHANNEL) {
struct vmbus_channel_modifychannel *modifymsg;
modifymsg = (struct vmbus_channel_modifychannel *)msginfo->msg;
if (modifymsg->child_relid == response->child_relid) {
memcpy(&msginfo->response.modify_response, response,
sizeof(*response));
complete(&msginfo->waitevent);
break;
}
}
}
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
}
/* /*
* vmbus_ongpadl_torndown - GPADL torndown handler. * vmbus_ongpadl_torndown - GPADL torndown handler.
* *
...@@ -1429,6 +1493,8 @@ channel_message_table[CHANNELMSG_COUNT] = { ...@@ -1429,6 +1493,8 @@ channel_message_table[CHANNELMSG_COUNT] = {
{ CHANNELMSG_TL_CONNECT_REQUEST, 0, NULL, 0}, { CHANNELMSG_TL_CONNECT_REQUEST, 0, NULL, 0},
{ CHANNELMSG_MODIFYCHANNEL, 0, NULL, 0}, { CHANNELMSG_MODIFYCHANNEL, 0, NULL, 0},
{ CHANNELMSG_TL_CONNECT_RESULT, 0, NULL, 0}, { CHANNELMSG_TL_CONNECT_RESULT, 0, NULL, 0},
{ CHANNELMSG_MODIFYCHANNEL_RESPONSE, 1, vmbus_onmodifychannel_response,
sizeof(struct vmbus_channel_modifychannel_response)},
}; };
/* /*
......
...@@ -26,9 +26,11 @@ ...@@ -26,9 +26,11 @@
struct vmbus_connection vmbus_connection = { struct vmbus_connection vmbus_connection = {
.conn_state = DISCONNECTED, .conn_state = DISCONNECTED,
.unload_event = COMPLETION_INITIALIZER(
vmbus_connection.unload_event),
.next_gpadl_handle = ATOMIC_INIT(0xE1E10), .next_gpadl_handle = ATOMIC_INIT(0xE1E10),
.ready_for_suspend_event= COMPLETION_INITIALIZER( .ready_for_suspend_event = COMPLETION_INITIALIZER(
vmbus_connection.ready_for_suspend_event), vmbus_connection.ready_for_suspend_event),
.ready_for_resume_event = COMPLETION_INITIALIZER( .ready_for_resume_event = COMPLETION_INITIALIZER(
vmbus_connection.ready_for_resume_event), vmbus_connection.ready_for_resume_event),
...@@ -45,6 +47,7 @@ EXPORT_SYMBOL_GPL(vmbus_proto_version); ...@@ -45,6 +47,7 @@ EXPORT_SYMBOL_GPL(vmbus_proto_version);
* Table of VMBus versions listed from newest to oldest. * Table of VMBus versions listed from newest to oldest.
*/ */
static __u32 vmbus_versions[] = { static __u32 vmbus_versions[] = {
VERSION_WIN10_V5_3,
VERSION_WIN10_V5_2, VERSION_WIN10_V5_2,
VERSION_WIN10_V5_1, VERSION_WIN10_V5_1,
VERSION_WIN10_V5, VERSION_WIN10_V5,
...@@ -60,7 +63,7 @@ static __u32 vmbus_versions[] = { ...@@ -60,7 +63,7 @@ static __u32 vmbus_versions[] = {
* Maximal VMBus protocol version guests can negotiate. Useful to cap the * Maximal VMBus protocol version guests can negotiate. Useful to cap the
* VMBus version for testing and debugging purpose. * VMBus version for testing and debugging purpose.
*/ */
static uint max_version = VERSION_WIN10_V5_2; static uint max_version = VERSION_WIN10_V5_3;
module_param(max_version, uint, S_IRUGO); module_param(max_version, uint, S_IRUGO);
MODULE_PARM_DESC(max_version, MODULE_PARM_DESC(max_version,
......
...@@ -13,9 +13,10 @@ ...@@ -13,9 +13,10 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/hyperv.h> #include <linux/hyperv.h>
#include <linux/version.h>
#include <linux/random.h> #include <linux/random.h>
#include <linux/clockchips.h> #include <linux/clockchips.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <clocksource/hyperv_timer.h> #include <clocksource/hyperv_timer.h>
#include <asm/mshyperv.h> #include <asm/mshyperv.h>
#include "hyperv_vmbus.h" #include "hyperv_vmbus.h"
...@@ -36,6 +37,42 @@ int hv_init(void) ...@@ -36,6 +37,42 @@ int hv_init(void)
return 0; return 0;
} }
/*
* Functions for allocating and freeing memory with size and
* alignment HV_HYP_PAGE_SIZE. These functions are needed because
* the guest page size may not be the same as the Hyper-V page
* size. We depend upon kmalloc() aligning power-of-two size
* allocations to the allocation size boundary, so that the
* allocated memory appears to Hyper-V as a page of the size
* it expects.
*/
void *hv_alloc_hyperv_page(void)
{
BUILD_BUG_ON(PAGE_SIZE < HV_HYP_PAGE_SIZE);
if (PAGE_SIZE == HV_HYP_PAGE_SIZE)
return (void *)__get_free_page(GFP_KERNEL);
else
return kmalloc(HV_HYP_PAGE_SIZE, GFP_KERNEL);
}
void *hv_alloc_hyperv_zeroed_page(void)
{
if (PAGE_SIZE == HV_HYP_PAGE_SIZE)
return (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
else
return kzalloc(HV_HYP_PAGE_SIZE, GFP_KERNEL);
}
void hv_free_hyperv_page(unsigned long addr)
{
if (PAGE_SIZE == HV_HYP_PAGE_SIZE)
free_page(addr);
else
kfree((void *)addr);
}
/* /*
* hv_post_message - Post a message using the hypervisor message IPC. * hv_post_message - Post a message using the hypervisor message IPC.
* *
...@@ -68,7 +105,7 @@ int hv_post_message(union hv_connection_id connection_id, ...@@ -68,7 +105,7 @@ int hv_post_message(union hv_connection_id connection_id,
*/ */
put_cpu_ptr(hv_cpu); put_cpu_ptr(hv_cpu);
return status & 0xFFFF; return hv_result(status);
} }
int hv_synic_alloc(void) int hv_synic_alloc(void)
...@@ -162,34 +199,48 @@ void hv_synic_enable_regs(unsigned int cpu) ...@@ -162,34 +199,48 @@ void hv_synic_enable_regs(unsigned int cpu)
union hv_synic_scontrol sctrl; union hv_synic_scontrol sctrl;
/* Setup the Synic's message page */ /* Setup the Synic's message page */
hv_get_simp(simp.as_uint64); simp.as_uint64 = hv_get_register(HV_REGISTER_SIMP);
simp.simp_enabled = 1; simp.simp_enabled = 1;
simp.base_simp_gpa = virt_to_phys(hv_cpu->synic_message_page) simp.base_simp_gpa = virt_to_phys(hv_cpu->synic_message_page)
>> HV_HYP_PAGE_SHIFT; >> HV_HYP_PAGE_SHIFT;
hv_set_simp(simp.as_uint64); hv_set_register(HV_REGISTER_SIMP, simp.as_uint64);
/* Setup the Synic's event page */ /* Setup the Synic's event page */
hv_get_siefp(siefp.as_uint64); siefp.as_uint64 = hv_get_register(HV_REGISTER_SIEFP);
siefp.siefp_enabled = 1; siefp.siefp_enabled = 1;
siefp.base_siefp_gpa = virt_to_phys(hv_cpu->synic_event_page) siefp.base_siefp_gpa = virt_to_phys(hv_cpu->synic_event_page)
>> HV_HYP_PAGE_SHIFT; >> HV_HYP_PAGE_SHIFT;
hv_set_siefp(siefp.as_uint64); hv_set_register(HV_REGISTER_SIEFP, siefp.as_uint64);
/* Setup the shared SINT. */ /* Setup the shared SINT. */
hv_get_synint_state(VMBUS_MESSAGE_SINT, shared_sint.as_uint64); if (vmbus_irq != -1)
enable_percpu_irq(vmbus_irq, 0);
shared_sint.as_uint64 = hv_get_register(HV_REGISTER_SINT0 +
VMBUS_MESSAGE_SINT);
shared_sint.vector = hv_get_vector(); shared_sint.vector = vmbus_interrupt;
shared_sint.masked = false; shared_sint.masked = false;
shared_sint.auto_eoi = hv_recommend_using_aeoi();
hv_set_synint_state(VMBUS_MESSAGE_SINT, shared_sint.as_uint64); /*
* On architectures where Hyper-V doesn't support AEOI (e.g., ARM64),
* it doesn't provide a recommendation flag and AEOI must be disabled.
*/
#ifdef HV_DEPRECATING_AEOI_RECOMMENDED
shared_sint.auto_eoi =
!(ms_hyperv.hints & HV_DEPRECATING_AEOI_RECOMMENDED);
#else
shared_sint.auto_eoi = 0;
#endif
hv_set_register(HV_REGISTER_SINT0 + VMBUS_MESSAGE_SINT,
shared_sint.as_uint64);
/* Enable the global synic bit */ /* Enable the global synic bit */
hv_get_synic_state(sctrl.as_uint64); sctrl.as_uint64 = hv_get_register(HV_REGISTER_SCONTROL);
sctrl.enable = 1; sctrl.enable = 1;
hv_set_synic_state(sctrl.as_uint64); hv_set_register(HV_REGISTER_SCONTROL, sctrl.as_uint64);
} }
int hv_synic_init(unsigned int cpu) int hv_synic_init(unsigned int cpu)
...@@ -211,30 +262,71 @@ void hv_synic_disable_regs(unsigned int cpu) ...@@ -211,30 +262,71 @@ void hv_synic_disable_regs(unsigned int cpu)
union hv_synic_siefp siefp; union hv_synic_siefp siefp;
union hv_synic_scontrol sctrl; union hv_synic_scontrol sctrl;
hv_get_synint_state(VMBUS_MESSAGE_SINT, shared_sint.as_uint64); shared_sint.as_uint64 = hv_get_register(HV_REGISTER_SINT0 +
VMBUS_MESSAGE_SINT);
shared_sint.masked = 1; shared_sint.masked = 1;
/* Need to correctly cleanup in the case of SMP!!! */ /* Need to correctly cleanup in the case of SMP!!! */
/* Disable the interrupt */ /* Disable the interrupt */
hv_set_synint_state(VMBUS_MESSAGE_SINT, shared_sint.as_uint64); hv_set_register(HV_REGISTER_SINT0 + VMBUS_MESSAGE_SINT,
shared_sint.as_uint64);
hv_get_simp(simp.as_uint64); simp.as_uint64 = hv_get_register(HV_REGISTER_SIMP);
simp.simp_enabled = 0; simp.simp_enabled = 0;
simp.base_simp_gpa = 0; simp.base_simp_gpa = 0;
hv_set_simp(simp.as_uint64); hv_set_register(HV_REGISTER_SIMP, simp.as_uint64);
hv_get_siefp(siefp.as_uint64); siefp.as_uint64 = hv_get_register(HV_REGISTER_SIEFP);
siefp.siefp_enabled = 0; siefp.siefp_enabled = 0;
siefp.base_siefp_gpa = 0; siefp.base_siefp_gpa = 0;
hv_set_siefp(siefp.as_uint64); hv_set_register(HV_REGISTER_SIEFP, siefp.as_uint64);
/* Disable the global synic bit */ /* Disable the global synic bit */
hv_get_synic_state(sctrl.as_uint64); sctrl.as_uint64 = hv_get_register(HV_REGISTER_SCONTROL);
sctrl.enable = 0; sctrl.enable = 0;
hv_set_synic_state(sctrl.as_uint64); hv_set_register(HV_REGISTER_SCONTROL, sctrl.as_uint64);
if (vmbus_irq != -1)
disable_percpu_irq(vmbus_irq);
}
#define HV_MAX_TRIES 3
/*
* Scan the event flags page of 'this' CPU looking for any bit that is set. If we find one
* bit set, then wait for a few milliseconds. Repeat these steps for a maximum of 3 times.
* Return 'true', if there is still any set bit after this operation; 'false', otherwise.
*
* If a bit is set, that means there is a pending channel interrupt. The expectation is
* that the normal interrupt handling mechanism will find and process the channel interrupt
* "very soon", and in the process clear the bit.
*/
static bool hv_synic_event_pending(void)
{
struct hv_per_cpu_context *hv_cpu = this_cpu_ptr(hv_context.cpu_context);
union hv_synic_event_flags *event =
(union hv_synic_event_flags *)hv_cpu->synic_event_page + VMBUS_MESSAGE_SINT;
unsigned long *recv_int_page = event->flags; /* assumes VMBus version >= VERSION_WIN8 */
bool pending;
u32 relid;
int tries = 0;
retry:
pending = false;
for_each_set_bit(relid, recv_int_page, HV_EVENT_FLAGS_COUNT) {
/* Special case - VMBus channel protocol messages */
if (relid == 0)
continue;
pending = true;
break;
}
if (pending && tries++ < HV_MAX_TRIES) {
usleep_range(10000, 20000);
goto retry;
}
return pending;
} }
int hv_synic_cleanup(unsigned int cpu) int hv_synic_cleanup(unsigned int cpu)
...@@ -242,6 +334,9 @@ int hv_synic_cleanup(unsigned int cpu) ...@@ -242,6 +334,9 @@ int hv_synic_cleanup(unsigned int cpu)
struct vmbus_channel *channel, *sc; struct vmbus_channel *channel, *sc;
bool channel_found = false; bool channel_found = false;
if (vmbus_connection.conn_state != CONNECTED)
goto always_cleanup;
/* /*
* Hyper-V does not provide a way to change the connect CPU once * Hyper-V does not provide a way to change the connect CPU once
* it is set; we must prevent the connect CPU from going offline * it is set; we must prevent the connect CPU from going offline
...@@ -249,8 +344,7 @@ int hv_synic_cleanup(unsigned int cpu) ...@@ -249,8 +344,7 @@ int hv_synic_cleanup(unsigned int cpu)
* path where the vmbus is already disconnected, the CPU must be * path where the vmbus is already disconnected, the CPU must be
* allowed to shut down. * allowed to shut down.
*/ */
if (cpu == VMBUS_CONNECT_CPU && if (cpu == VMBUS_CONNECT_CPU)
vmbus_connection.conn_state == CONNECTED)
return -EBUSY; return -EBUSY;
/* /*
...@@ -277,9 +371,21 @@ int hv_synic_cleanup(unsigned int cpu) ...@@ -277,9 +371,21 @@ int hv_synic_cleanup(unsigned int cpu)
} }
mutex_unlock(&vmbus_connection.channel_mutex); mutex_unlock(&vmbus_connection.channel_mutex);
if (channel_found && vmbus_connection.conn_state == CONNECTED) if (channel_found)
return -EBUSY;
/*
* channel_found == false means that any channels that were previously
* assigned to the CPU have been reassigned elsewhere with a call of
* vmbus_send_modifychannel(). Scan the event flags page looking for
* bits that are set and waiting with a timeout for vmbus_chan_sched()
* to process such bits. If bits are still set after this operation
* and VMBus is connected, fail the CPU offlining operation.
*/
if (vmbus_proto_version >= VERSION_WIN10_V4_1 && hv_synic_event_pending())
return -EBUSY; return -EBUSY;
always_cleanup:
hv_stimer_legacy_cleanup(cpu); hv_stimer_legacy_cleanup(cpu);
hv_synic_disable_regs(cpu); hv_synic_disable_regs(cpu);
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/memory.h> #include <linux/memory.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/percpu_counter.h> #include <linux/percpu_counter.h>
#include <linux/page_reporting.h>
#include <linux/hyperv.h> #include <linux/hyperv.h>
#include <asm/hyperv-tlfs.h> #include <asm/hyperv-tlfs.h>
...@@ -563,6 +564,8 @@ struct hv_dynmem_device { ...@@ -563,6 +564,8 @@ struct hv_dynmem_device {
* The negotiated version agreed by host. * The negotiated version agreed by host.
*/ */
__u32 version; __u32 version;
struct page_reporting_dev_info pr_dev_info;
}; };
static struct hv_dynmem_device dm_device; static struct hv_dynmem_device dm_device;
...@@ -1568,6 +1571,89 @@ static void balloon_onchannelcallback(void *context) ...@@ -1568,6 +1571,89 @@ static void balloon_onchannelcallback(void *context)
} }
/* Hyper-V only supports reporting 2MB pages or higher */
#define HV_MIN_PAGE_REPORTING_ORDER 9
#define HV_MIN_PAGE_REPORTING_LEN (HV_HYP_PAGE_SIZE << HV_MIN_PAGE_REPORTING_ORDER)
static int hv_free_page_report(struct page_reporting_dev_info *pr_dev_info,
struct scatterlist *sgl, unsigned int nents)
{
unsigned long flags;
struct hv_memory_hint *hint;
int i;
u64 status;
struct scatterlist *sg;
WARN_ON_ONCE(nents > HV_MEMORY_HINT_MAX_GPA_PAGE_RANGES);
WARN_ON_ONCE(sgl->length < HV_MIN_PAGE_REPORTING_LEN);
local_irq_save(flags);
hint = *(struct hv_memory_hint **)this_cpu_ptr(hyperv_pcpu_input_arg);
if (!hint) {
local_irq_restore(flags);
return -ENOSPC;
}
hint->type = HV_EXT_MEMORY_HEAT_HINT_TYPE_COLD_DISCARD;
hint->reserved = 0;
for_each_sg(sgl, sg, nents, i) {
union hv_gpa_page_range *range;
range = &hint->ranges[i];
range->address_space = 0;
/* page reporting only reports 2MB pages or higher */
range->page.largepage = 1;
range->page.additional_pages =
(sg->length / HV_MIN_PAGE_REPORTING_LEN) - 1;
range->page_size = HV_GPA_PAGE_RANGE_PAGE_SIZE_2MB;
range->base_large_pfn =
page_to_hvpfn(sg_page(sg)) >> HV_MIN_PAGE_REPORTING_ORDER;
}
status = hv_do_rep_hypercall(HV_EXT_CALL_MEMORY_HEAT_HINT, nents, 0,
hint, NULL);
local_irq_restore(flags);
if ((status & HV_HYPERCALL_RESULT_MASK) != HV_STATUS_SUCCESS) {
pr_err("Cold memory discard hypercall failed with status %llx\n",
status);
return -EINVAL;
}
return 0;
}
static void enable_page_reporting(void)
{
int ret;
/* Essentially, validating 'PAGE_REPORTING_MIN_ORDER' is big enough. */
if (pageblock_order < HV_MIN_PAGE_REPORTING_ORDER) {
pr_debug("Cold memory discard is only supported on 2MB pages and above\n");
return;
}
if (!hv_query_ext_cap(HV_EXT_CAPABILITY_MEMORY_COLD_DISCARD_HINT)) {
pr_debug("Cold memory discard hint not supported by Hyper-V\n");
return;
}
BUILD_BUG_ON(PAGE_REPORTING_CAPACITY > HV_MEMORY_HINT_MAX_GPA_PAGE_RANGES);
dm_device.pr_dev_info.report = hv_free_page_report;
ret = page_reporting_register(&dm_device.pr_dev_info);
if (ret < 0) {
dm_device.pr_dev_info.report = NULL;
pr_err("Failed to enable cold memory discard: %d\n", ret);
} else {
pr_info("Cold memory discard hint enabled\n");
}
}
static void disable_page_reporting(void)
{
if (dm_device.pr_dev_info.report) {
page_reporting_unregister(&dm_device.pr_dev_info);
dm_device.pr_dev_info.report = NULL;
}
}
static int balloon_connect_vsp(struct hv_device *dev) static int balloon_connect_vsp(struct hv_device *dev)
{ {
struct dm_version_request version_req; struct dm_version_request version_req;
...@@ -1713,6 +1799,7 @@ static int balloon_probe(struct hv_device *dev, ...@@ -1713,6 +1799,7 @@ static int balloon_probe(struct hv_device *dev,
if (ret != 0) if (ret != 0)
return ret; return ret;
enable_page_reporting();
dm_device.state = DM_INITIALIZED; dm_device.state = DM_INITIALIZED;
dm_device.thread = dm_device.thread =
...@@ -1727,6 +1814,7 @@ static int balloon_probe(struct hv_device *dev, ...@@ -1727,6 +1814,7 @@ static int balloon_probe(struct hv_device *dev,
probe_error: probe_error:
dm_device.state = DM_INIT_ERROR; dm_device.state = DM_INIT_ERROR;
dm_device.thread = NULL; dm_device.thread = NULL;
disable_page_reporting();
vmbus_close(dev->channel); vmbus_close(dev->channel);
#ifdef CONFIG_MEMORY_HOTPLUG #ifdef CONFIG_MEMORY_HOTPLUG
unregister_memory_notifier(&hv_memory_nb); unregister_memory_notifier(&hv_memory_nb);
...@@ -1749,6 +1837,7 @@ static int balloon_remove(struct hv_device *dev) ...@@ -1749,6 +1837,7 @@ static int balloon_remove(struct hv_device *dev)
cancel_work_sync(&dm->ha_wrk.wrk); cancel_work_sync(&dm->ha_wrk.wrk);
kthread_stop(dm->thread); kthread_stop(dm->thread);
disable_page_reporting();
vmbus_close(dev->channel); vmbus_close(dev->channel);
#ifdef CONFIG_MEMORY_HOTPLUG #ifdef CONFIG_MEMORY_HOTPLUG
unregister_memory_notifier(&hv_memory_nb); unregister_memory_notifier(&hv_memory_nb);
......
...@@ -103,6 +103,21 @@ TRACE_EVENT(vmbus_ongpadl_created, ...@@ -103,6 +103,21 @@ TRACE_EVENT(vmbus_ongpadl_created,
) )
); );
TRACE_EVENT(vmbus_onmodifychannel_response,
TP_PROTO(const struct vmbus_channel_modifychannel_response *response),
TP_ARGS(response),
TP_STRUCT__entry(
__field(u32, child_relid)
__field(u32, status)
),
TP_fast_assign(__entry->child_relid = response->child_relid;
__entry->status = response->status;
),
TP_printk("child_relid 0x%x, status %d",
__entry->child_relid, __entry->status
)
);
TRACE_EVENT(vmbus_ongpadl_torndown, TRACE_EVENT(vmbus_ongpadl_torndown,
TP_PROTO(const struct vmbus_channel_gpadl_torndown *gpadltorndown), TP_PROTO(const struct vmbus_channel_gpadl_torndown *gpadltorndown),
TP_ARGS(gpadltorndown), TP_ARGS(gpadltorndown),
......
...@@ -84,15 +84,6 @@ hv_set_next_write_location(struct hv_ring_buffer_info *ring_info, ...@@ -84,15 +84,6 @@ hv_set_next_write_location(struct hv_ring_buffer_info *ring_info,
ring_info->ring_buffer->write_index = next_write_location; ring_info->ring_buffer->write_index = next_write_location;
} }
/* Set the next read location for the specified ring buffer. */
static inline void
hv_set_next_read_location(struct hv_ring_buffer_info *ring_info,
u32 next_read_location)
{
ring_info->ring_buffer->read_index = next_read_location;
ring_info->priv_read_index = next_read_location;
}
/* Get the size of the ring buffer. */ /* Get the size of the ring buffer. */
static inline u32 static inline u32
hv_get_ring_buffersize(const struct hv_ring_buffer_info *ring_info) hv_get_ring_buffersize(const struct hv_ring_buffer_info *ring_info)
...@@ -313,7 +304,6 @@ int hv_ringbuffer_write(struct vmbus_channel *channel, ...@@ -313,7 +304,6 @@ int hv_ringbuffer_write(struct vmbus_channel *channel,
rqst_id = vmbus_next_request_id(&channel->requestor, requestid); rqst_id = vmbus_next_request_id(&channel->requestor, requestid);
if (rqst_id == VMBUS_RQST_ERROR) { if (rqst_id == VMBUS_RQST_ERROR) {
spin_unlock_irqrestore(&outring_info->ring_lock, flags); spin_unlock_irqrestore(&outring_info->ring_lock, flags);
pr_err("No request id available\n");
return -EAGAIN; return -EAGAIN;
} }
} }
......
...@@ -48,8 +48,10 @@ static int hyperv_cpuhp_online; ...@@ -48,8 +48,10 @@ static int hyperv_cpuhp_online;
static void *hv_panic_page; static void *hv_panic_page;
static long __percpu *vmbus_evt;
/* Values parsed from ACPI DSDT */ /* Values parsed from ACPI DSDT */
static int vmbus_irq; int vmbus_irq;
int vmbus_interrupt; int vmbus_interrupt;
/* /*
...@@ -1381,7 +1383,13 @@ static void vmbus_isr(void) ...@@ -1381,7 +1383,13 @@ static void vmbus_isr(void)
tasklet_schedule(&hv_cpu->msg_dpc); tasklet_schedule(&hv_cpu->msg_dpc);
} }
add_interrupt_randomness(hv_get_vector(), 0); add_interrupt_randomness(vmbus_interrupt, 0);
}
static irqreturn_t vmbus_percpu_isr(int irq, void *dev_id)
{
vmbus_isr();
return IRQ_HANDLED;
} }
/* /*
...@@ -1392,22 +1400,36 @@ static void hv_kmsg_dump(struct kmsg_dumper *dumper, ...@@ -1392,22 +1400,36 @@ static void hv_kmsg_dump(struct kmsg_dumper *dumper,
enum kmsg_dump_reason reason) enum kmsg_dump_reason reason)
{ {
size_t bytes_written; size_t bytes_written;
phys_addr_t panic_pa;
/* We are only interested in panics. */ /* We are only interested in panics. */
if ((reason != KMSG_DUMP_PANIC) || (!sysctl_record_panic_msg)) if ((reason != KMSG_DUMP_PANIC) || (!sysctl_record_panic_msg))
return; return;
panic_pa = virt_to_phys(hv_panic_page);
/* /*
* Write dump contents to the page. No need to synchronize; panic should * Write dump contents to the page. No need to synchronize; panic should
* be single-threaded. * be single-threaded.
*/ */
kmsg_dump_get_buffer(dumper, false, hv_panic_page, HV_HYP_PAGE_SIZE, kmsg_dump_get_buffer(dumper, false, hv_panic_page, HV_HYP_PAGE_SIZE,
&bytes_written); &bytes_written);
if (bytes_written) if (!bytes_written)
hyperv_report_panic_msg(panic_pa, bytes_written); return;
/*
* P3 to contain the physical address of the panic page & P4 to
* contain the size of the panic data in that page. Rest of the
* registers are no-op when the NOTIFY_MSG flag is set.
*/
hv_set_register(HV_REGISTER_CRASH_P0, 0);
hv_set_register(HV_REGISTER_CRASH_P1, 0);
hv_set_register(HV_REGISTER_CRASH_P2, 0);
hv_set_register(HV_REGISTER_CRASH_P3, virt_to_phys(hv_panic_page));
hv_set_register(HV_REGISTER_CRASH_P4, bytes_written);
/*
* Let Hyper-V know there is crash data available along with
* the panic message.
*/
hv_set_register(HV_REGISTER_CRASH_CTL,
(HV_CRASH_CTL_CRASH_NOTIFY | HV_CRASH_CTL_CRASH_NOTIFY_MSG));
} }
static struct kmsg_dumper hv_kmsg_dumper = { static struct kmsg_dumper hv_kmsg_dumper = {
...@@ -1482,9 +1504,28 @@ static int vmbus_bus_init(void) ...@@ -1482,9 +1504,28 @@ static int vmbus_bus_init(void)
if (ret) if (ret)
return ret; return ret;
ret = hv_setup_vmbus_irq(vmbus_irq, vmbus_isr); /*
if (ret) * VMbus interrupts are best modeled as per-cpu interrupts. If
goto err_setup; * on an architecture with support for per-cpu IRQs (e.g. ARM64),
* allocate a per-cpu IRQ using standard Linux kernel functionality.
* If not on such an architecture (e.g., x86/x64), then rely on
* code in the arch-specific portion of the code tree to connect
* the VMbus interrupt handler.
*/
if (vmbus_irq == -1) {
hv_setup_vmbus_handler(vmbus_isr);
} else {
vmbus_evt = alloc_percpu(long);
ret = request_percpu_irq(vmbus_irq, vmbus_percpu_isr,
"Hyper-V VMbus", vmbus_evt);
if (ret) {
pr_err("Can't request Hyper-V VMbus IRQ %d, Err %d",
vmbus_irq, ret);
free_percpu(vmbus_evt);
goto err_setup;
}
}
ret = hv_synic_alloc(); ret = hv_synic_alloc();
if (ret) if (ret)
...@@ -1521,7 +1562,7 @@ static int vmbus_bus_init(void) ...@@ -1521,7 +1562,7 @@ static int vmbus_bus_init(void)
* Register for panic kmsg callback only if the right * Register for panic kmsg callback only if the right
* capability is supported by the hypervisor. * capability is supported by the hypervisor.
*/ */
hv_get_crash_ctl(hyperv_crash_ctl); hyperv_crash_ctl = hv_get_register(HV_REGISTER_CRASH_CTL);
if (hyperv_crash_ctl & HV_CRASH_CTL_CRASH_NOTIFY_MSG) if (hyperv_crash_ctl & HV_CRASH_CTL_CRASH_NOTIFY_MSG)
hv_kmsg_dump_register(); hv_kmsg_dump_register();
...@@ -1545,7 +1586,12 @@ static int vmbus_bus_init(void) ...@@ -1545,7 +1586,12 @@ static int vmbus_bus_init(void)
err_cpuhp: err_cpuhp:
hv_synic_free(); hv_synic_free();
err_alloc: err_alloc:
hv_remove_vmbus_irq(); if (vmbus_irq == -1) {
hv_remove_vmbus_handler();
} else {
free_percpu_irq(vmbus_irq, vmbus_evt);
free_percpu(vmbus_evt);
}
err_setup: err_setup:
bus_unregister(&hv_bus); bus_unregister(&hv_bus);
unregister_sysctl_table(hv_ctl_table_hdr); unregister_sysctl_table(hv_ctl_table_hdr);
...@@ -1802,13 +1848,15 @@ static ssize_t target_cpu_store(struct vmbus_channel *channel, ...@@ -1802,13 +1848,15 @@ static ssize_t target_cpu_store(struct vmbus_channel *channel,
if (target_cpu == origin_cpu) if (target_cpu == origin_cpu)
goto cpu_store_unlock; goto cpu_store_unlock;
if (vmbus_send_modifychannel(channel->offermsg.child_relid, if (vmbus_send_modifychannel(channel,
hv_cpu_number_to_vp_number(target_cpu))) { hv_cpu_number_to_vp_number(target_cpu))) {
ret = -EIO; ret = -EIO;
goto cpu_store_unlock; goto cpu_store_unlock;
} }
/* /*
* For version before VERSION_WIN10_V5_3, the following warning holds:
*
* Warning. At this point, there is *no* guarantee that the host will * Warning. At this point, there is *no* guarantee that the host will
* have successfully processed the vmbus_send_modifychannel() request. * have successfully processed the vmbus_send_modifychannel() request.
* See the header comment of vmbus_send_modifychannel() for more info. * See the header comment of vmbus_send_modifychannel() for more info.
...@@ -2663,6 +2711,18 @@ static int __init hv_acpi_init(void) ...@@ -2663,6 +2711,18 @@ static int __init hv_acpi_init(void)
ret = -ETIMEDOUT; ret = -ETIMEDOUT;
goto cleanup; goto cleanup;
} }
/*
* If we're on an architecture with a hardcoded hypervisor
* vector (i.e. x86/x64), override the VMbus interrupt found
* in the ACPI tables. Ensure vmbus_irq is not set since the
* normal Linux IRQ mechanism is not used in this case.
*/
#ifdef HYPERVISOR_CALLBACK_VECTOR
vmbus_interrupt = HYPERVISOR_CALLBACK_VECTOR;
vmbus_irq = -1;
#endif
hv_debug_init(); hv_debug_init();
ret = vmbus_bus_init(); ret = vmbus_bus_init();
...@@ -2693,7 +2753,12 @@ static void __exit vmbus_exit(void) ...@@ -2693,7 +2753,12 @@ static void __exit vmbus_exit(void)
vmbus_connection.conn_state = DISCONNECTED; vmbus_connection.conn_state = DISCONNECTED;
hv_stimer_global_cleanup(); hv_stimer_global_cleanup();
vmbus_disconnect(); vmbus_disconnect();
hv_remove_vmbus_irq(); if (vmbus_irq == -1) {
hv_remove_vmbus_handler();
} else {
free_percpu_irq(vmbus_irq, vmbus_evt);
free_percpu(vmbus_evt);
}
for_each_online_cpu(cpu) { for_each_online_cpu(cpu) {
struct hv_per_cpu_context *hv_cpu struct hv_per_cpu_context *hv_cpu
= per_cpu_ptr(hv_context.cpu_context, cpu); = per_cpu_ptr(hv_context.cpu_context, cpu);
......
...@@ -1292,7 +1292,7 @@ static void hv_irq_unmask(struct irq_data *data) ...@@ -1292,7 +1292,7 @@ static void hv_irq_unmask(struct irq_data *data)
* resumes, hv_pci_restore_msi_state() is able to correctly restore * resumes, hv_pci_restore_msi_state() is able to correctly restore
* the interrupt with the correct affinity. * the interrupt with the correct affinity.
*/ */
if (res && hbus->state != hv_pcibus_removing) if (!hv_result_success(res) && hbus->state != hv_pcibus_removing)
dev_err(&hbus->hdev->device, dev_err(&hbus->hdev->device,
"%s() failed: %#llx", __func__, res); "%s() failed: %#llx", __func__, res);
......
...@@ -308,7 +308,7 @@ static inline int synthvid_send(struct hv_device *hdev, ...@@ -308,7 +308,7 @@ static inline int synthvid_send(struct hv_device *hdev,
VM_PKT_DATA_INBAND, 0); VM_PKT_DATA_INBAND, 0);
if (ret) if (ret)
pr_err("Unable to send packet via vmbus\n"); pr_err_ratelimited("Unable to send packet via vmbus; error %d\n", ret);
return ret; return ret;
} }
......
...@@ -89,9 +89,9 @@ ...@@ -89,9 +89,9 @@
#define HV_ACCESS_STATS BIT(8) #define HV_ACCESS_STATS BIT(8)
#define HV_DEBUGGING BIT(11) #define HV_DEBUGGING BIT(11)
#define HV_CPU_MANAGEMENT BIT(12) #define HV_CPU_MANAGEMENT BIT(12)
#define HV_ENABLE_EXTENDED_HYPERCALLS BIT(20)
#define HV_ISOLATION BIT(22) #define HV_ISOLATION BIT(22)
/* /*
* TSC page layout. * TSC page layout.
*/ */
...@@ -159,11 +159,18 @@ struct ms_hyperv_tsc_page { ...@@ -159,11 +159,18 @@ struct ms_hyperv_tsc_page {
#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_SPACE 0x00af #define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_SPACE 0x00af
#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_LIST 0x00b0 #define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_LIST 0x00b0
/* Extended hypercalls */
#define HV_EXT_CALL_QUERY_CAPABILITIES 0x8001
#define HV_EXT_CALL_MEMORY_HEAT_HINT 0x8003
#define HV_FLUSH_ALL_PROCESSORS BIT(0) #define HV_FLUSH_ALL_PROCESSORS BIT(0)
#define HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES BIT(1) #define HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES BIT(1)
#define HV_FLUSH_NON_GLOBAL_MAPPINGS_ONLY BIT(2) #define HV_FLUSH_NON_GLOBAL_MAPPINGS_ONLY BIT(2)
#define HV_FLUSH_USE_EXTENDED_RANGE_FORMAT BIT(3) #define HV_FLUSH_USE_EXTENDED_RANGE_FORMAT BIT(3)
/* Extended capability bits */
#define HV_EXT_CAPABILITY_MEMORY_COLD_DISCARD_HINT BIT(8)
enum HV_GENERIC_SET_FORMAT { enum HV_GENERIC_SET_FORMAT {
HV_GENERIC_SET_SPARSE_4K, HV_GENERIC_SET_SPARSE_4K,
HV_GENERIC_SET_ALL, HV_GENERIC_SET_ALL,
...@@ -220,6 +227,41 @@ enum HV_GENERIC_SET_FORMAT { ...@@ -220,6 +227,41 @@ enum HV_GENERIC_SET_FORMAT {
#define HV_MESSAGE_PAYLOAD_BYTE_COUNT (240) #define HV_MESSAGE_PAYLOAD_BYTE_COUNT (240)
#define HV_MESSAGE_PAYLOAD_QWORD_COUNT (30) #define HV_MESSAGE_PAYLOAD_QWORD_COUNT (30)
/*
* Define hypervisor message types. Some of the message types
* are x86/x64 specific, but there's no good way to separate
* them out into the arch-specific version of hyperv-tlfs.h
* because C doesn't provide a way to extend enum types.
* Keeping them all in the arch neutral hyperv-tlfs.h seems
* the least messy compromise.
*/
enum hv_message_type {
HVMSG_NONE = 0x00000000,
/* Memory access messages. */
HVMSG_UNMAPPED_GPA = 0x80000000,
HVMSG_GPA_INTERCEPT = 0x80000001,
/* Timer notification messages. */
HVMSG_TIMER_EXPIRED = 0x80000010,
/* Error messages. */
HVMSG_INVALID_VP_REGISTER_VALUE = 0x80000020,
HVMSG_UNRECOVERABLE_EXCEPTION = 0x80000021,
HVMSG_UNSUPPORTED_FEATURE = 0x80000022,
/* Trace buffer complete messages. */
HVMSG_EVENTLOG_BUFFERCOMPLETE = 0x80000040,
/* Platform-specific processor intercept messages. */
HVMSG_X64_IOPORT_INTERCEPT = 0x80010000,
HVMSG_X64_MSR_INTERCEPT = 0x80010001,
HVMSG_X64_CPUID_INTERCEPT = 0x80010002,
HVMSG_X64_EXCEPTION_INTERCEPT = 0x80010003,
HVMSG_X64_APIC_EOI = 0x80010004,
HVMSG_X64_LEGACY_FP_ERROR = 0x80010005
};
/* Define synthetic interrupt controller message flags. */ /* Define synthetic interrupt controller message flags. */
union hv_message_flags { union hv_message_flags {
__u8 asu8; __u8 asu8;
...@@ -373,8 +415,10 @@ struct hv_guest_mapping_flush { ...@@ -373,8 +415,10 @@ struct hv_guest_mapping_flush {
* by the bitwidth of "additional_pages" in union hv_gpa_page_range. * by the bitwidth of "additional_pages" in union hv_gpa_page_range.
*/ */
#define HV_MAX_FLUSH_PAGES (2048) #define HV_MAX_FLUSH_PAGES (2048)
#define HV_GPA_PAGE_RANGE_PAGE_SIZE_2MB 0
#define HV_GPA_PAGE_RANGE_PAGE_SIZE_1GB 1
/* HvFlushGuestPhysicalAddressList hypercall */ /* HvFlushGuestPhysicalAddressList, HvExtCallMemoryHeatHint hypercall */
union hv_gpa_page_range { union hv_gpa_page_range {
u64 address_space; u64 address_space;
struct { struct {
...@@ -382,6 +426,12 @@ union hv_gpa_page_range { ...@@ -382,6 +426,12 @@ union hv_gpa_page_range {
u64 largepage:1; u64 largepage:1;
u64 basepfn:52; u64 basepfn:52;
} page; } page;
struct {
u64 reserved:12;
u64 page_size:1;
u64 reserved1:8;
u64 base_large_pfn:43;
};
}; };
/* /*
...@@ -739,4 +789,20 @@ struct hv_input_unmap_device_interrupt { ...@@ -739,4 +789,20 @@ struct hv_input_unmap_device_interrupt {
#define HV_SOURCE_SHADOW_NONE 0x0 #define HV_SOURCE_SHADOW_NONE 0x0
#define HV_SOURCE_SHADOW_BRIDGE_BUS_RANGE 0x1 #define HV_SOURCE_SHADOW_BRIDGE_BUS_RANGE 0x1
/*
* The whole argument should fit in a page to be able to pass to the hypervisor
* in one hypercall.
*/
#define HV_MEMORY_HINT_MAX_GPA_PAGE_RANGES \
((HV_HYP_PAGE_SIZE - sizeof(struct hv_memory_hint)) / \
sizeof(union hv_gpa_page_range))
/* HvExtCallMemoryHeatHint hypercall */
#define HV_EXT_MEMORY_HEAT_HINT_TYPE_COLD_DISCARD 2
struct hv_memory_hint {
u64 type:2;
u64 reserved:62;
union hv_gpa_page_range ranges[];
} __packed;
#endif #endif
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
struct ms_hyperv_info { struct ms_hyperv_info {
u32 features; u32 features;
u32 features_b; u32 priv_high;
u32 misc_features; u32 misc_features;
u32 hints; u32 hints;
u32 nested_features; u32 nested_features;
...@@ -41,6 +41,53 @@ extern struct ms_hyperv_info ms_hyperv; ...@@ -41,6 +41,53 @@ extern struct ms_hyperv_info ms_hyperv;
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);
/* Helper functions that provide a consistent pattern for checking Hyper-V hypercall status. */
static inline int hv_result(u64 status)
{
return status & HV_HYPERCALL_RESULT_MASK;
}
static inline bool hv_result_success(u64 status)
{
return hv_result(status) == HV_STATUS_SUCCESS;
}
static inline unsigned int hv_repcomp(u64 status)
{
/* Bits [43:32] of status have 'Reps completed' data. */
return (status & HV_HYPERCALL_REP_COMP_MASK) >>
HV_HYPERCALL_REP_COMP_OFFSET;
}
/*
* Rep hypercalls. Callers of this functions are supposed to ensure that
* rep_count and varhead_size comply with Hyper-V hypercall definition.
*/
static inline u64 hv_do_rep_hypercall(u16 code, u16 rep_count, u16 varhead_size,
void *input, void *output)
{
u64 control = code;
u64 status;
u16 rep_comp;
control |= (u64)varhead_size << HV_HYPERCALL_VARHEAD_OFFSET;
control |= (u64)rep_count << HV_HYPERCALL_REP_COMP_OFFSET;
do {
status = hv_do_hypercall(control, input, output);
if (!hv_result_success(status))
return status;
rep_comp = hv_repcomp(status);
control &= ~HV_HYPERCALL_REP_START_MASK;
control |= (u64)rep_comp << HV_HYPERCALL_REP_START_OFFSET;
touch_nmi_watchdog();
} while (rep_comp < rep_count);
return status;
}
/* Generate the guest OS identifier as described in the Hyper-V TLFS */ /* Generate the guest OS identifier as described in the Hyper-V TLFS */
static inline __u64 generate_guest_id(__u64 d_info1, __u64 kernel_version, static inline __u64 generate_guest_id(__u64 d_info1, __u64 kernel_version,
...@@ -56,7 +103,6 @@ static inline __u64 generate_guest_id(__u64 d_info1, __u64 kernel_version, ...@@ -56,7 +103,6 @@ static inline __u64 generate_guest_id(__u64 d_info1, __u64 kernel_version,
return guest_id; return guest_id;
} }
/* Free the message slot and signal end-of-message if required */ /* Free the message slot and signal end-of-message if required */
static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type) static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type)
{ {
...@@ -88,14 +134,14 @@ static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type) ...@@ -88,14 +134,14 @@ static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type)
* possibly deliver another msg from the * possibly deliver another msg from the
* hypervisor * hypervisor
*/ */
hv_signal_eom(); hv_set_register(HV_REGISTER_EOM, 0);
} }
} }
int hv_setup_vmbus_irq(int irq, void (*handler)(void)); void hv_setup_vmbus_handler(void (*handler)(void));
void hv_remove_vmbus_irq(void); void hv_remove_vmbus_handler(void);
void hv_enable_vmbus_irq(void); void hv_setup_stimer0_handler(void (*handler)(void));
void hv_disable_vmbus_irq(void); void hv_remove_stimer0_handler(void);
void hv_setup_kexec_handler(void (*handler)(void)); void hv_setup_kexec_handler(void (*handler)(void));
void hv_remove_kexec_handler(void); void hv_remove_kexec_handler(void);
...@@ -103,6 +149,7 @@ void hv_setup_crash_handler(void (*handler)(struct pt_regs *regs)); ...@@ -103,6 +149,7 @@ void hv_setup_crash_handler(void (*handler)(struct pt_regs *regs));
void hv_remove_crash_handler(void); void hv_remove_crash_handler(void);
extern int vmbus_interrupt; extern int vmbus_interrupt;
extern int vmbus_irq;
#if IS_ENABLED(CONFIG_HYPERV) #if IS_ENABLED(CONFIG_HYPERV)
/* /*
...@@ -117,6 +164,10 @@ extern u32 hv_max_vp_index; ...@@ -117,6 +164,10 @@ 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
void *hv_alloc_hyperv_page(void);
void *hv_alloc_hyperv_zeroed_page(void);
void hv_free_hyperv_page(unsigned long addr);
/** /**
* hv_cpu_number_to_vp_number() - Map CPU to VP. * hv_cpu_number_to_vp_number() - Map CPU to VP.
* @cpu_number: CPU number in Linux terms * @cpu_number: CPU number in Linux terms
...@@ -169,21 +220,16 @@ static inline int cpumask_to_vpset(struct hv_vpset *vpset, ...@@ -169,21 +220,16 @@ static inline int cpumask_to_vpset(struct hv_vpset *vpset,
} }
void hyperv_report_panic(struct pt_regs *regs, long err, bool in_die); void hyperv_report_panic(struct pt_regs *regs, long err, bool in_die);
void hyperv_report_panic_msg(phys_addr_t pa, size_t size);
bool hv_is_hyperv_initialized(void); bool hv_is_hyperv_initialized(void);
bool hv_is_hibernation_supported(void); bool hv_is_hibernation_supported(void);
enum hv_isolation_type hv_get_isolation_type(void); enum hv_isolation_type hv_get_isolation_type(void);
bool hv_is_isolation_supported(void); bool hv_is_isolation_supported(void);
void hyperv_cleanup(void); void hyperv_cleanup(void);
bool hv_query_ext_cap(u64 cap_query);
#else /* CONFIG_HYPERV */ #else /* CONFIG_HYPERV */
static inline bool hv_is_hyperv_initialized(void) { return false; } static inline bool hv_is_hyperv_initialized(void) { return false; }
static inline bool hv_is_hibernation_supported(void) { return false; } static inline bool hv_is_hibernation_supported(void) { return false; }
static inline void hyperv_cleanup(void) {} static inline void hyperv_cleanup(void) {}
#endif /* CONFIG_HYPERV */ #endif /* CONFIG_HYPERV */
#if IS_ENABLED(CONFIG_HYPERV)
extern int hv_setup_stimer0_irq(int *irq, int *vector, void (*handler)(void));
extern void hv_remove_stimer0_irq(int irq);
#endif
#endif #endif
...@@ -21,8 +21,7 @@ ...@@ -21,8 +21,7 @@
#define HV_MIN_DELTA_TICKS 1 #define HV_MIN_DELTA_TICKS 1
/* Routines called by the VMbus driver */ /* Routines called by the VMbus driver */
extern int hv_stimer_alloc(void); extern int hv_stimer_alloc(bool have_percpu_irqs);
extern void hv_stimer_free(void);
extern int hv_stimer_cleanup(unsigned int cpu); extern int hv_stimer_cleanup(unsigned int cpu);
extern void hv_stimer_legacy_init(unsigned int cpu, int sint); extern void hv_stimer_legacy_init(unsigned int cpu, int sint);
extern void hv_stimer_legacy_cleanup(unsigned int cpu); extern void hv_stimer_legacy_cleanup(unsigned int cpu);
......
...@@ -234,6 +234,7 @@ static inline u32 hv_get_avail_to_write_percent( ...@@ -234,6 +234,7 @@ static inline u32 hv_get_avail_to_write_percent(
* 5 . 0 (Newer Windows 10) * 5 . 0 (Newer Windows 10)
* 5 . 1 (Windows 10 RS4) * 5 . 1 (Windows 10 RS4)
* 5 . 2 (Windows Server 2019, RS5) * 5 . 2 (Windows Server 2019, RS5)
* 5 . 3 (Windows Server 2022)
*/ */
#define VERSION_WS2008 ((0 << 16) | (13)) #define VERSION_WS2008 ((0 << 16) | (13))
...@@ -245,6 +246,7 @@ static inline u32 hv_get_avail_to_write_percent( ...@@ -245,6 +246,7 @@ static inline u32 hv_get_avail_to_write_percent(
#define VERSION_WIN10_V5 ((5 << 16) | (0)) #define VERSION_WIN10_V5 ((5 << 16) | (0))
#define VERSION_WIN10_V5_1 ((5 << 16) | (1)) #define VERSION_WIN10_V5_1 ((5 << 16) | (1))
#define VERSION_WIN10_V5_2 ((5 << 16) | (2)) #define VERSION_WIN10_V5_2 ((5 << 16) | (2))
#define VERSION_WIN10_V5_3 ((5 << 16) | (3))
/* Make maximum size of pipe payload of 16K */ /* Make maximum size of pipe payload of 16K */
#define MAX_PIPE_DATA_PAYLOAD (sizeof(u8) * 16384) #define MAX_PIPE_DATA_PAYLOAD (sizeof(u8) * 16384)
...@@ -284,7 +286,7 @@ struct vmbus_channel_offer { ...@@ -284,7 +286,7 @@ struct vmbus_channel_offer {
/* /*
* Pipes: * Pipes:
* The following sructure is an integrated pipe protocol, which * The following structure is an integrated pipe protocol, which
* is implemented on top of standard user-defined data. Pipe * is implemented on top of standard user-defined data. Pipe
* clients have MAX_PIPE_USER_DEFINED_BYTES left for their own * clients have MAX_PIPE_USER_DEFINED_BYTES left for their own
* use. * use.
...@@ -475,6 +477,7 @@ enum vmbus_channel_message_type { ...@@ -475,6 +477,7 @@ enum vmbus_channel_message_type {
CHANNELMSG_TL_CONNECT_REQUEST = 21, CHANNELMSG_TL_CONNECT_REQUEST = 21,
CHANNELMSG_MODIFYCHANNEL = 22, CHANNELMSG_MODIFYCHANNEL = 22,
CHANNELMSG_TL_CONNECT_RESULT = 23, CHANNELMSG_TL_CONNECT_RESULT = 23,
CHANNELMSG_MODIFYCHANNEL_RESPONSE = 24,
CHANNELMSG_COUNT CHANNELMSG_COUNT
}; };
...@@ -588,6 +591,13 @@ struct vmbus_channel_open_result { ...@@ -588,6 +591,13 @@ struct vmbus_channel_open_result {
u32 status; u32 status;
} __packed; } __packed;
/* Modify Channel Result parameters */
struct vmbus_channel_modifychannel_response {
struct vmbus_channel_message_header header;
u32 child_relid;
u32 status;
} __packed;
/* Close channel parameters; */ /* Close channel parameters; */
struct vmbus_channel_close_channel { struct vmbus_channel_close_channel {
struct vmbus_channel_message_header header; struct vmbus_channel_message_header header;
...@@ -720,6 +730,7 @@ struct vmbus_channel_msginfo { ...@@ -720,6 +730,7 @@ struct vmbus_channel_msginfo {
struct vmbus_channel_gpadl_torndown gpadl_torndown; struct vmbus_channel_gpadl_torndown gpadl_torndown;
struct vmbus_channel_gpadl_created gpadl_created; struct vmbus_channel_gpadl_created gpadl_created;
struct vmbus_channel_version_response version_response; struct vmbus_channel_version_response version_response;
struct vmbus_channel_modifychannel_response modify_response;
} response; } response;
u32 msgsize; u32 msgsize;
...@@ -883,11 +894,11 @@ struct vmbus_channel { ...@@ -883,11 +894,11 @@ struct vmbus_channel {
* Support for sub-channels. For high performance devices, * Support for sub-channels. For high performance devices,
* it will be useful to have multiple sub-channels to support * it will be useful to have multiple sub-channels to support
* a scalable communication infrastructure with the host. * a scalable communication infrastructure with the host.
* The support for sub-channels is implemented as an extention * The support for sub-channels is implemented as an extension
* to the current infrastructure. * to the current infrastructure.
* The initial offer is considered the primary channel and this * The initial offer is considered the primary channel and this
* offer message will indicate if the host supports sub-channels. * offer message will indicate if the host supports sub-channels.
* The guest is free to ask for sub-channels to be offerred and can * The guest is free to ask for sub-channels to be offered and can
* open these sub-channels as a normal "primary" channel. However, * open these sub-channels as a normal "primary" channel. However,
* all sub-channels will have the same type and instance guids as the * all sub-channels will have the same type and instance guids as the
* primary channel. Requests sent on a given channel will result in a * primary channel. Requests sent on a given channel will result in a
...@@ -951,7 +962,7 @@ struct vmbus_channel { ...@@ -951,7 +962,7 @@ struct vmbus_channel {
* Clearly, these optimizations improve throughput at the expense of * Clearly, these optimizations improve throughput at the expense of
* latency. Furthermore, since the channel is shared for both * latency. Furthermore, since the channel is shared for both
* control and data messages, control messages currently suffer * control and data messages, control messages currently suffer
* unnecessary latency adversley impacting performance and boot * unnecessary latency adversely impacting performance and boot
* time. To fix this issue, permit tagging the channel as being * time. To fix this issue, permit tagging the channel as being
* in "low latency" mode. In this mode, we will bypass the monitor * in "low latency" mode. In this mode, we will bypass the monitor
* mechanism. * mechanism.
...@@ -1594,7 +1605,7 @@ extern __u32 vmbus_proto_version; ...@@ -1594,7 +1605,7 @@ extern __u32 vmbus_proto_version;
int vmbus_send_tl_connect_request(const guid_t *shv_guest_servie_id, int vmbus_send_tl_connect_request(const guid_t *shv_guest_servie_id,
const guid_t *shv_host_servie_id); const guid_t *shv_host_servie_id);
int vmbus_send_modifychannel(u32 child_relid, u32 target_vp); int vmbus_send_modifychannel(struct vmbus_channel *channel, u32 target_vp);
void vmbus_set_event(struct vmbus_channel *channel); void vmbus_set_event(struct vmbus_channel *channel);
/* Get the start of the ring buffer. */ /* Get the start of the ring buffer. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment