This patch series enables the nested virtualization enlightenments for
SVM. This is very similar to the enlightenments for VMX except for the
fact that there is no enlightened VMCS. For SVM, VMCB is already an
architectural in-memory data structure.
Note: v5 is just a rebase on hyperv-next(5.13-rc1) and needed a rework
based on the patch series: (KVM: VMX: Clean up Hyper-V PV TLB flush)
https://lore.kernel.org/lkml/[email protected]/
The supported enlightenments are:
Enlightened TLB Flush: If this is enabled, ASID invalidations invalidate
only gva -> hpa entries. To flush entries derived from NPT, hyper-v
provided hypercalls (HvFlushGuestPhysicalAddressSpace or
HvFlushGuestPhysicalAddressList) should be used.
Enlightened MSR bitmap(TLFS 16.5.3): "When enabled, L0 hypervisor does
not monitor the MSR bitmaps for changes. Instead, the L1 hypervisor must
invalidate the corresponding clean field after making changes to one of
the MSR bitmaps."
Direct Virtual Flush(TLFS 16.8): The hypervisor exposes hypercalls
(HvFlushVirtualAddressSpace, HvFlushVirtualAddressSpaceEx,
HvFlushVirtualAddressList, and HvFlushVirtualAddressListEx) that allow
operating systems to more efficiently manage the virtual TLB. The L1
hypervisor can choose to allow its guest to use those hypercalls and
delegate the responsibility to handle them to the L0 hypervisor. This
requires the use of a partition assist page."
L2 Windows boot time was measured with and without the patch. Time was
measured from power on to the login screen and was averaged over a
consecutive 5 trials:
Without the patch: 42 seconds
With the patch: 29 seconds
--
Changes from v4
- Rebased on top of 5.13-rc1 and reworked based on the changes in the
patch series: (KVM: VMX: Clean up Hyper-V PV TLB flush)
Changes from v3
- Included definitions for software/hypervisor reserved fields in SVM
architectural data structures.
- Consolidated Hyper-V specific code into svm_onhyperv.[ch] to reduce
the "ifdefs". This change applies only to SVM, VMX is not touched and
is not in the scope of this patch series.
Changes from v2:
- Refactored the Remote TLB Flush logic into separate hyperv specific
source files (kvm_onhyperv.[ch]).
- Reverted the VMCB Clean bits macro changes as it is no longer needed.
Changes from v1:
- Move the remote TLB flush related fields from kvm_vcpu_hv and kvm_hv
to kvm_vcpu_arch and kvm_arch.
- Modify the VMCB clean mask runtime based on whether L1 hypervisor
is running on Hyper-V or not.
- Detect Hyper-V nested enlightenments based on
HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS.
- Address other minor review comments.
---
Vineeth Pillai (7):
hyperv: Detect Nested virtualization support for SVM
hyperv: SVM enlightened TLB flush support flag
KVM: x86: hyper-v: Move the remote TLB flush logic out of vmx
KVM: SVM: Software reserved fields
KVM: SVM: hyper-v: Remote TLB flush for SVM
KVM: SVM: hyper-v: Enlightened MSR-Bitmap support
KVM: SVM: hyper-v: Direct Virtual Flush support
arch/x86/include/asm/hyperv-tlfs.h | 9 ++
arch/x86/include/asm/kvm_host.h | 9 ++
arch/x86/include/asm/svm.h | 9 +-
arch/x86/include/uapi/asm/svm.h | 3 +
arch/x86/kernel/cpu/mshyperv.c | 10 ++-
arch/x86/kvm/Makefile | 9 ++
arch/x86/kvm/kvm_onhyperv.c | 93 +++++++++++++++++++++
arch/x86/kvm/kvm_onhyperv.h | 32 +++++++
arch/x86/kvm/svm/svm.c | 14 ++++
arch/x86/kvm/svm/svm.h | 22 ++++-
arch/x86/kvm/svm/svm_onhyperv.c | 41 +++++++++
arch/x86/kvm/svm/svm_onhyperv.h | 129 +++++++++++++++++++++++++++++
arch/x86/kvm/vmx/vmx.c | 105 +----------------------
arch/x86/kvm/vmx/vmx.h | 9 --
arch/x86/kvm/x86.c | 9 ++
15 files changed, 384 insertions(+), 119 deletions(-)
create mode 100644 arch/x86/kvm/kvm_onhyperv.c
create mode 100644 arch/x86/kvm/kvm_onhyperv.h
create mode 100644 arch/x86/kvm/svm/svm_onhyperv.c
create mode 100644 arch/x86/kvm/svm/svm_onhyperv.h
--
2.25.1
Enlightened MSR-Bitmap as per TLFS:
"The L1 hypervisor may collaborate with the L0 hypervisor to make MSR
accesses more efficient. It can enable enlightened MSR bitmaps by setting
the corresponding field in the enlightened VMCS to 1. When enabled, L0
hypervisor does not monitor the MSR bitmaps for changes. Instead, the L1
hypervisor must invalidate the corresponding clean field after making
changes to one of the MSR bitmaps."
Enable this for SVM.
Related VMX changes:
commit ceef7d10dfb6 ("KVM: x86: VMX: hyper-v: Enlightened MSR-Bitmap support")
Signed-off-by: Vineeth Pillai <[email protected]>
---
arch/x86/kvm/svm/svm.c | 3 +++
arch/x86/kvm/svm/svm.h | 5 +++++
arch/x86/kvm/svm/svm_onhyperv.h | 27 +++++++++++++++++++++++++++
3 files changed, 35 insertions(+)
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index a39865dbc200..d2a625411059 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -671,6 +671,9 @@ static void set_msr_interception_bitmap(struct kvm_vcpu *vcpu, u32 *msrpm,
write ? clear_bit(bit_write, &tmp) : set_bit(bit_write, &tmp);
msrpm[offset] = tmp;
+
+ svm_hv_vmcb_dirty_nested_enlightenments(vcpu);
+
}
void set_msr_interception(struct kvm_vcpu *vcpu, u32 *msrpm, u32 msr,
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index eb4b91832912..1d64d246ebbe 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -254,6 +254,11 @@ static inline void vmcb_mark_all_clean(struct vmcb *vmcb)
& ~VMCB_ALWAYS_DIRTY_MASK;
}
+static inline bool vmcb_is_clean(struct vmcb *vmcb, int bit)
+{
+ return (vmcb->control.clean & (1 << bit));
+}
+
static inline void vmcb_mark_dirty(struct vmcb *vmcb, int bit)
{
vmcb->control.clean &= ~(1 << bit);
diff --git a/arch/x86/kvm/svm/svm_onhyperv.h b/arch/x86/kvm/svm/svm_onhyperv.h
index 57291e222395..0f262460b2e6 100644
--- a/arch/x86/kvm/svm/svm_onhyperv.h
+++ b/arch/x86/kvm/svm/svm_onhyperv.h
@@ -31,6 +31,11 @@ struct hv_enlightenments {
u64 reserved;
} __packed;
+/*
+ * Hyper-V uses the software reserved clean bit in VMCB
+ */
+#define VMCB_HV_NESTED_ENLIGHTENMENTS VMCB_SW
+
static inline void svm_hv_init_vmcb(struct vmcb *vmcb)
{
struct hv_enlightenments *hve =
@@ -52,6 +57,23 @@ static inline void svm_hv_hardware_setup(void)
}
}
+static inline void svm_hv_vmcb_dirty_nested_enlightenments(
+ struct kvm_vcpu *vcpu)
+{
+ struct vmcb *vmcb = to_svm(vcpu)->vmcb;
+ struct hv_enlightenments *hve =
+ (struct hv_enlightenments *)vmcb->control.reserved_sw;
+
+ /*
+ * vmcb can be NULL if called during early vcpu init.
+ * And its okay not to mark vmcb dirty during vcpu init
+ * as we mark it dirty unconditionally towards end of vcpu
+ * init phase.
+ */
+ if (vmcb && vmcb_is_clean(vmcb, VMCB_HV_NESTED_ENLIGHTENMENTS) &&
+ hve->hv_enlightenments_control.msr_bitmap)
+ vmcb_mark_dirty(vmcb, VMCB_HV_NESTED_ENLIGHTENMENTS);
+}
#else
static inline void svm_hv_init_vmcb(struct vmcb *vmcb)
@@ -61,6 +83,11 @@ static inline void svm_hv_init_vmcb(struct vmcb *vmcb)
static inline void svm_hv_hardware_setup(void)
{
}
+
+static inline void svm_hv_vmcb_dirty_nested_enlightenments(
+ struct kvm_vcpu *vcpu)
+{
+}
#endif /* CONFIG_HYPERV */
#endif /* __ARCH_X86_KVM_SVM_ONHYPERV_H__ */
--
2.25.1
SVM added support for certain reserved fields to be used by
software or hypervisor. Add the following reserved fields:
- VMCB offset 0x3e0 - 0x3ff
- Clean bit 31
- SVM intercept exit code 0xf0000000
Later patches will make use of this for supporting Hyper-V
nested virtualization enhancements.
Signed-off-by: Vineeth Pillai <[email protected]>
---
arch/x86/include/asm/svm.h | 9 +++++++--
arch/x86/include/uapi/asm/svm.h | 3 +++
arch/x86/kvm/svm/svm.h | 17 +++++++++++++++--
3 files changed, 25 insertions(+), 4 deletions(-)
diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h
index 772e60efe243..e322676039f4 100644
--- a/arch/x86/include/asm/svm.h
+++ b/arch/x86/include/asm/svm.h
@@ -156,6 +156,12 @@ struct __attribute__ ((__packed__)) vmcb_control_area {
u64 avic_physical_id; /* Offset 0xf8 */
u8 reserved_7[8];
u64 vmsa_pa; /* Used for an SEV-ES guest */
+ u8 reserved_8[720];
+ /*
+ * Offset 0x3e0, 32 bytes reserved
+ * for use by hypervisor/software.
+ */
+ u8 reserved_sw[32];
};
@@ -314,7 +320,7 @@ struct ghcb {
#define EXPECTED_VMCB_SAVE_AREA_SIZE 1032
-#define EXPECTED_VMCB_CONTROL_AREA_SIZE 272
+#define EXPECTED_VMCB_CONTROL_AREA_SIZE 1024
#define EXPECTED_GHCB_SIZE PAGE_SIZE
static inline void __unused_size_checks(void)
@@ -326,7 +332,6 @@ static inline void __unused_size_checks(void)
struct vmcb {
struct vmcb_control_area control;
- u8 reserved_control[1024 - sizeof(struct vmcb_control_area)];
struct vmcb_save_area save;
} __packed;
diff --git a/arch/x86/include/uapi/asm/svm.h b/arch/x86/include/uapi/asm/svm.h
index 554f75fe013c..efa969325ede 100644
--- a/arch/x86/include/uapi/asm/svm.h
+++ b/arch/x86/include/uapi/asm/svm.h
@@ -110,6 +110,9 @@
#define SVM_VMGEXIT_GET_AP_JUMP_TABLE 1
#define SVM_VMGEXIT_UNSUPPORTED_EVENT 0x8000ffff
+/* Exit code reserved for hypervisor/software use */
+#define SVM_EXIT_SW 0xf0000000
+
#define SVM_EXIT_ERR -1
#define SVM_EXIT_REASONS \
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index 84b3133c2251..eb4b91832912 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -31,6 +31,11 @@
extern u32 msrpm_offsets[MSRPM_OFFSETS] __read_mostly;
extern bool npt_enabled;
+/*
+ * Clean bits in VMCB.
+ * VMCB_ALL_CLEAN_MASK might also need to
+ * be updated if this enum is modified.
+ */
enum {
VMCB_INTERCEPTS, /* Intercept vectors, TSC offset,
pause filter count */
@@ -48,9 +53,17 @@ enum {
* AVIC PHYSICAL_TABLE pointer,
* AVIC LOGICAL_TABLE pointer
*/
- VMCB_DIRTY_MAX,
+ VMCB_SW = 31, /* Reserved for hypervisor/software use */
};
+#define VMCB_ALL_CLEAN_MASK ( \
+ (1U << VMCB_INTERCEPTS) | (1U << VMCB_PERM_MAP) | \
+ (1U << VMCB_ASID) | (1U << VMCB_INTR) | \
+ (1U << VMCB_NPT) | (1U << VMCB_CR) | (1U << VMCB_DR) | \
+ (1U << VMCB_DT) | (1U << VMCB_SEG) | (1U << VMCB_CR2) | \
+ (1U << VMCB_LBR) | (1U << VMCB_AVIC) | \
+ (1U << VMCB_SW))
+
/* TPR and CR2 are always written before VMRUN */
#define VMCB_ALWAYS_DIRTY_MASK ((1U << VMCB_INTR) | (1U << VMCB_CR2))
@@ -237,7 +250,7 @@ static inline void vmcb_mark_all_dirty(struct vmcb *vmcb)
static inline void vmcb_mark_all_clean(struct vmcb *vmcb)
{
- vmcb->control.clean = ((1 << VMCB_DIRTY_MAX) - 1)
+ vmcb->control.clean = VMCB_ALL_CLEAN_MASK
& ~VMCB_ALWAYS_DIRTY_MASK;
}
--
2.25.1
Previously, to detect nested virtualization enlightenment support,
we were using HV_X64_ENLIGHTENED_VMCS_RECOMMENDED feature bit of
HYPERV_CPUID_ENLIGHTMENT_INFO.EAX CPUID as docuemented in TLFS:
"Bit 14: Recommend a nested hypervisor using the enlightened VMCS
interface. Also indicates that additional nested enlightenments
may be available (see leaf 0x4000000A)".
Enlightened VMCS, however, is an Intel only feature so the above
detection method doesn't work for AMD. So, use the
HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS.EAX CPUID information ("The
maximum input value for hypervisor CPUID information.") and this
works for both AMD and Intel.
Signed-off-by: Vineeth Pillai <[email protected]>
---
arch/x86/kernel/cpu/mshyperv.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c
index 22f13343b5da..c268c2730048 100644
--- a/arch/x86/kernel/cpu/mshyperv.c
+++ b/arch/x86/kernel/cpu/mshyperv.c
@@ -252,6 +252,7 @@ static void __init hv_smp_prepare_cpus(unsigned int max_cpus)
static void __init ms_hyperv_init_platform(void)
{
+ int hv_max_functions_eax;
int hv_host_info_eax;
int hv_host_info_ebx;
int hv_host_info_ecx;
@@ -269,6 +270,8 @@ static void __init ms_hyperv_init_platform(void)
ms_hyperv.misc_features = cpuid_edx(HYPERV_CPUID_FEATURES);
ms_hyperv.hints = cpuid_eax(HYPERV_CPUID_ENLIGHTMENT_INFO);
+ hv_max_functions_eax = cpuid_eax(HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS);
+
pr_info("Hyper-V: privilege flags low 0x%x, high 0x%x, hints 0x%x, misc 0x%x\n",
ms_hyperv.features, ms_hyperv.priv_high, ms_hyperv.hints,
ms_hyperv.misc_features);
@@ -298,8 +301,7 @@ static void __init ms_hyperv_init_platform(void)
/*
* Extract host information.
*/
- if (cpuid_eax(HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS) >=
- HYPERV_CPUID_VERSION) {
+ if (hv_max_functions_eax >= HYPERV_CPUID_VERSION) {
hv_host_info_eax = cpuid_eax(HYPERV_CPUID_VERSION);
hv_host_info_ebx = cpuid_ebx(HYPERV_CPUID_VERSION);
hv_host_info_ecx = cpuid_ecx(HYPERV_CPUID_VERSION);
@@ -325,9 +327,11 @@ static void __init ms_hyperv_init_platform(void)
ms_hyperv.isolation_config_a, ms_hyperv.isolation_config_b);
}
- if (ms_hyperv.hints & HV_X64_ENLIGHTENED_VMCS_RECOMMENDED) {
+ if (hv_max_functions_eax >= HYPERV_CPUID_NESTED_FEATURES) {
ms_hyperv.nested_features =
cpuid_eax(HYPERV_CPUID_NESTED_FEATURES);
+ pr_info("Hyper-V: Nested features: 0x%x\n",
+ ms_hyperv.nested_features);
}
/*
--
2.25.1
Currently the remote TLB flush logic is specific to VMX.
Move it to a common place so that SVM can use it as well.
Signed-off-by: Vineeth Pillai <[email protected]>
---
arch/x86/include/asm/kvm_host.h | 9 +++
arch/x86/kvm/Makefile | 5 ++
arch/x86/kvm/kvm_onhyperv.c | 93 ++++++++++++++++++++++++++++
arch/x86/kvm/kvm_onhyperv.h | 32 ++++++++++
arch/x86/kvm/vmx/vmx.c | 105 +-------------------------------
arch/x86/kvm/vmx/vmx.h | 9 ---
arch/x86/kvm/x86.c | 9 +++
7 files changed, 150 insertions(+), 112 deletions(-)
create mode 100644 arch/x86/kvm/kvm_onhyperv.c
create mode 100644 arch/x86/kvm/kvm_onhyperv.h
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index cbbcee0a84f9..bab305230e8d 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -849,6 +849,10 @@ struct kvm_vcpu_arch {
/* Protected Guests */
bool guest_state_protected;
+
+#if IS_ENABLED(CONFIG_HYPERV)
+ hpa_t hv_root_tdp;
+#endif
};
struct kvm_lpage_info {
@@ -1122,6 +1126,11 @@ struct kvm_arch {
*/
spinlock_t tdp_mmu_pages_lock;
#endif /* CONFIG_X86_64 */
+
+#if IS_ENABLED(CONFIG_HYPERV)
+ hpa_t hv_root_tdp;
+ spinlock_t hv_root_tdp_lock;
+#endif
};
struct kvm_vm_stat {
diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile
index c589db5d91b3..a06745c2fef1 100644
--- a/arch/x86/kvm/Makefile
+++ b/arch/x86/kvm/Makefile
@@ -18,6 +18,11 @@ kvm-y += x86.o emulate.o i8259.o irq.o lapic.o \
i8254.o ioapic.o irq_comm.o cpuid.o pmu.o mtrr.o \
hyperv.o debugfs.o mmu/mmu.o mmu/page_track.o \
mmu/spte.o
+
+ifdef CONFIG_HYPERV
+kvm-y += kvm_onhyperv.o
+endif
+
kvm-$(CONFIG_X86_64) += mmu/tdp_iter.o mmu/tdp_mmu.o
kvm-$(CONFIG_KVM_XEN) += xen.o
diff --git a/arch/x86/kvm/kvm_onhyperv.c b/arch/x86/kvm/kvm_onhyperv.c
new file mode 100644
index 000000000000..c7db2df50a7a
--- /dev/null
+++ b/arch/x86/kvm/kvm_onhyperv.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * KVM L1 hypervisor optimizations on Hyper-V.
+ */
+
+#include <linux/kvm_host.h>
+#include <asm/mshyperv.h>
+
+#include "hyperv.h"
+#include "kvm_onhyperv.h"
+
+static int kvm_fill_hv_flush_list_func(struct hv_guest_mapping_flush_list *flush,
+ void *data)
+{
+ struct kvm_tlb_range *range = data;
+
+ return hyperv_fill_flush_guest_mapping_list(flush, range->start_gfn,
+ range->pages);
+}
+
+static inline int hv_remote_flush_root_tdp(hpa_t root_tdp,
+ struct kvm_tlb_range *range)
+{
+ if (range)
+ return hyperv_flush_guest_mapping_range(root_tdp,
+ kvm_fill_hv_flush_list_func, (void *)range);
+ else
+ return hyperv_flush_guest_mapping(root_tdp);
+}
+
+int hv_remote_flush_tlb_with_range(struct kvm *kvm,
+ struct kvm_tlb_range *range)
+{
+ struct kvm_arch *kvm_arch = &kvm->arch;
+ struct kvm_vcpu *vcpu;
+ int ret = 0, i, nr_unique_valid_roots;
+ hpa_t root;
+
+ spin_lock(&kvm_arch->hv_root_tdp_lock);
+
+ if (!VALID_PAGE(kvm_arch->hv_root_tdp)) {
+ nr_unique_valid_roots = 0;
+
+ /*
+ * Flush all valid roots, and see if all vCPUs have converged
+ * on a common root, in which case future flushes can skip the
+ * loop and flush the common root.
+ */
+ kvm_for_each_vcpu(i, vcpu, kvm) {
+ root = vcpu->arch.hv_root_tdp;
+ if (!VALID_PAGE(root) || root == kvm_arch->hv_root_tdp)
+ continue;
+
+ /*
+ * Set the tracked root to the first valid root. Keep
+ * this root for the entirety of the loop even if more
+ * roots are encountered as a low effort optimization
+ * to avoid flushing the same (first) root again.
+ */
+ if (++nr_unique_valid_roots == 1)
+ kvm_arch->hv_root_tdp = root;
+
+ if (!ret)
+ ret = hv_remote_flush_root_tdp(root, range);
+
+ /*
+ * Stop processing roots if a failure occurred and
+ * multiple valid roots have already been detected.
+ */
+ if (ret && nr_unique_valid_roots > 1)
+ break;
+ }
+
+ /*
+ * The optimized flush of a single root can't be used if there
+ * are multiple valid roots (obviously).
+ */
+ if (nr_unique_valid_roots > 1)
+ kvm_arch->hv_root_tdp = INVALID_PAGE;
+ } else {
+ ret = hv_remote_flush_root_tdp(kvm_arch->hv_root_tdp, range);
+ }
+
+ spin_unlock(&kvm_arch->hv_root_tdp_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(hv_remote_flush_tlb_with_range);
+
+int hv_remote_flush_tlb(struct kvm *kvm)
+{
+ return hv_remote_flush_tlb_with_range(kvm, NULL);
+}
+EXPORT_SYMBOL_GPL(hv_remote_flush_tlb);
diff --git a/arch/x86/kvm/kvm_onhyperv.h b/arch/x86/kvm/kvm_onhyperv.h
new file mode 100644
index 000000000000..c03f01024a70
--- /dev/null
+++ b/arch/x86/kvm/kvm_onhyperv.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * KVM L1 hypervisor optimizations on Hyper-V.
+ */
+
+#ifndef __ARCH_X86_KVM_KVM_ONHYPERV_H__
+#define __ARCH_X86_KVM_KVM_ONHYPERV_H__
+
+#if IS_ENABLED(CONFIG_HYPERV)
+int hv_remote_flush_tlb_with_range(struct kvm *kvm,
+ struct kvm_tlb_range *range);
+int hv_remote_flush_tlb(struct kvm *kvm);
+
+static inline void hv_track_root_tdp(struct kvm_vcpu *vcpu, hpa_t root_tdp)
+{
+ struct kvm_arch *kvm_arch = &vcpu->kvm->arch;
+
+ if (kvm_x86_ops.tlb_remote_flush == hv_remote_flush_tlb) {
+ spin_lock(&kvm_arch->hv_root_tdp_lock);
+ vcpu->arch.hv_root_tdp = root_tdp;
+ if (root_tdp != kvm_arch->hv_root_tdp)
+ kvm_arch->hv_root_tdp = INVALID_PAGE;
+ spin_unlock(&kvm_arch->hv_root_tdp_lock);
+ }
+}
+#else
+static inline void hv_track_root_tdp(struct kvm_vcpu *vcpu, hpa_t root_tdp)
+{
+}
+#endif
+#endif
+
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index d000cddbd734..117fb88cd354 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -52,6 +52,7 @@
#include "cpuid.h"
#include "evmcs.h"
#include "hyperv.h"
+#include "kvm_onhyperv.h"
#include "irq.h"
#include "kvm_cache_regs.h"
#include "lapic.h"
@@ -474,86 +475,6 @@ static const u32 vmx_uret_msrs_list[] = {
static bool __read_mostly enlightened_vmcs = true;
module_param(enlightened_vmcs, bool, 0444);
-static int kvm_fill_hv_flush_list_func(struct hv_guest_mapping_flush_list *flush,
- void *data)
-{
- struct kvm_tlb_range *range = data;
-
- return hyperv_fill_flush_guest_mapping_list(flush, range->start_gfn,
- range->pages);
-}
-
-static inline int hv_remote_flush_root_ept(hpa_t root_ept,
- struct kvm_tlb_range *range)
-{
- if (range)
- return hyperv_flush_guest_mapping_range(root_ept,
- kvm_fill_hv_flush_list_func, (void *)range);
- else
- return hyperv_flush_guest_mapping(root_ept);
-}
-
-static int hv_remote_flush_tlb_with_range(struct kvm *kvm,
- struct kvm_tlb_range *range)
-{
- struct kvm_vmx *kvm_vmx = to_kvm_vmx(kvm);
- struct kvm_vcpu *vcpu;
- int ret = 0, i, nr_unique_valid_roots;
- hpa_t root;
-
- spin_lock(&kvm_vmx->hv_root_ept_lock);
-
- if (!VALID_PAGE(kvm_vmx->hv_root_ept)) {
- nr_unique_valid_roots = 0;
-
- /*
- * Flush all valid roots, and see if all vCPUs have converged
- * on a common root, in which case future flushes can skip the
- * loop and flush the common root.
- */
- kvm_for_each_vcpu(i, vcpu, kvm) {
- root = to_vmx(vcpu)->hv_root_ept;
- if (!VALID_PAGE(root) || root == kvm_vmx->hv_root_ept)
- continue;
-
- /*
- * Set the tracked root to the first valid root. Keep
- * this root for the entirety of the loop even if more
- * roots are encountered as a low effort optimization
- * to avoid flushing the same (first) root again.
- */
- if (++nr_unique_valid_roots == 1)
- kvm_vmx->hv_root_ept = root;
-
- if (!ret)
- ret = hv_remote_flush_root_ept(root, range);
-
- /*
- * Stop processing roots if a failure occurred and
- * multiple valid roots have already been detected.
- */
- if (ret && nr_unique_valid_roots > 1)
- break;
- }
-
- /*
- * The optimized flush of a single root can't be used if there
- * are multiple valid roots (obviously).
- */
- if (nr_unique_valid_roots > 1)
- kvm_vmx->hv_root_ept = INVALID_PAGE;
- } else {
- ret = hv_remote_flush_root_ept(kvm_vmx->hv_root_ept, range);
- }
-
- spin_unlock(&kvm_vmx->hv_root_ept_lock);
- return ret;
-}
-static int hv_remote_flush_tlb(struct kvm *kvm)
-{
- return hv_remote_flush_tlb_with_range(kvm, NULL);
-}
-
static int hv_enable_direct_tlbflush(struct kvm_vcpu *vcpu)
{
struct hv_enlightened_vmcs *evmcs;
@@ -581,21 +502,6 @@ static int hv_enable_direct_tlbflush(struct kvm_vcpu *vcpu)
#endif /* IS_ENABLED(CONFIG_HYPERV) */
-static void hv_track_root_ept(struct kvm_vcpu *vcpu, hpa_t root_ept)
-{
-#if IS_ENABLED(CONFIG_HYPERV)
- struct kvm_vmx *kvm_vmx = to_kvm_vmx(vcpu->kvm);
-
- if (kvm_x86_ops.tlb_remote_flush == hv_remote_flush_tlb) {
- spin_lock(&kvm_vmx->hv_root_ept_lock);
- to_vmx(vcpu)->hv_root_ept = root_ept;
- if (root_ept != kvm_vmx->hv_root_ept)
- kvm_vmx->hv_root_ept = INVALID_PAGE;
- spin_unlock(&kvm_vmx->hv_root_ept_lock);
- }
-#endif
-}
-
/*
* Comment's format: document - errata name - stepping - processor name.
* Refer from
@@ -3202,7 +3108,7 @@ static void vmx_load_mmu_pgd(struct kvm_vcpu *vcpu, hpa_t root_hpa,
eptp = construct_eptp(vcpu, root_hpa, root_level);
vmcs_write64(EPT_POINTER, eptp);
- hv_track_root_ept(vcpu, root_hpa);
+ hv_track_root_tdp(vcpu, root_hpa);
if (!enable_unrestricted_guest && !is_paging(vcpu))
guest_cr3 = to_kvm_vmx(kvm)->ept_identity_map_addr;
@@ -6980,9 +6886,6 @@ static int vmx_create_vcpu(struct kvm_vcpu *vcpu)
vmx->pi_desc.nv = POSTED_INTR_VECTOR;
vmx->pi_desc.sn = 1;
-#if IS_ENABLED(CONFIG_HYPERV)
- vmx->hv_root_ept = INVALID_PAGE;
-#endif
return 0;
free_vmcs:
@@ -6999,10 +6902,6 @@ static int vmx_create_vcpu(struct kvm_vcpu *vcpu)
static int vmx_vm_init(struct kvm *kvm)
{
-#if IS_ENABLED(CONFIG_HYPERV)
- spin_lock_init(&to_kvm_vmx(kvm)->hv_root_ept_lock);
-#endif
-
if (!ple_gap)
kvm->arch.pause_in_guest = true;
diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h
index 008cb87ff088..d1363e734a01 100644
--- a/arch/x86/kvm/vmx/vmx.h
+++ b/arch/x86/kvm/vmx/vmx.h
@@ -328,10 +328,6 @@ struct vcpu_vmx {
/* SGX Launch Control public key hash */
u64 msr_ia32_sgxlepubkeyhash[4];
-#if IS_ENABLED(CONFIG_HYPERV)
- u64 hv_root_ept;
-#endif
-
struct pt_desc pt_desc;
struct lbr_desc lbr_desc;
@@ -349,11 +345,6 @@ struct kvm_vmx {
unsigned int tss_addr;
bool ept_identity_pagetable_done;
gpa_t ept_identity_map_addr;
-
-#if IS_ENABLED(CONFIG_HYPERV)
- hpa_t hv_root_ept;
- spinlock_t hv_root_ept_lock;
-#endif
};
bool nested_vmx_allowed(struct kvm_vcpu *vcpu);
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 6eda2834fc05..580f3c6c86f9 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -10279,6 +10279,10 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
vcpu->arch.pending_external_vector = -1;
vcpu->arch.preempted_in_kernel = false;
+#if IS_ENABLED(CONFIG_HYPERV)
+ vcpu->arch.hv_root_tdp = INVALID_PAGE;
+#endif
+
r = static_call(kvm_x86_vcpu_create)(vcpu);
if (r)
goto free_guest_fpu;
@@ -10662,6 +10666,11 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
kvm->arch.guest_can_read_msr_platform_info = true;
+#if IS_ENABLED(CONFIG_HYPERV)
+ spin_lock_init(&kvm->arch.hv_root_tdp_lock);
+ kvm->arch.hv_root_tdp = INVALID_PAGE;
+#endif
+
INIT_DELAYED_WORK(&kvm->arch.kvmclock_update_work, kvmclock_update_fn);
INIT_DELAYED_WORK(&kvm->arch.kvmclock_sync_work, kvmclock_sync_fn);
--
2.25.1
From Hyper-V TLFS:
"The hypervisor exposes hypercalls (HvFlushVirtualAddressSpace,
HvFlushVirtualAddressSpaceEx, HvFlushVirtualAddressList, and
HvFlushVirtualAddressListEx) that allow operating systems to more
efficiently manage the virtual TLB. The L1 hypervisor can choose to
allow its guest to use those hypercalls and delegate the responsibility
to handle them to the L0 hypervisor. This requires the use of a
partition assist page."
Add the Direct Virtual Flush support for SVM.
Related VMX changes:
commit 6f6a657c9998 ("KVM/Hyper-V/VMX: Add direct tlb flush support")
Signed-off-by: Vineeth Pillai <[email protected]>
---
arch/x86/kvm/Makefile | 4 ++++
arch/x86/kvm/svm/svm.c | 2 ++
arch/x86/kvm/svm/svm_onhyperv.c | 41 +++++++++++++++++++++++++++++++++
arch/x86/kvm/svm/svm_onhyperv.h | 36 +++++++++++++++++++++++++++++
4 files changed, 83 insertions(+)
create mode 100644 arch/x86/kvm/svm/svm_onhyperv.c
diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile
index a06745c2fef1..83331376b779 100644
--- a/arch/x86/kvm/Makefile
+++ b/arch/x86/kvm/Makefile
@@ -32,6 +32,10 @@ kvm-intel-$(CONFIG_X86_SGX_KVM) += vmx/sgx.o
kvm-amd-y += svm/svm.o svm/vmenter.o svm/pmu.o svm/nested.o svm/avic.o svm/sev.o
+ifdef CONFIG_HYPERV
+kvm-amd-y += svm/svm_onhyperv.o
+endif
+
obj-$(CONFIG_KVM) += kvm.o
obj-$(CONFIG_KVM_INTEL) += kvm-intel.o
obj-$(CONFIG_KVM_AMD) += kvm-amd.o
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index d2a625411059..5139cb6baadc 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -3779,6 +3779,8 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu)
}
svm->vmcb->save.cr2 = vcpu->arch.cr2;
+ svm_hv_update_vp_id(svm->vmcb, vcpu);
+
/*
* Run with all-zero DR6 unless needed, so that we can get the exact cause
* of a #DB.
diff --git a/arch/x86/kvm/svm/svm_onhyperv.c b/arch/x86/kvm/svm/svm_onhyperv.c
new file mode 100644
index 000000000000..3281856ebd94
--- /dev/null
+++ b/arch/x86/kvm/svm/svm_onhyperv.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * KVM L1 hypervisor optimizations on Hyper-V for SVM.
+ */
+
+#include <linux/kvm_host.h>
+#include "kvm_cache_regs.h"
+
+#include <asm/mshyperv.h>
+
+#include "svm.h"
+#include "svm_ops.h"
+
+#include "hyperv.h"
+#include "kvm_onhyperv.h"
+#include "svm_onhyperv.h"
+
+int hv_enable_direct_tlbflush(struct kvm_vcpu *vcpu)
+{
+ struct hv_enlightenments *hve;
+ struct hv_partition_assist_pg **p_hv_pa_pg =
+ &to_kvm_hv(vcpu->kvm)->hv_pa_pg;
+
+ if (!*p_hv_pa_pg)
+ *p_hv_pa_pg = kzalloc(PAGE_SIZE, GFP_KERNEL);
+
+ if (!*p_hv_pa_pg)
+ return -ENOMEM;
+
+ hve = (struct hv_enlightenments *)to_svm(vcpu)->vmcb->control.reserved_sw;
+
+ hve->partition_assist_page = __pa(*p_hv_pa_pg);
+ hve->hv_vm_id = (unsigned long)vcpu->kvm;
+ if (!hve->hv_enlightenments_control.nested_flush_hypercall) {
+ hve->hv_enlightenments_control.nested_flush_hypercall = 1;
+ vmcb_mark_dirty(to_svm(vcpu)->vmcb, VMCB_HV_NESTED_ENLIGHTENMENTS);
+ }
+
+ return 0;
+}
+
diff --git a/arch/x86/kvm/svm/svm_onhyperv.h b/arch/x86/kvm/svm/svm_onhyperv.h
index 0f262460b2e6..7487052fcef8 100644
--- a/arch/x86/kvm/svm/svm_onhyperv.h
+++ b/arch/x86/kvm/svm/svm_onhyperv.h
@@ -36,6 +36,8 @@ struct hv_enlightenments {
*/
#define VMCB_HV_NESTED_ENLIGHTENMENTS VMCB_SW
+int hv_enable_direct_tlbflush(struct kvm_vcpu *vcpu);
+
static inline void svm_hv_init_vmcb(struct vmcb *vmcb)
{
struct hv_enlightenments *hve =
@@ -55,6 +57,23 @@ static inline void svm_hv_hardware_setup(void)
svm_x86_ops.tlb_remote_flush_with_range =
hv_remote_flush_tlb_with_range;
}
+
+ if (ms_hyperv.nested_features & HV_X64_NESTED_DIRECT_FLUSH) {
+ int cpu;
+
+ pr_info("kvm: Hyper-V Direct TLB Flush enabled\n");
+ for_each_online_cpu(cpu) {
+ struct hv_vp_assist_page *vp_ap =
+ hv_get_vp_assist_page(cpu);
+
+ if (!vp_ap)
+ continue;
+
+ vp_ap->nested_control.features.directhypercall = 1;
+ }
+ svm_x86_ops.enable_direct_tlbflush =
+ hv_enable_direct_tlbflush;
+ }
}
static inline void svm_hv_vmcb_dirty_nested_enlightenments(
@@ -74,6 +93,18 @@ static inline void svm_hv_vmcb_dirty_nested_enlightenments(
hve->hv_enlightenments_control.msr_bitmap)
vmcb_mark_dirty(vmcb, VMCB_HV_NESTED_ENLIGHTENMENTS);
}
+
+static inline void svm_hv_update_vp_id(struct vmcb *vmcb,
+ struct kvm_vcpu *vcpu)
+{
+ struct hv_enlightenments *hve =
+ (struct hv_enlightenments *)vmcb->control.reserved_sw;
+
+ if (hve->hv_vp_id != to_hv_vcpu(vcpu)->vp_index) {
+ hve->hv_vp_id = to_hv_vcpu(vcpu)->vp_index;
+ vmcb_mark_dirty(vmcb, VMCB_HV_NESTED_ENLIGHTENMENTS);
+ }
+}
#else
static inline void svm_hv_init_vmcb(struct vmcb *vmcb)
@@ -88,6 +119,11 @@ static inline void svm_hv_vmcb_dirty_nested_enlightenments(
struct kvm_vcpu *vcpu)
{
}
+
+static inline void svm_hv_update_vp_id(struct vmcb *vmcb,
+ struct kvm_vcpu *vcpu)
+{
+}
#endif /* CONFIG_HYPERV */
#endif /* __ARCH_X86_KVM_SVM_ONHYPERV_H__ */
--
2.25.1
Enable remote TLB flush for SVM.
Signed-off-by: Vineeth Pillai <[email protected]>
---
arch/x86/kvm/svm/svm.c | 9 +++++
arch/x86/kvm/svm/svm_onhyperv.h | 66 +++++++++++++++++++++++++++++++++
2 files changed, 75 insertions(+)
create mode 100644 arch/x86/kvm/svm/svm_onhyperv.h
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index b649f92287a2..a39865dbc200 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -43,6 +43,9 @@
#include "svm.h"
#include "svm_ops.h"
+#include "kvm_onhyperv.h"
+#include "svm_onhyperv.h"
+
#define __ex(x) __kvm_handle_fault_on_reboot(x)
MODULE_AUTHOR("Qumranet");
@@ -992,6 +995,8 @@ static __init int svm_hardware_setup(void)
/* Note, SEV setup consumes npt_enabled. */
sev_hardware_setup();
+ svm_hv_hardware_setup();
+
svm_adjust_mmio_mask();
for_each_possible_cpu(cpu) {
@@ -1276,6 +1281,8 @@ static void init_vmcb(struct kvm_vcpu *vcpu)
}
}
+ svm_hv_init_vmcb(svm->vmcb);
+
vmcb_mark_all_dirty(svm->vmcb);
enable_gif(svm);
@@ -3884,6 +3891,8 @@ static void svm_load_mmu_pgd(struct kvm_vcpu *vcpu, hpa_t root_hpa,
svm->vmcb->control.nested_cr3 = __sme_set(root_hpa);
vmcb_mark_dirty(svm->vmcb, VMCB_NPT);
+ hv_track_root_tdp(vcpu, root_hpa);
+
/* Loading L2's CR3 is handled by enter_svm_guest_mode. */
if (!test_bit(VCPU_EXREG_CR3, (ulong *)&vcpu->arch.regs_avail))
return;
diff --git a/arch/x86/kvm/svm/svm_onhyperv.h b/arch/x86/kvm/svm/svm_onhyperv.h
new file mode 100644
index 000000000000..57291e222395
--- /dev/null
+++ b/arch/x86/kvm/svm/svm_onhyperv.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * KVM L1 hypervisor optimizations on Hyper-V for SVM.
+ */
+
+#ifndef __ARCH_X86_KVM_SVM_ONHYPERV_H__
+#define __ARCH_X86_KVM_SVM_ONHYPERV_H__
+
+#if IS_ENABLED(CONFIG_HYPERV)
+#include <asm/mshyperv.h>
+
+#include "hyperv.h"
+#include "kvm_onhyperv.h"
+
+static struct kvm_x86_ops svm_x86_ops;
+
+/*
+ * Hyper-V uses the software reserved 32 bytes in VMCB
+ * control area to expose SVM enlightenments to guests.
+ */
+struct hv_enlightenments {
+ struct __packed hv_enlightenments_control {
+ u32 nested_flush_hypercall:1;
+ u32 msr_bitmap:1;
+ u32 enlightened_npt_tlb: 1;
+ u32 reserved:29;
+ } __packed hv_enlightenments_control;
+ u32 hv_vp_id;
+ u64 hv_vm_id;
+ u64 partition_assist_page;
+ u64 reserved;
+} __packed;
+
+static inline void svm_hv_init_vmcb(struct vmcb *vmcb)
+{
+ struct hv_enlightenments *hve =
+ (struct hv_enlightenments *)vmcb->control.reserved_sw;
+
+ if (npt_enabled &&
+ ms_hyperv.nested_features & HV_X64_NESTED_ENLIGHTENED_TLB)
+ hve->hv_enlightenments_control.enlightened_npt_tlb = 1;
+}
+
+static inline void svm_hv_hardware_setup(void)
+{
+ if (npt_enabled &&
+ ms_hyperv.nested_features & HV_X64_NESTED_ENLIGHTENED_TLB) {
+ pr_info("kvm: Hyper-V enlightened NPT TLB flush enabled\n");
+ svm_x86_ops.tlb_remote_flush = hv_remote_flush_tlb;
+ svm_x86_ops.tlb_remote_flush_with_range =
+ hv_remote_flush_tlb_with_range;
+ }
+}
+
+#else
+
+static inline void svm_hv_init_vmcb(struct vmcb *vmcb)
+{
+}
+
+static inline void svm_hv_hardware_setup(void)
+{
+}
+#endif /* CONFIG_HYPERV */
+
+#endif /* __ARCH_X86_KVM_SVM_ONHYPERV_H__ */
--
2.25.1
From: Vineeth Pillai <[email protected]> Sent: Thursday, June 3, 2021 8:15 AM
>
> Previously, to detect nested virtualization enlightenment support,
> we were using HV_X64_ENLIGHTENED_VMCS_RECOMMENDED feature bit of
> HYPERV_CPUID_ENLIGHTMENT_INFO.EAX CPUID as docuemented in TLFS:
s/docuemented/documented/
> "Bit 14: Recommend a nested hypervisor using the enlightened VMCS
> interface. Also indicates that additional nested enlightenments
> may be available (see leaf 0x4000000A)".
>
> Enlightened VMCS, however, is an Intel only feature so the above
> detection method doesn't work for AMD. So, use the
> HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS.EAX CPUID information ("The
> maximum input value for hypervisor CPUID information.") and this
> works for both AMD and Intel.
>
> Signed-off-by: Vineeth Pillai <[email protected]>
> ---
> arch/x86/kernel/cpu/mshyperv.c | 10 +++++++---
> 1 file changed, 7 insertions(+), 3 deletions(-)
>
> diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c
> index 22f13343b5da..c268c2730048 100644
> --- a/arch/x86/kernel/cpu/mshyperv.c
> +++ b/arch/x86/kernel/cpu/mshyperv.c
> @@ -252,6 +252,7 @@ static void __init hv_smp_prepare_cpus(unsigned int max_cpus)
>
> static void __init ms_hyperv_init_platform(void)
> {
> + int hv_max_functions_eax;
> int hv_host_info_eax;
> int hv_host_info_ebx;
> int hv_host_info_ecx;
> @@ -269,6 +270,8 @@ static void __init ms_hyperv_init_platform(void)
> ms_hyperv.misc_features = cpuid_edx(HYPERV_CPUID_FEATURES);
> ms_hyperv.hints = cpuid_eax(HYPERV_CPUID_ENLIGHTMENT_INFO);
>
> + hv_max_functions_eax =
> cpuid_eax(HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS);
> +
> pr_info("Hyper-V: privilege flags low 0x%x, high 0x%x, hints 0x%x, misc 0x%x\n",
> ms_hyperv.features, ms_hyperv.priv_high, ms_hyperv.hints,
> ms_hyperv.misc_features);
> @@ -298,8 +301,7 @@ static void __init ms_hyperv_init_platform(void)
> /*
> * Extract host information.
> */
> - if (cpuid_eax(HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS) >=
> - HYPERV_CPUID_VERSION) {
> + if (hv_max_functions_eax >= HYPERV_CPUID_VERSION) {
> hv_host_info_eax = cpuid_eax(HYPERV_CPUID_VERSION);
> hv_host_info_ebx = cpuid_ebx(HYPERV_CPUID_VERSION);
> hv_host_info_ecx = cpuid_ecx(HYPERV_CPUID_VERSION);
> @@ -325,9 +327,11 @@ static void __init ms_hyperv_init_platform(void)
> ms_hyperv.isolation_config_a, ms_hyperv.isolation_config_b);
> }
>
> - if (ms_hyperv.hints & HV_X64_ENLIGHTENED_VMCS_RECOMMENDED) {
> + if (hv_max_functions_eax >= HYPERV_CPUID_NESTED_FEATURES) {
> ms_hyperv.nested_features =
> cpuid_eax(HYPERV_CPUID_NESTED_FEATURES);
> + pr_info("Hyper-V: Nested features: 0x%x\n",
Nit: Drop the colon after "Nested features". Current code isn't very consistent
but I'm trying to establish the pattern of "Hyper-V:" followed by names and
values, with multiple name/value pairs separated by a comma.
> + ms_hyperv.nested_features);
> }
>
> /*
> --
> 2.25.1
Nits notwithstanding,
Reviewed-by: Michael Kelley <[email protected]>
From: Vineeth Pillai <[email protected]> Sent: Thursday, June 3, 2021 8:15 AM
>
> Enable remote TLB flush for SVM.
>
> Signed-off-by: Vineeth Pillai <[email protected]>
> ---
> arch/x86/kvm/svm/svm.c | 9 +++++
> arch/x86/kvm/svm/svm_onhyperv.h | 66 +++++++++++++++++++++++++++++++++
> 2 files changed, 75 insertions(+)
> create mode 100644 arch/x86/kvm/svm/svm_onhyperv.h
>
> diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
> index b649f92287a2..a39865dbc200 100644
> --- a/arch/x86/kvm/svm/svm.c
> +++ b/arch/x86/kvm/svm/svm.c
> @@ -43,6 +43,9 @@
> #include "svm.h"
> #include "svm_ops.h"
>
> +#include "kvm_onhyperv.h"
> +#include "svm_onhyperv.h"
> +
> #define __ex(x) __kvm_handle_fault_on_reboot(x)
>
> MODULE_AUTHOR("Qumranet");
> @@ -992,6 +995,8 @@ static __init int svm_hardware_setup(void)
> /* Note, SEV setup consumes npt_enabled. */
> sev_hardware_setup();
>
> + svm_hv_hardware_setup();
> +
> svm_adjust_mmio_mask();
>
> for_each_possible_cpu(cpu) {
> @@ -1276,6 +1281,8 @@ static void init_vmcb(struct kvm_vcpu *vcpu)
> }
> }
>
> + svm_hv_init_vmcb(svm->vmcb);
> +
> vmcb_mark_all_dirty(svm->vmcb);
>
> enable_gif(svm);
> @@ -3884,6 +3891,8 @@ static void svm_load_mmu_pgd(struct kvm_vcpu *vcpu, hpa_t
> root_hpa,
> svm->vmcb->control.nested_cr3 = __sme_set(root_hpa);
> vmcb_mark_dirty(svm->vmcb, VMCB_NPT);
>
> + hv_track_root_tdp(vcpu, root_hpa);
> +
> /* Loading L2's CR3 is handled by enter_svm_guest_mode. */
> if (!test_bit(VCPU_EXREG_CR3, (ulong *)&vcpu->arch.regs_avail))
> return;
> diff --git a/arch/x86/kvm/svm/svm_onhyperv.h b/arch/x86/kvm/svm/svm_onhyperv.h
> new file mode 100644
> index 000000000000..57291e222395
> --- /dev/null
> +++ b/arch/x86/kvm/svm/svm_onhyperv.h
> @@ -0,0 +1,66 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * KVM L1 hypervisor optimizations on Hyper-V for SVM.
> + */
> +
> +#ifndef __ARCH_X86_KVM_SVM_ONHYPERV_H__
> +#define __ARCH_X86_KVM_SVM_ONHYPERV_H__
> +
> +#if IS_ENABLED(CONFIG_HYPERV)
> +#include <asm/mshyperv.h>
> +
> +#include "hyperv.h"
> +#include "kvm_onhyperv.h"
> +
> +static struct kvm_x86_ops svm_x86_ops;
> +
> +/*
> + * Hyper-V uses the software reserved 32 bytes in VMCB
> + * control area to expose SVM enlightenments to guests.
> + */
> +struct hv_enlightenments {
> + struct __packed hv_enlightenments_control {
> + u32 nested_flush_hypercall:1;
> + u32 msr_bitmap:1;
> + u32 enlightened_npt_tlb: 1;
> + u32 reserved:29;
> + } __packed hv_enlightenments_control;
> + u32 hv_vp_id;
> + u64 hv_vm_id;
> + u64 partition_assist_page;
> + u64 reserved;
> +} __packed;
> +
> +static inline void svm_hv_init_vmcb(struct vmcb *vmcb)
> +{
> + struct hv_enlightenments *hve =
> + (struct hv_enlightenments *)vmcb->control.reserved_sw;
Perhaps add a "BUILD_BUG_ON" to verify that struct
hv_enlightenments fits in the space allocated for
vmcb->control.reserved_sw?
> +
> + if (npt_enabled &&
> + ms_hyperv.nested_features & HV_X64_NESTED_ENLIGHTENED_TLB)
> + hve->hv_enlightenments_control.enlightened_npt_tlb = 1;
> +}
> +
> +static inline void svm_hv_hardware_setup(void)
> +{
> + if (npt_enabled &&
> + ms_hyperv.nested_features & HV_X64_NESTED_ENLIGHTENED_TLB) {
> + pr_info("kvm: Hyper-V enlightened NPT TLB flush enabled\n");
> + svm_x86_ops.tlb_remote_flush = hv_remote_flush_tlb;
> + svm_x86_ops.tlb_remote_flush_with_range =
> + hv_remote_flush_tlb_with_range;
> + }
> +}
> +
> +#else
> +
> +static inline void svm_hv_init_vmcb(struct vmcb *vmcb)
> +{
> +}
> +
> +static inline void svm_hv_hardware_setup(void)
> +{
> +}
> +#endif /* CONFIG_HYPERV */
> +
> +#endif /* __ARCH_X86_KVM_SVM_ONHYPERV_H__ */
> --
> 2.25.1
Vineeth Pillai <[email protected]> writes:
> From Hyper-V TLFS:
> "The hypervisor exposes hypercalls (HvFlushVirtualAddressSpace,
> HvFlushVirtualAddressSpaceEx, HvFlushVirtualAddressList, and
> HvFlushVirtualAddressListEx) that allow operating systems to more
> efficiently manage the virtual TLB. The L1 hypervisor can choose to
> allow its guest to use those hypercalls and delegate the responsibility
> to handle them to the L0 hypervisor. This requires the use of a
> partition assist page."
>
> Add the Direct Virtual Flush support for SVM.
>
> Related VMX changes:
> commit 6f6a657c9998 ("KVM/Hyper-V/VMX: Add direct tlb flush support")
>
> Signed-off-by: Vineeth Pillai <[email protected]>
> ---
> arch/x86/kvm/Makefile | 4 ++++
> arch/x86/kvm/svm/svm.c | 2 ++
> arch/x86/kvm/svm/svm_onhyperv.c | 41 +++++++++++++++++++++++++++++++++
> arch/x86/kvm/svm/svm_onhyperv.h | 36 +++++++++++++++++++++++++++++
> 4 files changed, 83 insertions(+)
> create mode 100644 arch/x86/kvm/svm/svm_onhyperv.c
>
> diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile
> index a06745c2fef1..83331376b779 100644
> --- a/arch/x86/kvm/Makefile
> +++ b/arch/x86/kvm/Makefile
> @@ -32,6 +32,10 @@ kvm-intel-$(CONFIG_X86_SGX_KVM) += vmx/sgx.o
>
> kvm-amd-y += svm/svm.o svm/vmenter.o svm/pmu.o svm/nested.o svm/avic.o svm/sev.o
>
> +ifdef CONFIG_HYPERV
> +kvm-amd-y += svm/svm_onhyperv.o
> +endif
> +
> obj-$(CONFIG_KVM) += kvm.o
> obj-$(CONFIG_KVM_INTEL) += kvm-intel.o
> obj-$(CONFIG_KVM_AMD) += kvm-amd.o
> diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
> index d2a625411059..5139cb6baadc 100644
> --- a/arch/x86/kvm/svm/svm.c
> +++ b/arch/x86/kvm/svm/svm.c
> @@ -3779,6 +3779,8 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu)
> }
> svm->vmcb->save.cr2 = vcpu->arch.cr2;
>
> + svm_hv_update_vp_id(svm->vmcb, vcpu);
> +
> /*
> * Run with all-zero DR6 unless needed, so that we can get the exact cause
> * of a #DB.
> diff --git a/arch/x86/kvm/svm/svm_onhyperv.c b/arch/x86/kvm/svm/svm_onhyperv.c
> new file mode 100644
> index 000000000000..3281856ebd94
> --- /dev/null
> +++ b/arch/x86/kvm/svm/svm_onhyperv.c
> @@ -0,0 +1,41 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * KVM L1 hypervisor optimizations on Hyper-V for SVM.
> + */
> +
> +#include <linux/kvm_host.h>
> +#include "kvm_cache_regs.h"
> +
> +#include <asm/mshyperv.h>
> +
> +#include "svm.h"
> +#include "svm_ops.h"
> +
> +#include "hyperv.h"
> +#include "kvm_onhyperv.h"
> +#include "svm_onhyperv.h"
> +
> +int hv_enable_direct_tlbflush(struct kvm_vcpu *vcpu)
> +{
I would've avoided re-using 'hv_enable_direct_tlbflush()' name which we
already have in vmx. In fact, in the spirit of this patch, I'd suggest
we create arch/x86/kvm/vmx/vmx_onhyperv.c and move the existing
hv_enable_direct_tlbflush() there. We can then re-name it to e.g.
vmx_enable_hv_direct_tlbflush()
so the one introduced by this patch will be
svm_enable_hv_direct_tlbflush()
> + struct hv_enlightenments *hve;
> + struct hv_partition_assist_pg **p_hv_pa_pg =
> + &to_kvm_hv(vcpu->kvm)->hv_pa_pg;
> +
> + if (!*p_hv_pa_pg)
> + *p_hv_pa_pg = kzalloc(PAGE_SIZE, GFP_KERNEL);
> +
> + if (!*p_hv_pa_pg)
> + return -ENOMEM;
> +
> + hve = (struct hv_enlightenments *)to_svm(vcpu)->vmcb->control.reserved_sw;
> +
> + hve->partition_assist_page = __pa(*p_hv_pa_pg);
> + hve->hv_vm_id = (unsigned long)vcpu->kvm;
> + if (!hve->hv_enlightenments_control.nested_flush_hypercall) {
> + hve->hv_enlightenments_control.nested_flush_hypercall = 1;
> + vmcb_mark_dirty(to_svm(vcpu)->vmcb, VMCB_HV_NESTED_ENLIGHTENMENTS);
> + }
> +
> + return 0;
> +}
> +
> diff --git a/arch/x86/kvm/svm/svm_onhyperv.h b/arch/x86/kvm/svm/svm_onhyperv.h
> index 0f262460b2e6..7487052fcef8 100644
> --- a/arch/x86/kvm/svm/svm_onhyperv.h
> +++ b/arch/x86/kvm/svm/svm_onhyperv.h
> @@ -36,6 +36,8 @@ struct hv_enlightenments {
> */
> #define VMCB_HV_NESTED_ENLIGHTENMENTS VMCB_SW
>
> +int hv_enable_direct_tlbflush(struct kvm_vcpu *vcpu);
> +
> static inline void svm_hv_init_vmcb(struct vmcb *vmcb)
> {
> struct hv_enlightenments *hve =
> @@ -55,6 +57,23 @@ static inline void svm_hv_hardware_setup(void)
> svm_x86_ops.tlb_remote_flush_with_range =
> hv_remote_flush_tlb_with_range;
> }
> +
> + if (ms_hyperv.nested_features & HV_X64_NESTED_DIRECT_FLUSH) {
> + int cpu;
> +
> + pr_info("kvm: Hyper-V Direct TLB Flush enabled\n");
> + for_each_online_cpu(cpu) {
> + struct hv_vp_assist_page *vp_ap =
> + hv_get_vp_assist_page(cpu);
> +
> + if (!vp_ap)
> + continue;
> +
> + vp_ap->nested_control.features.directhypercall = 1;
> + }
> + svm_x86_ops.enable_direct_tlbflush =
> + hv_enable_direct_tlbflush;
> + }
> }
>
> static inline void svm_hv_vmcb_dirty_nested_enlightenments(
> @@ -74,6 +93,18 @@ static inline void svm_hv_vmcb_dirty_nested_enlightenments(
> hve->hv_enlightenments_control.msr_bitmap)
> vmcb_mark_dirty(vmcb, VMCB_HV_NESTED_ENLIGHTENMENTS);
> }
> +
> +static inline void svm_hv_update_vp_id(struct vmcb *vmcb,
> + struct kvm_vcpu *vcpu)
> +{
> + struct hv_enlightenments *hve =
> + (struct hv_enlightenments *)vmcb->control.reserved_sw;
> +
> + if (hve->hv_vp_id != to_hv_vcpu(vcpu)->vp_index) {
> + hve->hv_vp_id = to_hv_vcpu(vcpu)->vp_index;
> + vmcb_mark_dirty(vmcb, VMCB_HV_NESTED_ENLIGHTENMENTS);
> + }
> +}
> #else
>
> static inline void svm_hv_init_vmcb(struct vmcb *vmcb)
> @@ -88,6 +119,11 @@ static inline void svm_hv_vmcb_dirty_nested_enlightenments(
> struct kvm_vcpu *vcpu)
> {
> }
> +
> +static inline void svm_hv_update_vp_id(struct vmcb *vmcb,
> + struct kvm_vcpu *vcpu)
> +{
> +}
> #endif /* CONFIG_HYPERV */
>
> #endif /* __ARCH_X86_KVM_SVM_ONHYPERV_H__ */
--
Vitaly
Vineeth Pillai <[email protected]> writes:
> Currently the remote TLB flush logic is specific to VMX.
> Move it to a common place so that SVM can use it as well.
>
> Signed-off-by: Vineeth Pillai <[email protected]>
> ---
> arch/x86/include/asm/kvm_host.h | 9 +++
> arch/x86/kvm/Makefile | 5 ++
> arch/x86/kvm/kvm_onhyperv.c | 93 ++++++++++++++++++++++++++++
> arch/x86/kvm/kvm_onhyperv.h | 32 ++++++++++
> arch/x86/kvm/vmx/vmx.c | 105 +-------------------------------
> arch/x86/kvm/vmx/vmx.h | 9 ---
> arch/x86/kvm/x86.c | 9 +++
> 7 files changed, 150 insertions(+), 112 deletions(-)
> create mode 100644 arch/x86/kvm/kvm_onhyperv.c
> create mode 100644 arch/x86/kvm/kvm_onhyperv.h
>
> diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
> index cbbcee0a84f9..bab305230e8d 100644
> --- a/arch/x86/include/asm/kvm_host.h
> +++ b/arch/x86/include/asm/kvm_host.h
> @@ -849,6 +849,10 @@ struct kvm_vcpu_arch {
>
> /* Protected Guests */
> bool guest_state_protected;
> +
> +#if IS_ENABLED(CONFIG_HYPERV)
> + hpa_t hv_root_tdp;
> +#endif
> };
>
> struct kvm_lpage_info {
> @@ -1122,6 +1126,11 @@ struct kvm_arch {
> */
> spinlock_t tdp_mmu_pages_lock;
> #endif /* CONFIG_X86_64 */
> +
> +#if IS_ENABLED(CONFIG_HYPERV)
> + hpa_t hv_root_tdp;
> + spinlock_t hv_root_tdp_lock;
> +#endif
> };
>
> struct kvm_vm_stat {
> diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile
> index c589db5d91b3..a06745c2fef1 100644
> --- a/arch/x86/kvm/Makefile
> +++ b/arch/x86/kvm/Makefile
> @@ -18,6 +18,11 @@ kvm-y += x86.o emulate.o i8259.o irq.o lapic.o \
> i8254.o ioapic.o irq_comm.o cpuid.o pmu.o mtrr.o \
> hyperv.o debugfs.o mmu/mmu.o mmu/page_track.o \
> mmu/spte.o
> +
> +ifdef CONFIG_HYPERV
> +kvm-y += kvm_onhyperv.o
> +endif
> +
> kvm-$(CONFIG_X86_64) += mmu/tdp_iter.o mmu/tdp_mmu.o
> kvm-$(CONFIG_KVM_XEN) += xen.o
>
> diff --git a/arch/x86/kvm/kvm_onhyperv.c b/arch/x86/kvm/kvm_onhyperv.c
> new file mode 100644
> index 000000000000..c7db2df50a7a
> --- /dev/null
> +++ b/arch/x86/kvm/kvm_onhyperv.c
> @@ -0,0 +1,93 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * KVM L1 hypervisor optimizations on Hyper-V.
> + */
> +
> +#include <linux/kvm_host.h>
> +#include <asm/mshyperv.h>
> +
> +#include "hyperv.h"
> +#include "kvm_onhyperv.h"
> +
> +static int kvm_fill_hv_flush_list_func(struct hv_guest_mapping_flush_list *flush,
> + void *data)
> +{
> + struct kvm_tlb_range *range = data;
> +
> + return hyperv_fill_flush_guest_mapping_list(flush, range->start_gfn,
> + range->pages);
> +}
> +
> +static inline int hv_remote_flush_root_tdp(hpa_t root_tdp,
> + struct kvm_tlb_range *range)
> +{
> + if (range)
> + return hyperv_flush_guest_mapping_range(root_tdp,
> + kvm_fill_hv_flush_list_func, (void *)range);
> + else
> + return hyperv_flush_guest_mapping(root_tdp);
> +}
> +
> +int hv_remote_flush_tlb_with_range(struct kvm *kvm,
> + struct kvm_tlb_range *range)
> +{
> + struct kvm_arch *kvm_arch = &kvm->arch;
> + struct kvm_vcpu *vcpu;
> + int ret = 0, i, nr_unique_valid_roots;
> + hpa_t root;
> +
> + spin_lock(&kvm_arch->hv_root_tdp_lock);
> +
> + if (!VALID_PAGE(kvm_arch->hv_root_tdp)) {
> + nr_unique_valid_roots = 0;
> +
> + /*
> + * Flush all valid roots, and see if all vCPUs have converged
> + * on a common root, in which case future flushes can skip the
> + * loop and flush the common root.
> + */
> + kvm_for_each_vcpu(i, vcpu, kvm) {
> + root = vcpu->arch.hv_root_tdp;
> + if (!VALID_PAGE(root) || root == kvm_arch->hv_root_tdp)
> + continue;
> +
> + /*
> + * Set the tracked root to the first valid root. Keep
> + * this root for the entirety of the loop even if more
> + * roots are encountered as a low effort optimization
> + * to avoid flushing the same (first) root again.
> + */
> + if (++nr_unique_valid_roots == 1)
> + kvm_arch->hv_root_tdp = root;
> +
> + if (!ret)
> + ret = hv_remote_flush_root_tdp(root, range);
> +
> + /*
> + * Stop processing roots if a failure occurred and
> + * multiple valid roots have already been detected.
> + */
> + if (ret && nr_unique_valid_roots > 1)
> + break;
> + }
> +
> + /*
> + * The optimized flush of a single root can't be used if there
> + * are multiple valid roots (obviously).
> + */
> + if (nr_unique_valid_roots > 1)
> + kvm_arch->hv_root_tdp = INVALID_PAGE;
> + } else {
> + ret = hv_remote_flush_root_tdp(kvm_arch->hv_root_tdp, range);
> + }
> +
> + spin_unlock(&kvm_arch->hv_root_tdp_lock);
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(hv_remote_flush_tlb_with_range);
> +
> +int hv_remote_flush_tlb(struct kvm *kvm)
> +{
> + return hv_remote_flush_tlb_with_range(kvm, NULL);
> +}
> +EXPORT_SYMBOL_GPL(hv_remote_flush_tlb);
> diff --git a/arch/x86/kvm/kvm_onhyperv.h b/arch/x86/kvm/kvm_onhyperv.h
> new file mode 100644
> index 000000000000..c03f01024a70
> --- /dev/null
> +++ b/arch/x86/kvm/kvm_onhyperv.h
> @@ -0,0 +1,32 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * KVM L1 hypervisor optimizations on Hyper-V.
> + */
> +
> +#ifndef __ARCH_X86_KVM_KVM_ONHYPERV_H__
> +#define __ARCH_X86_KVM_KVM_ONHYPERV_H__
> +
> +#if IS_ENABLED(CONFIG_HYPERV)
> +int hv_remote_flush_tlb_with_range(struct kvm *kvm,
> + struct kvm_tlb_range *range);
> +int hv_remote_flush_tlb(struct kvm *kvm);
> +
> +static inline void hv_track_root_tdp(struct kvm_vcpu *vcpu, hpa_t root_tdp)
> +{
> + struct kvm_arch *kvm_arch = &vcpu->kvm->arch;
> +
> + if (kvm_x86_ops.tlb_remote_flush == hv_remote_flush_tlb) {
> + spin_lock(&kvm_arch->hv_root_tdp_lock);
> + vcpu->arch.hv_root_tdp = root_tdp;
> + if (root_tdp != kvm_arch->hv_root_tdp)
> + kvm_arch->hv_root_tdp = INVALID_PAGE;
> + spin_unlock(&kvm_arch->hv_root_tdp_lock);
> + }
> +}
> +#else
> +static inline void hv_track_root_tdp(struct kvm_vcpu *vcpu, hpa_t root_tdp)
> +{
> +}
> +#endif
> +#endif
Super-nitpick: I'd suggest adding /* __ARCH_X86_KVM_KVM_ONHYPERV_H__ */
to the second '#endif' and /* IS_ENABLED(CONFIG_HYPERV) */ to '#else'
and the first one: files/functions tend to grow and it becomes hard to
see where the particular '#endif/#else' belongs.
> +
> diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
> index d000cddbd734..117fb88cd354 100644
> --- a/arch/x86/kvm/vmx/vmx.c
> +++ b/arch/x86/kvm/vmx/vmx.c
> @@ -52,6 +52,7 @@
> #include "cpuid.h"
> #include "evmcs.h"
> #include "hyperv.h"
> +#include "kvm_onhyperv.h"
> #include "irq.h"
> #include "kvm_cache_regs.h"
> #include "lapic.h"
> @@ -474,86 +475,6 @@ static const u32 vmx_uret_msrs_list[] = {
> static bool __read_mostly enlightened_vmcs = true;
> module_param(enlightened_vmcs, bool, 0444);
>
> -static int kvm_fill_hv_flush_list_func(struct hv_guest_mapping_flush_list *flush,
> - void *data)
> -{
> - struct kvm_tlb_range *range = data;
> -
> - return hyperv_fill_flush_guest_mapping_list(flush, range->start_gfn,
> - range->pages);
> -}
> -
> -static inline int hv_remote_flush_root_ept(hpa_t root_ept,
> - struct kvm_tlb_range *range)
> -{
> - if (range)
> - return hyperv_flush_guest_mapping_range(root_ept,
> - kvm_fill_hv_flush_list_func, (void *)range);
> - else
> - return hyperv_flush_guest_mapping(root_ept);
> -}
> -
> -static int hv_remote_flush_tlb_with_range(struct kvm *kvm,
> - struct kvm_tlb_range *range)
> -{
> - struct kvm_vmx *kvm_vmx = to_kvm_vmx(kvm);
> - struct kvm_vcpu *vcpu;
> - int ret = 0, i, nr_unique_valid_roots;
> - hpa_t root;
> -
> - spin_lock(&kvm_vmx->hv_root_ept_lock);
> -
> - if (!VALID_PAGE(kvm_vmx->hv_root_ept)) {
> - nr_unique_valid_roots = 0;
> -
> - /*
> - * Flush all valid roots, and see if all vCPUs have converged
> - * on a common root, in which case future flushes can skip the
> - * loop and flush the common root.
> - */
> - kvm_for_each_vcpu(i, vcpu, kvm) {
> - root = to_vmx(vcpu)->hv_root_ept;
> - if (!VALID_PAGE(root) || root == kvm_vmx->hv_root_ept)
> - continue;
> -
> - /*
> - * Set the tracked root to the first valid root. Keep
> - * this root for the entirety of the loop even if more
> - * roots are encountered as a low effort optimization
> - * to avoid flushing the same (first) root again.
> - */
> - if (++nr_unique_valid_roots == 1)
> - kvm_vmx->hv_root_ept = root;
> -
> - if (!ret)
> - ret = hv_remote_flush_root_ept(root, range);
> -
> - /*
> - * Stop processing roots if a failure occurred and
> - * multiple valid roots have already been detected.
> - */
> - if (ret && nr_unique_valid_roots > 1)
> - break;
> - }
> -
> - /*
> - * The optimized flush of a single root can't be used if there
> - * are multiple valid roots (obviously).
> - */
> - if (nr_unique_valid_roots > 1)
> - kvm_vmx->hv_root_ept = INVALID_PAGE;
> - } else {
> - ret = hv_remote_flush_root_ept(kvm_vmx->hv_root_ept, range);
> - }
> -
> - spin_unlock(&kvm_vmx->hv_root_ept_lock);
> - return ret;
> -}
> -static int hv_remote_flush_tlb(struct kvm *kvm)
> -{
> - return hv_remote_flush_tlb_with_range(kvm, NULL);
> -}
> -
> static int hv_enable_direct_tlbflush(struct kvm_vcpu *vcpu)
> {
> struct hv_enlightened_vmcs *evmcs;
> @@ -581,21 +502,6 @@ static int hv_enable_direct_tlbflush(struct kvm_vcpu *vcpu)
>
> #endif /* IS_ENABLED(CONFIG_HYPERV) */
>
> -static void hv_track_root_ept(struct kvm_vcpu *vcpu, hpa_t root_ept)
> -{
> -#if IS_ENABLED(CONFIG_HYPERV)
> - struct kvm_vmx *kvm_vmx = to_kvm_vmx(vcpu->kvm);
> -
> - if (kvm_x86_ops.tlb_remote_flush == hv_remote_flush_tlb) {
> - spin_lock(&kvm_vmx->hv_root_ept_lock);
> - to_vmx(vcpu)->hv_root_ept = root_ept;
> - if (root_ept != kvm_vmx->hv_root_ept)
> - kvm_vmx->hv_root_ept = INVALID_PAGE;
> - spin_unlock(&kvm_vmx->hv_root_ept_lock);
> - }
> -#endif
> -}
> -
> /*
> * Comment's format: document - errata name - stepping - processor name.
> * Refer from
> @@ -3202,7 +3108,7 @@ static void vmx_load_mmu_pgd(struct kvm_vcpu *vcpu, hpa_t root_hpa,
> eptp = construct_eptp(vcpu, root_hpa, root_level);
> vmcs_write64(EPT_POINTER, eptp);
>
> - hv_track_root_ept(vcpu, root_hpa);
> + hv_track_root_tdp(vcpu, root_hpa);
>
> if (!enable_unrestricted_guest && !is_paging(vcpu))
> guest_cr3 = to_kvm_vmx(kvm)->ept_identity_map_addr;
> @@ -6980,9 +6886,6 @@ static int vmx_create_vcpu(struct kvm_vcpu *vcpu)
> vmx->pi_desc.nv = POSTED_INTR_VECTOR;
> vmx->pi_desc.sn = 1;
>
> -#if IS_ENABLED(CONFIG_HYPERV)
> - vmx->hv_root_ept = INVALID_PAGE;
> -#endif
> return 0;
>
> free_vmcs:
> @@ -6999,10 +6902,6 @@ static int vmx_create_vcpu(struct kvm_vcpu *vcpu)
>
> static int vmx_vm_init(struct kvm *kvm)
> {
> -#if IS_ENABLED(CONFIG_HYPERV)
> - spin_lock_init(&to_kvm_vmx(kvm)->hv_root_ept_lock);
> -#endif
> -
> if (!ple_gap)
> kvm->arch.pause_in_guest = true;
>
> diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h
> index 008cb87ff088..d1363e734a01 100644
> --- a/arch/x86/kvm/vmx/vmx.h
> +++ b/arch/x86/kvm/vmx/vmx.h
> @@ -328,10 +328,6 @@ struct vcpu_vmx {
> /* SGX Launch Control public key hash */
> u64 msr_ia32_sgxlepubkeyhash[4];
>
> -#if IS_ENABLED(CONFIG_HYPERV)
> - u64 hv_root_ept;
> -#endif
> -
> struct pt_desc pt_desc;
> struct lbr_desc lbr_desc;
>
> @@ -349,11 +345,6 @@ struct kvm_vmx {
> unsigned int tss_addr;
> bool ept_identity_pagetable_done;
> gpa_t ept_identity_map_addr;
> -
> -#if IS_ENABLED(CONFIG_HYPERV)
> - hpa_t hv_root_ept;
> - spinlock_t hv_root_ept_lock;
> -#endif
> };
>
> bool nested_vmx_allowed(struct kvm_vcpu *vcpu);
> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index 6eda2834fc05..580f3c6c86f9 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
> @@ -10279,6 +10279,10 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
> vcpu->arch.pending_external_vector = -1;
> vcpu->arch.preempted_in_kernel = false;
>
> +#if IS_ENABLED(CONFIG_HYPERV)
> + vcpu->arch.hv_root_tdp = INVALID_PAGE;
> +#endif
> +
> r = static_call(kvm_x86_vcpu_create)(vcpu);
> if (r)
> goto free_guest_fpu;
> @@ -10662,6 +10666,11 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
>
> kvm->arch.guest_can_read_msr_platform_info = true;
>
> +#if IS_ENABLED(CONFIG_HYPERV)
> + spin_lock_init(&kvm->arch.hv_root_tdp_lock);
> + kvm->arch.hv_root_tdp = INVALID_PAGE;
> +#endif
> +
> INIT_DELAYED_WORK(&kvm->arch.kvmclock_update_work, kvmclock_update_fn);
> INIT_DELAYED_WORK(&kvm->arch.kvmclock_sync_work, kvmclock_sync_fn);
--
Vitaly
On 10/06/21 13:20, Vitaly Kuznetsov wrote:
>> +static inline void hv_track_root_tdp(struct kvm_vcpu *vcpu, hpa_t root_tdp)
>> +{
>> + struct kvm_arch *kvm_arch = &vcpu->kvm->arch;
>> +
>> + if (kvm_x86_ops.tlb_remote_flush == hv_remote_flush_tlb) {
>> + spin_lock(&kvm_arch->hv_root_tdp_lock);
>> + vcpu->arch.hv_root_tdp = root_tdp;
>> + if (root_tdp != kvm_arch->hv_root_tdp)
>> + kvm_arch->hv_root_tdp = INVALID_PAGE;
>> + spin_unlock(&kvm_arch->hv_root_tdp_lock);
>> + }
>> +}
>> +#else
>> +static inline void hv_track_root_tdp(struct kvm_vcpu *vcpu, hpa_t root_tdp)
>> +{
>> +}
>> +#endif
>> +#endif
>
> Super-nitpick: I'd suggest adding /* __ARCH_X86_KVM_KVM_ONHYPERV_H__ */
> to the second '#endif' and /* IS_ENABLED(CONFIG_HYPERV) */ to '#else'
> and the first one: files/functions tend to grow and it becomes hard to
> see where the particular '#endif/#else' belongs.
Done, thanks. I've also changed the #if to just "#ifdef CONFIG_HYPERV",
since IS_ENABLED is only needed in C statements.
Paolo
>> +
>> diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
>> index d000cddbd734..117fb88cd354 100644
>> --- a/arch/x86/kvm/vmx/vmx.c
>> +++ b/arch/x86/kvm/vmx/vmx.c
>> @@ -52,6 +52,7 @@
>> #include "cpuid.h"
>> #include "evmcs.h"
>> #include "hyperv.h"
>> +#include "kvm_onhyperv.h"
>> #include "irq.h"
>> #include "kvm_cache_regs.h"
>> #include "lapic.h"
>> @@ -474,86 +475,6 @@ static const u32 vmx_uret_msrs_list[] = {
>> static bool __read_mostly enlightened_vmcs = true;
>> module_param(enlightened_vmcs, bool, 0444);
>>
>> -static int kvm_fill_hv_flush_list_func(struct hv_guest_mapping_flush_list *flush,
>> - void *data)
>> -{
>> - struct kvm_tlb_range *range = data;
>> -
>> - return hyperv_fill_flush_guest_mapping_list(flush, range->start_gfn,
>> - range->pages);
>> -}
>> -
>> -static inline int hv_remote_flush_root_ept(hpa_t root_ept,
>> - struct kvm_tlb_range *range)
>> -{
>> - if (range)
>> - return hyperv_flush_guest_mapping_range(root_ept,
>> - kvm_fill_hv_flush_list_func, (void *)range);
>> - else
>> - return hyperv_flush_guest_mapping(root_ept);
>> -}
>> -
>> -static int hv_remote_flush_tlb_with_range(struct kvm *kvm,
>> - struct kvm_tlb_range *range)
>> -{
>> - struct kvm_vmx *kvm_vmx = to_kvm_vmx(kvm);
>> - struct kvm_vcpu *vcpu;
>> - int ret = 0, i, nr_unique_valid_roots;
>> - hpa_t root;
>> -
>> - spin_lock(&kvm_vmx->hv_root_ept_lock);
>> -
>> - if (!VALID_PAGE(kvm_vmx->hv_root_ept)) {
>> - nr_unique_valid_roots = 0;
>> -
>> - /*
>> - * Flush all valid roots, and see if all vCPUs have converged
>> - * on a common root, in which case future flushes can skip the
>> - * loop and flush the common root.
>> - */
>> - kvm_for_each_vcpu(i, vcpu, kvm) {
>> - root = to_vmx(vcpu)->hv_root_ept;
>> - if (!VALID_PAGE(root) || root == kvm_vmx->hv_root_ept)
>> - continue;
>> -
>> - /*
>> - * Set the tracked root to the first valid root. Keep
>> - * this root for the entirety of the loop even if more
>> - * roots are encountered as a low effort optimization
>> - * to avoid flushing the same (first) root again.
>> - */
>> - if (++nr_unique_valid_roots == 1)
>> - kvm_vmx->hv_root_ept = root;
>> -
>> - if (!ret)
>> - ret = hv_remote_flush_root_ept(root, range);
>> -
>> - /*
>> - * Stop processing roots if a failure occurred and
>> - * multiple valid roots have already been detected.
>> - */
>> - if (ret && nr_unique_valid_roots > 1)
>> - break;
>> - }
>> -
>> - /*
>> - * The optimized flush of a single root can't be used if there
>> - * are multiple valid roots (obviously).
>> - */
>> - if (nr_unique_valid_roots > 1)
>> - kvm_vmx->hv_root_ept = INVALID_PAGE;
>> - } else {
>> - ret = hv_remote_flush_root_ept(kvm_vmx->hv_root_ept, range);
>> - }
>> -
>> - spin_unlock(&kvm_vmx->hv_root_ept_lock);
>> - return ret;
>> -}
>> -static int hv_remote_flush_tlb(struct kvm *kvm)
>> -{
>> - return hv_remote_flush_tlb_with_range(kvm, NULL);
>> -}
>> -
>> static int hv_enable_direct_tlbflush(struct kvm_vcpu *vcpu)
>> {
>> struct hv_enlightened_vmcs *evmcs;
>> @@ -581,21 +502,6 @@ static int hv_enable_direct_tlbflush(struct kvm_vcpu *vcpu)
>>
>> #endif /* IS_ENABLED(CONFIG_HYPERV) */
>>
>> -static void hv_track_root_ept(struct kvm_vcpu *vcpu, hpa_t root_ept)
>> -{
>> -#if IS_ENABLED(CONFIG_HYPERV)
>> - struct kvm_vmx *kvm_vmx = to_kvm_vmx(vcpu->kvm);
>> -
>> - if (kvm_x86_ops.tlb_remote_flush == hv_remote_flush_tlb) {
>> - spin_lock(&kvm_vmx->hv_root_ept_lock);
>> - to_vmx(vcpu)->hv_root_ept = root_ept;
>> - if (root_ept != kvm_vmx->hv_root_ept)
>> - kvm_vmx->hv_root_ept = INVALID_PAGE;
>> - spin_unlock(&kvm_vmx->hv_root_ept_lock);
>> - }
>> -#endif
>> -}
>> -
>> /*
>> * Comment's format: document - errata name - stepping - processor name.
>> * Refer from
>> @@ -3202,7 +3108,7 @@ static void vmx_load_mmu_pgd(struct kvm_vcpu *vcpu, hpa_t root_hpa,
>> eptp = construct_eptp(vcpu, root_hpa, root_level);
>> vmcs_write64(EPT_POINTER, eptp);
>>
>> - hv_track_root_ept(vcpu, root_hpa);
>> + hv_track_root_tdp(vcpu, root_hpa);
>>
>> if (!enable_unrestricted_guest && !is_paging(vcpu))
>> guest_cr3 = to_kvm_vmx(kvm)->ept_identity_map_addr;
>> @@ -6980,9 +6886,6 @@ static int vmx_create_vcpu(struct kvm_vcpu *vcpu)
>> vmx->pi_desc.nv = POSTED_INTR_VECTOR;
>> vmx->pi_desc.sn = 1;
>>
>> -#if IS_ENABLED(CONFIG_HYPERV)
>> - vmx->hv_root_ept = INVALID_PAGE;
>> -#endif
>> return 0;
>>
>> free_vmcs:
>> @@ -6999,10 +6902,6 @@ static int vmx_create_vcpu(struct kvm_vcpu *vcpu)
>>
>> static int vmx_vm_init(struct kvm *kvm)
>> {
>> -#if IS_ENABLED(CONFIG_HYPERV)
>> - spin_lock_init(&to_kvm_vmx(kvm)->hv_root_ept_lock);
>> -#endif
>> -
>> if (!ple_gap)
>> kvm->arch.pause_in_guest = true;
>>
>> diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h
>> index 008cb87ff088..d1363e734a01 100644
>> --- a/arch/x86/kvm/vmx/vmx.h
>> +++ b/arch/x86/kvm/vmx/vmx.h
>> @@ -328,10 +328,6 @@ struct vcpu_vmx {
>> /* SGX Launch Control public key hash */
>> u64 msr_ia32_sgxlepubkeyhash[4];
>>
>> -#if IS_ENABLED(CONFIG_HYPERV)
>> - u64 hv_root_ept;
>> -#endif
>> -
>> struct pt_desc pt_desc;
>> struct lbr_desc lbr_desc;
>>
>> @@ -349,11 +345,6 @@ struct kvm_vmx {
>> unsigned int tss_addr;
>> bool ept_identity_pagetable_done;
>> gpa_t ept_identity_map_addr;
>> -
>> -#if IS_ENABLED(CONFIG_HYPERV)
>> - hpa_t hv_root_ept;
>> - spinlock_t hv_root_ept_lock;
>> -#endif
>> };
>>
>> bool nested_vmx_allowed(struct kvm_vcpu *vcpu);
>> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
>> index 6eda2834fc05..580f3c6c86f9 100644
>> --- a/arch/x86/kvm/x86.c
>> +++ b/arch/x86/kvm/x86.c
>> @@ -10279,6 +10279,10 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
>> vcpu->arch.pending_external_vector = -1;
>> vcpu->arch.preempted_in_kernel = false;
>>
>> +#if IS_ENABLED(CONFIG_HYPERV)
>> + vcpu->arch.hv_root_tdp = INVALID_PAGE;
>> +#endif
>> +
>> r = static_call(kvm_x86_vcpu_create)(vcpu);
>> if (r)
>> goto free_guest_fpu;
>> @@ -10662,6 +10666,11 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
>>
>> kvm->arch.guest_can_read_msr_platform_info = true;
>>
>> +#if IS_ENABLED(CONFIG_HYPERV)
>> + spin_lock_init(&kvm->arch.hv_root_tdp_lock);
>> + kvm->arch.hv_root_tdp = INVALID_PAGE;
>> +#endif
>> +
>> INIT_DELAYED_WORK(&kvm->arch.kvmclock_update_work, kvmclock_update_fn);
>> INIT_DELAYED_WORK(&kvm->arch.kvmclock_sync_work, kvmclock_sync_fn);
>
On 03/06/21 17:14, Vineeth Pillai wrote:
> This patch series enables the nested virtualization enlightenments for
> SVM. This is very similar to the enlightenments for VMX except for the
> fact that there is no enlightened VMCS. For SVM, VMCB is already an
> architectural in-memory data structure.
>
> Note: v5 is just a rebase on hyperv-next(5.13-rc1) and needed a rework
> based on the patch series: (KVM: VMX: Clean up Hyper-V PV TLB flush)
> https://lore.kernel.org/lkml/[email protected]/
>
> The supported enlightenments are:
>
> Enlightened TLB Flush: If this is enabled, ASID invalidations invalidate
> only gva -> hpa entries. To flush entries derived from NPT, hyper-v
> provided hypercalls (HvFlushGuestPhysicalAddressSpace or
> HvFlushGuestPhysicalAddressList) should be used.
>
> Enlightened MSR bitmap(TLFS 16.5.3): "When enabled, L0 hypervisor does
> not monitor the MSR bitmaps for changes. Instead, the L1 hypervisor must
> invalidate the corresponding clean field after making changes to one of
> the MSR bitmaps."
>
> Direct Virtual Flush(TLFS 16.8): The hypervisor exposes hypercalls
> (HvFlushVirtualAddressSpace, HvFlushVirtualAddressSpaceEx,
> HvFlushVirtualAddressList, and HvFlushVirtualAddressListEx) that allow
> operating systems to more efficiently manage the virtual TLB. The L1
> hypervisor can choose to allow its guest to use those hypercalls and
> delegate the responsibility to handle them to the L0 hypervisor. This
> requires the use of a partition assist page."
>
> L2 Windows boot time was measured with and without the patch. Time was
> measured from power on to the login screen and was averaged over a
> consecutive 5 trials:
> Without the patch: 42 seconds
> With the patch: 29 seconds
> --
>
> Changes from v4
> - Rebased on top of 5.13-rc1 and reworked based on the changes in the
> patch series: (KVM: VMX: Clean up Hyper-V PV TLB flush)
>
> Changes from v3
> - Included definitions for software/hypervisor reserved fields in SVM
> architectural data structures.
> - Consolidated Hyper-V specific code into svm_onhyperv.[ch] to reduce
> the "ifdefs". This change applies only to SVM, VMX is not touched and
> is not in the scope of this patch series.
>
> Changes from v2:
> - Refactored the Remote TLB Flush logic into separate hyperv specific
> source files (kvm_onhyperv.[ch]).
> - Reverted the VMCB Clean bits macro changes as it is no longer needed.
>
> Changes from v1:
> - Move the remote TLB flush related fields from kvm_vcpu_hv and kvm_hv
> to kvm_vcpu_arch and kvm_arch.
> - Modify the VMCB clean mask runtime based on whether L1 hypervisor
> is running on Hyper-V or not.
> - Detect Hyper-V nested enlightenments based on
> HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS.
> - Address other minor review comments.
> ---
>
> Vineeth Pillai (7):
> hyperv: Detect Nested virtualization support for SVM
> hyperv: SVM enlightened TLB flush support flag
> KVM: x86: hyper-v: Move the remote TLB flush logic out of vmx
> KVM: SVM: Software reserved fields
> KVM: SVM: hyper-v: Remote TLB flush for SVM
> KVM: SVM: hyper-v: Enlightened MSR-Bitmap support
> KVM: SVM: hyper-v: Direct Virtual Flush support
>
> arch/x86/include/asm/hyperv-tlfs.h | 9 ++
> arch/x86/include/asm/kvm_host.h | 9 ++
> arch/x86/include/asm/svm.h | 9 +-
> arch/x86/include/uapi/asm/svm.h | 3 +
> arch/x86/kernel/cpu/mshyperv.c | 10 ++-
> arch/x86/kvm/Makefile | 9 ++
> arch/x86/kvm/kvm_onhyperv.c | 93 +++++++++++++++++++++
> arch/x86/kvm/kvm_onhyperv.h | 32 +++++++
> arch/x86/kvm/svm/svm.c | 14 ++++
> arch/x86/kvm/svm/svm.h | 22 ++++-
> arch/x86/kvm/svm/svm_onhyperv.c | 41 +++++++++
> arch/x86/kvm/svm/svm_onhyperv.h | 129 +++++++++++++++++++++++++++++
> arch/x86/kvm/vmx/vmx.c | 105 +----------------------
> arch/x86/kvm/vmx/vmx.h | 9 --
> arch/x86/kvm/x86.c | 9 ++
> 15 files changed, 384 insertions(+), 119 deletions(-)
> create mode 100644 arch/x86/kvm/kvm_onhyperv.c
> create mode 100644 arch/x86/kvm/kvm_onhyperv.h
> create mode 100644 arch/x86/kvm/svm/svm_onhyperv.c
> create mode 100644 arch/x86/kvm/svm/svm_onhyperv.h
>
Queued, thanks.
Paolo
On 10/06/21 13:16, Vitaly Kuznetsov wrote:
>> +int hv_enable_direct_tlbflush(struct kvm_vcpu *vcpu)
>> +{
> I would've avoided re-using 'hv_enable_direct_tlbflush()' name which we
> already have in vmx. In fact, in the spirit of this patch, I'd suggest
> we create arch/x86/kvm/vmx/vmx_onhyperv.c and move the existing
> hv_enable_direct_tlbflush() there. We can then re-name it to e.g.
>
> vmx_enable_hv_direct_tlbflush()
>
> so the one introduced by this patch will be
>
> svm_enable_hv_direct_tlbflush()
>
I did the rename, and agree with creating a similar file that is split
off vmx.c.
Paolo
Paolo Bonzini <[email protected]> writes:
> On 10/06/21 13:20, Vitaly Kuznetsov wrote:
>
>>> +static inline void hv_track_root_tdp(struct kvm_vcpu *vcpu, hpa_t root_tdp)
>>> +{
>>> + struct kvm_arch *kvm_arch = &vcpu->kvm->arch;
>>> +
>>> + if (kvm_x86_ops.tlb_remote_flush == hv_remote_flush_tlb) {
>>> + spin_lock(&kvm_arch->hv_root_tdp_lock);
>>> + vcpu->arch.hv_root_tdp = root_tdp;
>>> + if (root_tdp != kvm_arch->hv_root_tdp)
>>> + kvm_arch->hv_root_tdp = INVALID_PAGE;
>>> + spin_unlock(&kvm_arch->hv_root_tdp_lock);
>>> + }
>>> +}
>>> +#else
>>> +static inline void hv_track_root_tdp(struct kvm_vcpu *vcpu, hpa_t root_tdp)
>>> +{
>>> +}
>>> +#endif
>>> +#endif
>>
>> Super-nitpick: I'd suggest adding /* __ARCH_X86_KVM_KVM_ONHYPERV_H__ */
>> to the second '#endif' and /* IS_ENABLED(CONFIG_HYPERV) */ to '#else'
>> and the first one: files/functions tend to grow and it becomes hard to
>> see where the particular '#endif/#else' belongs.
>
> Done, thanks. I've also changed the #if to just "#ifdef CONFIG_HYPERV",
> since IS_ENABLED is only needed in C statements.
kvm/queue fails to compile and I blame this change:
In file included from arch/x86/kvm/svm/svm_onhyperv.c:16:
arch/x86/kvm/svm/svm_onhyperv.h: In function ‘svm_hv_hardware_setup’:
arch/x86/kvm/svm/svm_onhyperv.h:56:34: error: ‘hv_remote_flush_tlb’ undeclared (first use in this function); did you mean ‘svm_flush_tlb’?
56 | svm_x86_ops.tlb_remote_flush = hv_remote_flush_tlb;
| ^~~~~~~~~~~~~~~~~~~
| svm_flush_tlb
arch/x86/kvm/svm/svm_onhyperv.h:56:34: note: each undeclared identifier is reported only once for each function it appears in
arch/x86/kvm/svm/svm_onhyperv.h:58:5: error: ‘hv_remote_flush_tlb_with_range’ undeclared (first use in this function)
58 | hv_remote_flush_tlb_with_range;
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
make[2]: *** [scripts/Makefile.build:272: arch/x86/kvm/svm/svm_onhyperv.o] Error 1
make[2]: *** Waiting for unfinished jobs....
In file included from arch/x86/kvm/svm/svm.c:47:
arch/x86/kvm/svm/svm_onhyperv.h: In function ‘svm_hv_hardware_setup’:
arch/x86/kvm/svm/svm_onhyperv.h:56:34: error: ‘hv_remote_flush_tlb’ undeclared (first use in this function); did you mean ‘svm_flush_tlb’?
56 | svm_x86_ops.tlb_remote_flush = hv_remote_flush_tlb;
| ^~~~~~~~~~~~~~~~~~~
| svm_flush_tlb
arch/x86/kvm/svm/svm_onhyperv.h:56:34: note: each undeclared identifier is reported only once for each function it appears in
arch/x86/kvm/svm/svm_onhyperv.h:58:5: error: ‘hv_remote_flush_tlb_with_range’ undeclared (first use in this function)
58 | hv_remote_flush_tlb_with_range;
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
make[2]: *** [scripts/Makefile.build:272: arch/x86/kvm/svm/svm.o] Error 1
arch/x86/kvm/vmx/vmx.c: In function ‘hardware_setup’:
arch/x86/kvm/vmx/vmx.c:7752:34: error: ‘hv_remote_flush_tlb’ undeclared (first use in this function)
7752 | vmx_x86_ops.tlb_remote_flush = hv_remote_flush_tlb;
| ^~~~~~~~~~~~~~~~~~~
arch/x86/kvm/vmx/vmx.c:7752:34: note: each undeclared identifier is reported only once for each function it appears in
arch/x86/kvm/vmx/vmx.c:7754:5: error: ‘hv_remote_flush_tlb_with_range’ undeclared (first use in this function)
7754 | hv_remote_flush_tlb_with_range;
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(Note: CONFIG_HYPERV can be 'm'.)
The following:
index 96da53edfe83..1c67abf2eba9 100644
--- a/arch/x86/kvm/kvm_onhyperv.h
+++ b/arch/x86/kvm/kvm_onhyperv.h
@@ -6,7 +6,7 @@
#ifndef __ARCH_X86_KVM_KVM_ONHYPERV_H__
#define __ARCH_X86_KVM_KVM_ONHYPERV_H__
-#ifdef CONFIG_HYPERV
+#if IS_ENABLED(CONFIG_HYPERV)
int hv_remote_flush_tlb_with_range(struct kvm *kvm,
struct kvm_tlb_range *range);
int hv_remote_flush_tlb(struct kvm *kvm);
saves the day for me.
--
Vitaly
On Thu, 2021-06-10 at 17:17 +0200, Paolo Bonzini wrote:
> On 03/06/21 17:14, Vineeth Pillai wrote:
> > This patch series enables the nested virtualization enlightenments for
> > SVM. This is very similar to the enlightenments for VMX except for the
> > fact that there is no enlightened VMCS. For SVM, VMCB is already an
> > architectural in-memory data structure.
> >
> > Note: v5 is just a rebase on hyperv-next(5.13-rc1) and needed a rework
> > based on the patch series: (KVM: VMX: Clean up Hyper-V PV TLB flush)
> > https://lore.kernel.org/lkml/[email protected]/
> >
> > The supported enlightenments are:
> >
> > Enlightened TLB Flush: If this is enabled, ASID invalidations invalidate
> > only gva -> hpa entries. To flush entries derived from NPT, hyper-v
> > provided hypercalls (HvFlushGuestPhysicalAddressSpace or
> > HvFlushGuestPhysicalAddressList) should be used.
> >
> > Enlightened MSR bitmap(TLFS 16.5.3): "When enabled, L0 hypervisor does
> > not monitor the MSR bitmaps for changes. Instead, the L1 hypervisor must
> > invalidate the corresponding clean field after making changes to one of
> > the MSR bitmaps."
> >
> > Direct Virtual Flush(TLFS 16.8): The hypervisor exposes hypercalls
> > (HvFlushVirtualAddressSpace, HvFlushVirtualAddressSpaceEx,
> > HvFlushVirtualAddressList, and HvFlushVirtualAddressListEx) that allow
> > operating systems to more efficiently manage the virtual TLB. The L1
> > hypervisor can choose to allow its guest to use those hypercalls and
> > delegate the responsibility to handle them to the L0 hypervisor. This
> > requires the use of a partition assist page."
> >
> > L2 Windows boot time was measured with and without the patch. Time was
> > measured from power on to the login screen and was averaged over a
> > consecutive 5 trials:
> > Without the patch: 42 seconds
> > With the patch: 29 seconds
> > --
> >
> > Changes from v4
> > - Rebased on top of 5.13-rc1 and reworked based on the changes in the
> > patch series: (KVM: VMX: Clean up Hyper-V PV TLB flush)
> >
> > Changes from v3
> > - Included definitions for software/hypervisor reserved fields in SVM
> > architectural data structures.
> > - Consolidated Hyper-V specific code into svm_onhyperv.[ch] to reduce
> > the "ifdefs". This change applies only to SVM, VMX is not touched and
> > is not in the scope of this patch series.
> >
> > Changes from v2:
> > - Refactored the Remote TLB Flush logic into separate hyperv specific
> > source files (kvm_onhyperv.[ch]).
> > - Reverted the VMCB Clean bits macro changes as it is no longer needed.
> >
> > Changes from v1:
> > - Move the remote TLB flush related fields from kvm_vcpu_hv and kvm_hv
> > to kvm_vcpu_arch and kvm_arch.
> > - Modify the VMCB clean mask runtime based on whether L1 hypervisor
> > is running on Hyper-V or not.
> > - Detect Hyper-V nested enlightenments based on
> > HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS.
> > - Address other minor review comments.
> > ---
> >
> > Vineeth Pillai (7):
> > hyperv: Detect Nested virtualization support for SVM
> > hyperv: SVM enlightened TLB flush support flag
> > KVM: x86: hyper-v: Move the remote TLB flush logic out of vmx
> > KVM: SVM: Software reserved fields
> > KVM: SVM: hyper-v: Remote TLB flush for SVM
> > KVM: SVM: hyper-v: Enlightened MSR-Bitmap support
> > KVM: SVM: hyper-v: Direct Virtual Flush support
> >
> > arch/x86/include/asm/hyperv-tlfs.h | 9 ++
> > arch/x86/include/asm/kvm_host.h | 9 ++
> > arch/x86/include/asm/svm.h | 9 +-
> > arch/x86/include/uapi/asm/svm.h | 3 +
> > arch/x86/kernel/cpu/mshyperv.c | 10 ++-
> > arch/x86/kvm/Makefile | 9 ++
> > arch/x86/kvm/kvm_onhyperv.c | 93 +++++++++++++++++++++
> > arch/x86/kvm/kvm_onhyperv.h | 32 +++++++
> > arch/x86/kvm/svm/svm.c | 14 ++++
> > arch/x86/kvm/svm/svm.h | 22 ++++-
> > arch/x86/kvm/svm/svm_onhyperv.c | 41 +++++++++
> > arch/x86/kvm/svm/svm_onhyperv.h | 129 +++++++++++++++++++++++++++++
> > arch/x86/kvm/vmx/vmx.c | 105 +----------------------
> > arch/x86/kvm/vmx/vmx.h | 9 --
> > arch/x86/kvm/x86.c | 9 ++
> > 15 files changed, 384 insertions(+), 119 deletions(-)
> > create mode 100644 arch/x86/kvm/kvm_onhyperv.c
> > create mode 100644 arch/x86/kvm/kvm_onhyperv.h
> > create mode 100644 arch/x86/kvm/svm/svm_onhyperv.c
> > create mode 100644 arch/x86/kvm/svm/svm_onhyperv.h
> >
>
> Queued, thanks.
>
> Paolo
>
Hi!
This patch series causes a build failure here:
arch/x86/kvm/vmx/vmx.c: In function ‘hardware_setup’:
arch/x86/kvm/vmx/vmx.c:7752:34: error: ‘hv_remote_flush_tlb’ undeclared (first use in this function)
7752 | vmx_x86_ops.tlb_remote_flush = hv_remote_flush_tlb;
| ^~~~~~~~~~~~~~~~~~~
arch/x86/kvm/vmx/vmx.c:7752:34: note: each undeclared identifier is reported only once for each function it appears in
arch/x86/kvm/vmx/vmx.c:7754:5: error: ‘hv_remote_flush_tlb_with_range’ undeclared (first use in this function)
7754 | hv_remote_flush_tlb_with_range;
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Also this:
arch/x86/kvm/vmx/vmx.c: In function ‘hardware_setup’:
arch/x86/kvm/vmx/vmx.c:7752:34: error: ‘hv_remote_flush_tlb’ undeclared (first use in this function)
7752 | vmx_x86_ops.tlb_remote_flush = hv_remote_flush_tlb;
| ^~~~~~~~~~~~~~~~~~~
arch/x86/kvm/vmx/vmx.c:7752:34: note: each undeclared identifier is reported only once for each function it appears in
arch/x86/kvm/vmx/vmx.c:7754:5: error: ‘hv_remote_flush_tlb_with_range’ undeclared (first use in this function)
7754 | hv_remote_flush_tlb_with_range;
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Best regards,
Maxim Levitsky
Maxim Levitsky <[email protected]> writes:
> On Thu, 2021-06-10 at 17:17 +0200, Paolo Bonzini wrote:
>> On 03/06/21 17:14, Vineeth Pillai wrote:
>> > This patch series enables the nested virtualization enlightenments for
>> > SVM. This is very similar to the enlightenments for VMX except for the
>> > fact that there is no enlightened VMCS. For SVM, VMCB is already an
>> > architectural in-memory data structure.
>> >
>> > Note: v5 is just a rebase on hyperv-next(5.13-rc1) and needed a rework
>> > based on the patch series: (KVM: VMX: Clean up Hyper-V PV TLB flush)
>> > https://lore.kernel.org/lkml/[email protected]/
>> >
>> > The supported enlightenments are:
>> >
>> > Enlightened TLB Flush: If this is enabled, ASID invalidations invalidate
>> > only gva -> hpa entries. To flush entries derived from NPT, hyper-v
>> > provided hypercalls (HvFlushGuestPhysicalAddressSpace or
>> > HvFlushGuestPhysicalAddressList) should be used.
>> >
>> > Enlightened MSR bitmap(TLFS 16.5.3): "When enabled, L0 hypervisor does
>> > not monitor the MSR bitmaps for changes. Instead, the L1 hypervisor must
>> > invalidate the corresponding clean field after making changes to one of
>> > the MSR bitmaps."
>> >
>> > Direct Virtual Flush(TLFS 16.8): The hypervisor exposes hypercalls
>> > (HvFlushVirtualAddressSpace, HvFlushVirtualAddressSpaceEx,
>> > HvFlushVirtualAddressList, and HvFlushVirtualAddressListEx) that allow
>> > operating systems to more efficiently manage the virtual TLB. The L1
>> > hypervisor can choose to allow its guest to use those hypercalls and
>> > delegate the responsibility to handle them to the L0 hypervisor. This
>> > requires the use of a partition assist page."
>> >
>> > L2 Windows boot time was measured with and without the patch. Time was
>> > measured from power on to the login screen and was averaged over a
>> > consecutive 5 trials:
>> > Without the patch: 42 seconds
>> > With the patch: 29 seconds
>> > --
>> >
>> > Changes from v4
>> > - Rebased on top of 5.13-rc1 and reworked based on the changes in the
>> > patch series: (KVM: VMX: Clean up Hyper-V PV TLB flush)
>> >
>> > Changes from v3
>> > - Included definitions for software/hypervisor reserved fields in SVM
>> > architectural data structures.
>> > - Consolidated Hyper-V specific code into svm_onhyperv.[ch] to reduce
>> > the "ifdefs". This change applies only to SVM, VMX is not touched and
>> > is not in the scope of this patch series.
>> >
>> > Changes from v2:
>> > - Refactored the Remote TLB Flush logic into separate hyperv specific
>> > source files (kvm_onhyperv.[ch]).
>> > - Reverted the VMCB Clean bits macro changes as it is no longer needed.
>> >
>> > Changes from v1:
>> > - Move the remote TLB flush related fields from kvm_vcpu_hv and kvm_hv
>> > to kvm_vcpu_arch and kvm_arch.
>> > - Modify the VMCB clean mask runtime based on whether L1 hypervisor
>> > is running on Hyper-V or not.
>> > - Detect Hyper-V nested enlightenments based on
>> > HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS.
>> > - Address other minor review comments.
>> > ---
>> >
>> > Vineeth Pillai (7):
>> > hyperv: Detect Nested virtualization support for SVM
>> > hyperv: SVM enlightened TLB flush support flag
>> > KVM: x86: hyper-v: Move the remote TLB flush logic out of vmx
>> > KVM: SVM: Software reserved fields
>> > KVM: SVM: hyper-v: Remote TLB flush for SVM
>> > KVM: SVM: hyper-v: Enlightened MSR-Bitmap support
>> > KVM: SVM: hyper-v: Direct Virtual Flush support
>> >
>> > arch/x86/include/asm/hyperv-tlfs.h | 9 ++
>> > arch/x86/include/asm/kvm_host.h | 9 ++
>> > arch/x86/include/asm/svm.h | 9 +-
>> > arch/x86/include/uapi/asm/svm.h | 3 +
>> > arch/x86/kernel/cpu/mshyperv.c | 10 ++-
>> > arch/x86/kvm/Makefile | 9 ++
>> > arch/x86/kvm/kvm_onhyperv.c | 93 +++++++++++++++++++++
>> > arch/x86/kvm/kvm_onhyperv.h | 32 +++++++
>> > arch/x86/kvm/svm/svm.c | 14 ++++
>> > arch/x86/kvm/svm/svm.h | 22 ++++-
>> > arch/x86/kvm/svm/svm_onhyperv.c | 41 +++++++++
>> > arch/x86/kvm/svm/svm_onhyperv.h | 129 +++++++++++++++++++++++++++++
>> > arch/x86/kvm/vmx/vmx.c | 105 +----------------------
>> > arch/x86/kvm/vmx/vmx.h | 9 --
>> > arch/x86/kvm/x86.c | 9 ++
>> > 15 files changed, 384 insertions(+), 119 deletions(-)
>> > create mode 100644 arch/x86/kvm/kvm_onhyperv.c
>> > create mode 100644 arch/x86/kvm/kvm_onhyperv.h
>> > create mode 100644 arch/x86/kvm/svm/svm_onhyperv.c
>> > create mode 100644 arch/x86/kvm/svm/svm_onhyperv.h
>> >
>>
>> Queued, thanks.
>>
>> Paolo
>>
>
> Hi!
>
> This patch series causes a build failure here:
>
> arch/x86/kvm/vmx/vmx.c: In function ‘hardware_setup’:
> arch/x86/kvm/vmx/vmx.c:7752:34: error: ‘hv_remote_flush_tlb’ undeclared (first use in this function)
> 7752 | vmx_x86_ops.tlb_remote_flush = hv_remote_flush_tlb;
> | ^~~~~~~~~~~~~~~~~~~
> arch/x86/kvm/vmx/vmx.c:7752:34: note: each undeclared identifier is reported only once for each function it appears in
> arch/x86/kvm/vmx/vmx.c:7754:5: error: ‘hv_remote_flush_tlb_with_range’ undeclared (first use in this function)
> 7754 | hv_remote_flush_tlb_with_range;
> | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>
>
> Also this:
>
> arch/x86/kvm/vmx/vmx.c: In function ‘hardware_setup’:
> arch/x86/kvm/vmx/vmx.c:7752:34: error: ‘hv_remote_flush_tlb’ undeclared (first use in this function)
> 7752 | vmx_x86_ops.tlb_remote_flush = hv_remote_flush_tlb;
> | ^~~~~~~~~~~~~~~~~~~
> arch/x86/kvm/vmx/vmx.c:7752:34: note: each undeclared identifier is reported only once for each function it appears in
> arch/x86/kvm/vmx/vmx.c:7754:5: error: ‘hv_remote_flush_tlb_with_range’ undeclared (first use in this function)
> 7754 | hv_remote_flush_tlb_with_range;
> | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>
Yea, reported already:
https://lore.kernel.org/kvm/[email protected]/T/#mf732ba9567923d800329f896761e4ee104475894
>
> Best regards,
> Maxim Levitsky
>
--
Vitaly
On 11/06/21 11:44, Vitaly Kuznetsov wrote:
> Yea, reported already:
>
> https://lore.kernel.org/kvm/[email protected]/T/#mf732ba9567923d800329f896761e4ee104475894
And that's how I learnt that 1) IS_ENABLED != #ifdef for modules 2)
CONFIG_HYPERV=m is possible. Sorry Vineeth for clobbering your
perfectly fine patch!
Paolo
Paolo Bonzini <[email protected]> writes:
> CONFIG_HYPERV=m is possible.
We've stubmled upon this multiple times already. Initially, the whole
Hyper-V support code was a module (what's now in drivers/hv/) but then
some core functionallity was moved out to arch/x86/ but we didn't add a
new config back then. Still suffering :-)
Ideally, we would want to have
CONFIG_HYPERV_GUEST=y/n for what's in arch/x86/ (just like CONFIG_KVM_GUEST)
CONFIG_HYPERV_VMBUS=y/n/m for what's in drivers/hv
--
Vitaly
Vineeth Pillai <[email protected]> writes:
> From Hyper-V TLFS:
> "The hypervisor exposes hypercalls (HvFlushVirtualAddressSpace,
> HvFlushVirtualAddressSpaceEx, HvFlushVirtualAddressList, and
> HvFlushVirtualAddressListEx) that allow operating systems to more
> efficiently manage the virtual TLB. The L1 hypervisor can choose to
> allow its guest to use those hypercalls and delegate the responsibility
> to handle them to the L0 hypervisor. This requires the use of a
> partition assist page."
>
> Add the Direct Virtual Flush support for SVM.
>
> Related VMX changes:
> commit 6f6a657c9998 ("KVM/Hyper-V/VMX: Add direct tlb flush support")
>
> Signed-off-by: Vineeth Pillai <[email protected]>
> ---
> arch/x86/kvm/Makefile | 4 ++++
> arch/x86/kvm/svm/svm.c | 2 ++
> arch/x86/kvm/svm/svm_onhyperv.c | 41 +++++++++++++++++++++++++++++++++
> arch/x86/kvm/svm/svm_onhyperv.h | 36 +++++++++++++++++++++++++++++
> 4 files changed, 83 insertions(+)
> create mode 100644 arch/x86/kvm/svm/svm_onhyperv.c
>
> diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile
> index a06745c2fef1..83331376b779 100644
> --- a/arch/x86/kvm/Makefile
> +++ b/arch/x86/kvm/Makefile
> @@ -32,6 +32,10 @@ kvm-intel-$(CONFIG_X86_SGX_KVM) += vmx/sgx.o
>
> kvm-amd-y += svm/svm.o svm/vmenter.o svm/pmu.o svm/nested.o svm/avic.o svm/sev.o
>
> +ifdef CONFIG_HYPERV
> +kvm-amd-y += svm/svm_onhyperv.o
> +endif
> +
> obj-$(CONFIG_KVM) += kvm.o
> obj-$(CONFIG_KVM_INTEL) += kvm-intel.o
> obj-$(CONFIG_KVM_AMD) += kvm-amd.o
> diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
> index d2a625411059..5139cb6baadc 100644
> --- a/arch/x86/kvm/svm/svm.c
> +++ b/arch/x86/kvm/svm/svm.c
> @@ -3779,6 +3779,8 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu)
> }
> svm->vmcb->save.cr2 = vcpu->arch.cr2;
>
> + svm_hv_update_vp_id(svm->vmcb, vcpu);
> +
> /*
> * Run with all-zero DR6 unless needed, so that we can get the exact cause
> * of a #DB.
> diff --git a/arch/x86/kvm/svm/svm_onhyperv.c b/arch/x86/kvm/svm/svm_onhyperv.c
> new file mode 100644
> index 000000000000..3281856ebd94
> --- /dev/null
> +++ b/arch/x86/kvm/svm/svm_onhyperv.c
> @@ -0,0 +1,41 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * KVM L1 hypervisor optimizations on Hyper-V for SVM.
> + */
> +
> +#include <linux/kvm_host.h>
> +#include "kvm_cache_regs.h"
> +
> +#include <asm/mshyperv.h>
> +
> +#include "svm.h"
> +#include "svm_ops.h"
> +
> +#include "hyperv.h"
> +#include "kvm_onhyperv.h"
> +#include "svm_onhyperv.h"
> +
> +int hv_enable_direct_tlbflush(struct kvm_vcpu *vcpu)
> +{
> + struct hv_enlightenments *hve;
> + struct hv_partition_assist_pg **p_hv_pa_pg =
> + &to_kvm_hv(vcpu->kvm)->hv_pa_pg;
> +
> + if (!*p_hv_pa_pg)
> + *p_hv_pa_pg = kzalloc(PAGE_SIZE, GFP_KERNEL);
> +
> + if (!*p_hv_pa_pg)
> + return -ENOMEM;
> +
> + hve = (struct hv_enlightenments *)to_svm(vcpu)->vmcb->control.reserved_sw;
> +
> + hve->partition_assist_page = __pa(*p_hv_pa_pg);
> + hve->hv_vm_id = (unsigned long)vcpu->kvm;
> + if (!hve->hv_enlightenments_control.nested_flush_hypercall) {
> + hve->hv_enlightenments_control.nested_flush_hypercall = 1;
> + vmcb_mark_dirty(to_svm(vcpu)->vmcb, VMCB_HV_NESTED_ENLIGHTENMENTS);
> + }
> +
> + return 0;
> +}
> +
> diff --git a/arch/x86/kvm/svm/svm_onhyperv.h b/arch/x86/kvm/svm/svm_onhyperv.h
> index 0f262460b2e6..7487052fcef8 100644
> --- a/arch/x86/kvm/svm/svm_onhyperv.h
> +++ b/arch/x86/kvm/svm/svm_onhyperv.h
> @@ -36,6 +36,8 @@ struct hv_enlightenments {
> */
> #define VMCB_HV_NESTED_ENLIGHTENMENTS VMCB_SW
>
> +int hv_enable_direct_tlbflush(struct kvm_vcpu *vcpu);
> +
> static inline void svm_hv_init_vmcb(struct vmcb *vmcb)
> {
> struct hv_enlightenments *hve =
> @@ -55,6 +57,23 @@ static inline void svm_hv_hardware_setup(void)
> svm_x86_ops.tlb_remote_flush_with_range =
> hv_remote_flush_tlb_with_range;
> }
> +
> + if (ms_hyperv.nested_features & HV_X64_NESTED_DIRECT_FLUSH) {
> + int cpu;
> +
> + pr_info("kvm: Hyper-V Direct TLB Flush enabled\n");
> + for_each_online_cpu(cpu) {
> + struct hv_vp_assist_page *vp_ap =
> + hv_get_vp_assist_page(cpu);
> +
> + if (!vp_ap)
> + continue;
> +
> + vp_ap->nested_control.features.directhypercall = 1;
> + }
> + svm_x86_ops.enable_direct_tlbflush =
> + hv_enable_direct_tlbflush;
> + }
> }
>
> static inline void svm_hv_vmcb_dirty_nested_enlightenments(
> @@ -74,6 +93,18 @@ static inline void svm_hv_vmcb_dirty_nested_enlightenments(
> hve->hv_enlightenments_control.msr_bitmap)
> vmcb_mark_dirty(vmcb, VMCB_HV_NESTED_ENLIGHTENMENTS);
> }
> +
> +static inline void svm_hv_update_vp_id(struct vmcb *vmcb,
> + struct kvm_vcpu *vcpu)
> +{
> + struct hv_enlightenments *hve =
> + (struct hv_enlightenments *)vmcb->control.reserved_sw;
> +
> + if (hve->hv_vp_id != to_hv_vcpu(vcpu)->vp_index) {
> + hve->hv_vp_id = to_hv_vcpu(vcpu)->vp_index;
> + vmcb_mark_dirty(vmcb, VMCB_HV_NESTED_ENLIGHTENMENTS);
> + }
This blows up in testing when no Hyper-V context was created on a vCPU,
e.g. when running KVM selftests (to_hv_vcpu(vcpu) is NULL when no
Hyper-V emulation features were requested on a vCPU but
svm_hv_update_vp_id() is called unconditionally by svm_vcpu_run()).
I'll be sending a patch to fix the immediate issue but I was wondering
why we need to call svm_hv_update_vp_id() from svm_vcpu_run() as VP
index is unlikely to change; we can probably just call it from
kvm_hv_set_msr() instead.
> +}
> #else
>
> static inline void svm_hv_init_vmcb(struct vmcb *vmcb)
> @@ -88,6 +119,11 @@ static inline void svm_hv_vmcb_dirty_nested_enlightenments(
> struct kvm_vcpu *vcpu)
> {
> }
> +
> +static inline void svm_hv_update_vp_id(struct vmcb *vmcb,
> + struct kvm_vcpu *vcpu)
> +{
> +}
> #endif /* CONFIG_HYPERV */
>
> #endif /* __ARCH_X86_KVM_SVM_ONHYPERV_H__ */
--
Vitaly
Hi Vitaly,
>> +
>> +static inline void svm_hv_update_vp_id(struct vmcb *vmcb,
>> + struct kvm_vcpu *vcpu)
>> +{
>> + struct hv_enlightenments *hve =
>> + (struct hv_enlightenments *)vmcb->control.reserved_sw;
>> +
>> + if (hve->hv_vp_id != to_hv_vcpu(vcpu)->vp_index) {
>> + hve->hv_vp_id = to_hv_vcpu(vcpu)->vp_index;
>> + vmcb_mark_dirty(vmcb, VMCB_HV_NESTED_ENLIGHTENMENTS);
>> + }
> This blows up in testing when no Hyper-V context was created on a vCPU,
> e.g. when running KVM selftests (to_hv_vcpu(vcpu) is NULL when no
> Hyper-V emulation features were requested on a vCPU but
> svm_hv_update_vp_id() is called unconditionally by svm_vcpu_run()).
>
> I'll be sending a patch to fix the immediate issue but I was wondering
> why we need to call svm_hv_update_vp_id() from svm_vcpu_run() as VP
> index is unlikely to change; we can probably just call it from
> kvm_hv_set_msr() instead.
Thanks a lot for catching this.
I think you are right, updating at kvm_hv_set_msr() makes sense. I was
following the vmx logic where it also sets the vp_id in vmx_vcpu_run. But it
calls a wrapper "kvm_hv_get_vpindex" which actually checks if hv_vcpu is not
null before the assignment. I should have used that instead, my mistake.
I will look a bit more into it and send out a patch for vmx and svm
after little
more investigation.
Thanks,
Vineeth