add bus lock VM exit support
v2 cover add bus lock VM exit support
Resend to rebase on 5.9-rc1.
---
Add the support for bus lock VM exit in KVM. It is a sub-feature of bus lock detection. Another sub-feature named bus lock debug exception is blocked due to requirement to rework the HW design:
https://lore.kernel.org/lkml/[email protected]/
In this patch series, the first patch applies Sean's refactor to vcpu_vmx.exit_reason available at
https://patchwork.kernel.org/patch/11500659.
It is necessary as bus lock VM exit adds a new modifier bit(bit 26) in exit_reason field in VMCS.
The second patch is the enabling work for bus lock VM exit. Add the support to set the capability to enable bus lock vm exit. The current implementation just exit to user space when handling the bus lock detected in guest.
The concrete throttling policy in user space still needs to be discussed. We can enforce ratelimit on bus lock in guest, just inject some sleep time, or any other ideas?
Document for Bus Lock Detection is now available at the latest "Intel Architecture Instruction Set Extensions Programming Reference".
v1->v2 Changelogs:
- resolve Vitaly's comment to introduce the KVM_EXIT_BUS_LOCK and a capability to enable it.
- add the support to exit to user space when handling bus locks.
- extend the vcpu->run->flags to indicate bus lock detected for other exit reasons when exiting to user space.
Chenyi Qiang (1):
KVM: VMX: Enable bus lock VM exit
Sean Christopherson (1):
KVM: VMX: Convert vcpu_vmx.exit_reason to a union
arch/x86/include/asm/kvm_host.h | 9 +++ arch/x86/include/asm/vmx.h | 1 + arch/x86/include/asm/vmxfeatures.h | 1 + arch/x86/include/uapi/asm/kvm.h | 1 + arch/x86/include/uapi/asm/vmx.h | 4 +- arch/x86/kvm/vmx/capabilities.h | 6 ++ arch/x86/kvm/vmx/nested.c | 42 ++++++++----- arch/x86/kvm/vmx/vmx.c | 97 ++++++++++++++++++++---------- arch/x86/kvm/vmx/vmx.h | 25 +++++++- arch/x86/kvm/x86.c | 36 ++++++++++- arch/x86/kvm/x86.h | 5 ++ include/uapi/linux/kvm.h | 2 + 12 files changed, 179 insertions(+), 50 deletions(-)
Signature · 1 line
2.17.1
RFC v2 1/2 KVM: VMX: Convert vcpu_vmx.exit_reason to a union
From: Sean Christopherson <[email protected]>
Convert vcpu_vmx.exit_reason from a u32 to a union (of size u32). The full VM_EXIT_REASON field is comprised of a 16-bit basic exit reason in bits 15:0, and single-bit modifiers in bits 31:16.
Historically, KVM has only had to worry about handling the "failed VM-Entry" modifier, which could only be set in very specific flows and required dedicated handling. I.e. manually stripping the FAILED_VMENTRY bit was a somewhat viable approach. But even with only a single bit to worry about, KVM has had several bugs related to comparing a basic exit reason against the full exit reason store in vcpu_vmx.
Upcoming Intel features, e.g. SGX, will add new modifier bits that can be set on more or less any VM-Exit, as opposed to the significantly more restricted FAILED_VMENTRY, i.e. correctly handling everything in one-off flows isn't scalable. Tracking exit reason in a union forces code to explicitly choose between consuming the full exit reason and the basic exit, and is a convenient way to document and access the modifiers.
No functional change intended.
--- arch/x86/kvm/vmx/nested.c | 42 ++++++++++++++++--------- arch/x86/kvm/vmx/vmx.c | 64 ++++++++++++++++++++------------------- arch/x86/kvm/vmx/vmx.h | 25 ++++++++++++++- 3 files changed, 84 insertions(+), 47 deletions(-)
arch/x86/kvm/vmx/nested.c +27 -15
@@ -3304,7 +3304,11 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu,struct vmcs12 *vmcs12 =get_vmcs12 (vcpu);enum vm_entry_failure_code entry_failure_code;bool evaluate_pending_interrupts; -u32 exit_reason, failed_index; +union vmx_exit_reason exit_reason = { + .basic =EXIT_REASON_INVALID_STATE , + .failed_vmentry =1 , + }; +u32 failed_index;if (kvm_check_request (KVM_REQ_TLB_FLUSH_CURRENT , vcpu))kvm_vcpu_flush_tlb_current (vcpu);
@@ -3354,7 +3358,7 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu,if (nested_vmx_check_guest_state (vcpu, vmcs12, &entry_failure_code)) { - exit_reason =EXIT_REASON_INVALID_STATE ; + exit_reason.basic =EXIT_REASON_INVALID_STATE ; vmcs12->exit_qualification = entry_failure_code; goto vmentry_fail_vmexit; }
@@ -3365,7 +3369,7 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu, vcpu->arch.tsc_offset += vmcs12->tsc_offset;if (prepare_vmcs02 (vcpu, vmcs12, &entry_failure_code)) { - exit_reason =EXIT_REASON_INVALID_STATE ; + exit_reason.basic =EXIT_REASON_INVALID_STATE ; vmcs12->exit_qualification = entry_failure_code; goto vmentry_fail_vmexit_guest_mode; }
@@ -3375,7 +3379,7 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu, vmcs12->vm_entry_msr_load_addr, vmcs12->vm_entry_msr_load_count);if (failed_index) { - exit_reason =EXIT_REASON_MSR_LOAD_FAIL ; + exit_reason.basic =EXIT_REASON_MSR_LOAD_FAIL ; vmcs12->exit_qualification = failed_index; goto vmentry_fail_vmexit_guest_mode; }
@@ -3443,7 +3447,7 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu,return NVMX_VMENTRY_VMEXIT ;load_vmcs12_host_state (vcpu, vmcs12); - vmcs12->vm_exit_reason = exit_reason |VMX_EXIT_REASONS_FAILED_VMENTRY ; + vmcs12->vm_exit_reason = exit_reason.full;if (enable_shadow_vmcs || vmx->nested.hv_evmcs) vmx->nested.need_vmcs12_to_shadow_sync = true;return NVMX_VMENTRY_VMEXIT ;
@@ -5488,7 +5492,12 @@ static int handle_vmfunc(struct kvm_vcpu *vcpu)return kvm_skip_emulated_instruction (vcpu); fail: -nested_vmx_vmexit (vcpu, vmx->exit_reason, +/* +* This is effectively a reflected VM-Exit, as opposed to a synthesized +* nested VM-Exit. Pass the original exit reason, i.e. don't hardcode +* EXIT_REASON_VMFUNC as the exit reason. +*/ +nested_vmx_vmexit (vcpu, vmx->exit_reason.full,vmx_get_intr_info (vcpu),vmx_get_exit_qual (vcpu));return 1 ;
@@ -5556,7 +5565,8 @@ static bool nested_vmx_exit_handled_io(struct kvm_vcpu *vcpu,* MSR bitmap. This may be the case even when L0 doesn't use MSR bitmaps. */ static bool nested_vmx_exit_handled_msr (struct kvm_vcpu *vcpu, -struct vmcs12 *vmcs12,u32 exit_reason) +struct vmcs12 *vmcs12, +union vmx_exit_reason exit_reason) {u32 msr_index =kvm_rcx_read (vcpu);gpa_t bitmap;
@@ -5570,7 +5580,7 @@ static bool nested_vmx_exit_handled_msr(struct kvm_vcpu *vcpu,* First we need to figure out which of the four to use: */ bitmap = vmcs12->msr_bitmap; -if (exit_reason ==EXIT_REASON_MSR_WRITE ) +if (exit_reason.basic ==EXIT_REASON_MSR_WRITE ) bitmap +=2048 ;if (msr_index >=0xc0000000 ) { msr_index -=0xc0000000 ;
@@ -5707,11 +5717,12 @@ static bool nested_vmx_exit_handled_mtf(struct vmcs12 *vmcs12)* Return true if L0 wants to handle an exit from L2 regardless of whether or not * L1 wants the exit. Only call this when in is_guest_mode (L2). */ -static bool nested_vmx_l0_wants_exit (struct kvm_vcpu *vcpu,u32 exit_reason) +static bool nested_vmx_l0_wants_exit (struct kvm_vcpu *vcpu, +union vmx_exit_reason exit_reason) {u32 intr_info; -switch ((u16 )exit_reason) { +switch ((u16 )exit_reason.basic) {case EXIT_REASON_EXCEPTION_NMI : intr_info =vmx_get_intr_info (vcpu);if (is_nmi (intr_info))
@@ -5767,12 +5778,13 @@ static bool nested_vmx_l0_wants_exit(struct kvm_vcpu *vcpu, u32 exit_reason)* Return 1 if L1 wants to intercept an exit from L2. Only call this when in * is_guest_mode (L2). */ -static bool nested_vmx_l1_wants_exit (struct kvm_vcpu *vcpu,u32 exit_reason) +static bool nested_vmx_l1_wants_exit (struct kvm_vcpu *vcpu, +union vmx_exit_reason exit_reason) {struct vmcs12 *vmcs12 =get_vmcs12 (vcpu);u32 intr_info; -switch ((u16 )exit_reason) { +switch ((u16 )exit_reason.basic) {case EXIT_REASON_EXCEPTION_NMI : intr_info =vmx_get_intr_info (vcpu);if (is_nmi (intr_info))
@@ -5891,7 +5903,7 @@ static bool nested_vmx_l1_wants_exit(struct kvm_vcpu *vcpu, u32 exit_reason)bool nested_vmx_reflect_vmexit (struct kvm_vcpu *vcpu) {struct vcpu_vmx *vmx =to_vmx (vcpu); -u32 exit_reason = vmx->exit_reason; +union vmx_exit_reason exit_reason = vmx->exit_reason;unsigned long exit_qual;u32 exit_intr_info;
@@ -5913,7 +5925,7 @@ bool nested_vmx_reflect_vmexit(struct kvm_vcpu *vcpu) exit_intr_info =vmx_get_intr_info (vcpu); exit_qual =vmx_get_exit_qual (vcpu); -trace_kvm_nested_vmexit (kvm_rip_read (vcpu), exit_reason, exit_qual, +trace_kvm_nested_vmexit (kvm_rip_read (vcpu), exit_reason.full, exit_qual, vmx->idt_vectoring_info, exit_intr_info,vmcs_read32 (VM_EXIT_INTR_ERROR_CODE ),KVM_ISA_VMX );
@@ -5942,7 +5954,7 @@ bool nested_vmx_reflect_vmexit(struct kvm_vcpu *vcpu) } reflect_vmexit: -nested_vmx_vmexit (vcpu, exit_reason, exit_intr_info, exit_qual); +nested_vmx_vmexit (vcpu, exit_reason.full, exit_intr_info, exit_qual);return true; }
arch/x86/kvm/vmx/vmx.c +33 -31
147 lines not on this page · raw message
arch/x86/kvm/vmx/vmx.h +24 -1
@@ -92,6 +92,29 @@ struct pt_desc {struct pt_ctx guest; }; +union vmx_exit_reason { +struct { +u32 basic :16 ; +u32 reserved16 :1 ; +u32 reserved17 :1 ; +u32 reserved18 :1 ; +u32 reserved19 :1 ; +u32 reserved20 :1 ; +u32 reserved21 :1 ; +u32 reserved22 :1 ; +u32 reserved23 :1 ; +u32 reserved24 :1 ; +u32 reserved25 :1 ; +u32 reserved26 :1 ; +u32 enclave_mode :1 ; +u32 smi_pending_mtf :1 ; +u32 smi_from_vmx_root :1 ; +u32 reserved30 :1 ; +u32 failed_vmentry :1 ; + }; +u32 full; +}; +/* * The nested_vmx structure is part of vcpu_vmx, and holds information we need * for correct emulation of VMX (i.e., nested VMX) on this vcpu.
@@ -266,7 +289,7 @@ struct vcpu_vmx {int vpid;bool emulation_required; -u32 exit_reason; +union vmx_exit_reason exit_reason;/* Posted interrupt descriptor */ struct pi_desc pi_desc;
Signature · 1 line
2.17.1
RFC v2 2/2 KVM: VMX: Enable bus lock VM exit
In reply to Chenyi QiangVirtual Machine can exploit bus locks to degrade the performance of system. Bus lock can be caused by split locked access to writeback(WB) memory or by using locks on uncacheable(UC) memory. The bus lock is typically >1000 cycles slower than an atomic operation within a cache line. It also disrupts performance on other cores (which must wait for the bus lock to be released before their memory operations can complete).
To address the threat, bus lock VM exit is introduced to notify the VMM when a bus lock was acquired, allowing it to enforce throttling or other policy based mitigations.
A VMM can enable VM exit due to bus locks by setting a new "Bus Lock Detection" VM-execution control(bit 30 of Secondary Processor-based VM execution controls). If delivery of this VM exit was preempted by a higher priority VM exit (e.g. EPT misconfiguration, EPT violation, APIC access VM exit, APIC write VM exit, exception bitmap exiting), bit 26 of exit reason in vmcs field is set to 1.
In current implementation, the KVM exposes this capability through KVM_CAP_X86_BLD. The user can set it to enable the bus lock VM exit (disabled by default). If bus locks in guest are detected by KVM, exit to user space even when current exit reason is handled by KVM internally. Set a new field KVM_RUN_BUS_LOCK in vcpu->run->flags to inform the user space that there is a bus lock in guest and it is preempted by a higher priority VM exit.
Every bus lock acquired in non-root mode will be recorded in vcpu->stat.bus_locks and exposed through debugfs when the bus lock VM exit is enabled.
Document for Bus Lock VM exit is now available at the latest "Intel Architecture Instruction Set Extensions Programming Reference".
--- arch/x86/include/asm/kvm_host.h | 9 ++++++++ arch/x86/include/asm/vmx.h | 1 + arch/x86/include/asm/vmxfeatures.h | 1 + arch/x86/include/uapi/asm/kvm.h | 1 + arch/x86/include/uapi/asm/vmx.h | 4 +++- arch/x86/kvm/vmx/capabilities.h | 6 +++++ arch/x86/kvm/vmx/vmx.c | 33 ++++++++++++++++++++++++++- arch/x86/kvm/vmx/vmx.h | 2 +- arch/x86/kvm/x86.c | 36 +++++++++++++++++++++++++++++- arch/x86/kvm/x86.h | 5 +++++ include/uapi/linux/kvm.h | 2 ++ 11 files changed, 96 insertions(+), 4 deletions(-)
arch/x86/include/asm/kvm_host.h +9 -0
@@ -788,6 +788,9 @@ struct kvm_vcpu_arch {/* AMD MSRC001_0015 Hardware Configuration */ u64 msr_hwcr; + +/* Set when bus lock VM exit is preempted by a higher priority VM exit */ +bool bus_lock_detected; };struct kvm_lpage_info {
@@ -961,6 +964,9 @@ struct kvm_arch {bool guest_can_read_msr_platform_info;bool exception_payload_enabled; +/* Set when bus lock vm exit is enabled by user */ +bool bus_lock_exit; +struct kvm_pmu_event_filter *pmu_event_filter;struct task_struct *nx_lpage_recovery_thread; };
@@ -1010,6 +1016,7 @@ struct kvm_vcpu_stat {u64 req_event;u64 halt_poll_success_ns;u64 halt_poll_fail_ns; +u64 bus_locks; };struct x86_instruction_info ;
@@ -1347,6 +1354,8 @@ extern u8 kvm_tsc_scaling_ratio_frac_bits;extern u64 kvm_max_tsc_scaling_ratio;/* 1ull << kvm_tsc_scaling_ratio_frac_bits */ extern u64 kvm_default_tsc_scaling_ratio; +/* bus lock detection supported */ +extern bool kvm_has_bus_lock_exit;extern u64 kvm_mce_cap_supported;
arch/x86/include/asm/vmx.h +1 -0
@@ -73,6 +73,7 @@#define SECONDARY_EXEC_PT_USE_GPA VMCS_CONTROL_BIT(PT_USE_GPA)#define SECONDARY_EXEC_TSC_SCALING VMCS_CONTROL_BIT(TSC_SCALING)#define SECONDARY_EXEC_ENABLE_USR_WAIT_PAUSE VMCS_CONTROL_BIT(USR_WAIT_PAUSE) +#define SECONDARY_EXEC_BUS_LOCK_DETECTION VMCS_CONTROL_BIT(BUS_LOCK_DETECTION)#define PIN_BASED_EXT_INTR_MASK VMCS_CONTROL_BIT(INTR_EXITING)#define PIN_BASED_NMI_EXITING VMCS_CONTROL_BIT(NMI_EXITING)
arch/x86/include/asm/vmxfeatures.h +1 -0
@@ -83,5 +83,6 @@#define VMX_FEATURE_TSC_SCALING ( 2*32+ 25)/* Scale hardware TSC when read in guest */ #define VMX_FEATURE_USR_WAIT_PAUSE ( 2*32+ 26)/* Enable TPAUSE, UMONITOR, UMWAIT in guest */ #define VMX_FEATURE_ENCLV_EXITING ( 2*32+ 28)/* "" VM-Exit on ENCLV (leaf dependent) */ +#define VMX_FEATURE_BUS_LOCK_DETECTION ( 2*32+ 30)/* VM-Exit when bus lock caused */ #endif /* _ASM_X86_VMXFEATURES_H */
arch/x86/include/uapi/asm/kvm.h +1 -0
@@ -111,6 +111,7 @@ struct kvm_ioapic_state {#define KVM_NR_IRQCHIPS 3#define KVM_RUN_X86_SMM (1 << 0) +#define KVM_RUN_BUS_LOCK (1 << 1)/* for KVM_GET_REGS and KVM_SET_REGS */ struct kvm_regs {
arch/x86/include/uapi/asm/vmx.h +3 -1
@@ -88,6 +88,7 @@#define EXIT_REASON_XRSTORS 64#define EXIT_REASON_UMWAIT 67#define EXIT_REASON_TPAUSE 68 +#define EXIT_REASON_BUS_LOCK 74#define VMX_EXIT_REASONS \ { EXIT_REASON_EXCEPTION_NMI, "EXCEPTION_NMI" }, \
@@ -148,7 +149,8 @@ { EXIT_REASON_XSAVES, "XSAVES" }, \ { EXIT_REASON_XRSTORS, "XRSTORS" }, \ { EXIT_REASON_UMWAIT, "UMWAIT" }, \ - { EXIT_REASON_TPAUSE, "TPAUSE" } + { EXIT_REASON_TPAUSE, "TPAUSE" }, \ + { EXIT_REASON_BUS_LOCK, "BUS_LOCK" } #define VMX_EXIT_REASON_FLAGS \ { VMX_EXIT_REASONS_FAILED_VMENTRY, "FAILED_VMENTRY" }
arch/x86/kvm/vmx/capabilities.h +6 -0
@@ -262,6 +262,12 @@ static inline bool cpu_has_vmx_tsc_scaling(void)SECONDARY_EXEC_TSC_SCALING ; } +static inline bool cpu_has_vmx_bus_lock_detection (void ) +{ +return vmcs_config.cpu_based_2nd_exec_ctrl & +SECONDARY_EXEC_BUS_LOCK_DETECTION ; +} +static inline bool cpu_has_vmx_apicv (void ) { return cpu_has_vmx_apic_register_virt() &&
arch/x86/kvm/vmx/vmx.c +32 -1
@@ -2461,7 +2461,8 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf,SECONDARY_EXEC_ENABLE_USR_WAIT_PAUSE |SECONDARY_EXEC_PT_USE_GPA |SECONDARY_EXEC_PT_CONCEAL_VMX | -SECONDARY_EXEC_ENABLE_VMFUNC ; +SECONDARY_EXEC_ENABLE_VMFUNC | +SECONDARY_EXEC_BUS_LOCK_DETECTION ;if (cpu_has_sgx ()) opt2 |=SECONDARY_EXEC_ENCLS_EXITING ; if (adjust_vmx_controls(min2, opt2,
@@ -4244,6 +4245,9 @@ static void vmx_compute_secondary_exec_control(struct vcpu_vmx *vmx) } } +if (!kvm_bus_lock_exit_enabled (vmx->vcpu.kvm)) + exec_control &= ~SECONDARY_EXEC_BUS_LOCK_DETECTION ; + vmx->secondary_exec_control = exec_control; }
@@ -5685,6 +5689,16 @@ static int handle_encls(struct kvm_vcpu *vcpu)return 1 ; } +static int handle_bus_lock (struct kvm_vcpu *vcpu) +{ +struct kvm_run *kvm_run = vcpu->run; + + vcpu->stat.bus_locks++; + + kvm_run->exit_reason =KVM_EXIT_BUS_LOCK ; +return 0 ; +} +/* * The exit handlers return 1 if the exit was handled fully and guest execution * may resume. Otherwise they set the kvm_run parameter to indicate what needs
@@ -5741,6 +5755,7 @@ static int (*kvm_vmx_exit_handlers[])(struct kvm_vcpu *vcpu) = { [EXIT_REASON_VMFUNC] = handle_vmx_instruction, [EXIT_REASON_PREEMPTION_TIMER] = handle_preemption_timer, [EXIT_REASON_ENCLS] = handle_encls, + [EXIT_REASON_BUS_LOCK] = handle_bus_lock, }; static const int kvm_vmx_max_exit_handlers =
@@ -6884,6 +6899,19 @@ static fastpath_t vmx_vcpu_run(struct kvm_vcpu *vcpu)if (unlikely (vmx->exit_reason.failed_vmentry))return EXIT_FASTPATH_NONE ; +/* +* check the exit_reason to see if there is a bus lock +* happened in guest. +*/ +if (kvm_bus_lock_exit_enabled (vmx->vcpu.kvm)) { +if (vmx->exit_reason.bus_lock_detected) { + vcpu->stat.bus_locks++; + vcpu->arch.bus_lock_detected = true; + }else { + vcpu->arch.bus_lock_detected = false; + } + } + vmx->loaded_vmcs->launched =1 ; vmx->idt_vectoring_info =vmcs_read32 (IDT_VECTORING_INFO_FIELD );
@@ -8097,6 +8125,9 @@ static __init int hardware_setup(void) kvm_tsc_scaling_ratio_frac_bits =48 ; } +if (cpu_has_vmx_bus_lock_detection ()) + kvm_has_bus_lock_exit = true; +set_bit (0 , vmx_vpid_bitmap);/* 0 is reserved for host */ if (enable_ept)
arch/x86/kvm/vmx/vmx.h +1 -1
@@ -105,7 +105,7 @@ union vmx_exit_reason { u32 reserved23 : 1; u32 reserved24 : 1; u32 reserved25 : 1; - u32 reserved26 : 1; + u32 bus_lock_detected : 1; u32 enclave_mode : 1; u32 smi_pending_mtf : 1; u32 smi_from_vmx_root : 1;
arch/x86/kvm/x86.c +35 -1
@@ -134,6 +134,8 @@ u64 __read_mostly kvm_max_tsc_scaling_ratio;EXPORT_SYMBOL_GPL (kvm_max_tsc_scaling_ratio);u64 __read_mostly kvm_default_tsc_scaling_ratio;EXPORT_SYMBOL_GPL (kvm_default_tsc_scaling_ratio); +bool __read_mostly kvm_has_bus_lock_exit; +EXPORT_SYMBOL_GPL (kvm_has_bus_lock_exit);/* tsc tolerance in parts per million - default to 1/2 of the NTP threshold */ static u32 __read_mostly tsc_tolerance_ppm =250 ;
@@ -224,6 +226,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {VCPU_STAT ("l1d_flush" , l1d_flush),VCPU_STAT ("halt_poll_success_ns" , halt_poll_success_ns),VCPU_STAT ("halt_poll_fail_ns" , halt_poll_fail_ns), +VCPU_STAT ("bus_locks" , bus_locks),VM_STAT ("mmu_shadow_zapped" , mmu_shadow_zapped),VM_STAT ("mmu_pte_write" , mmu_pte_write), VM_STAT("mmu_pte_updated", mmu_pte_updated),
@@ -3581,6 +3584,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)case KVM_CAP_SMALLER_MAXPHYADDR : r = (int ) allow_smaller_maxphyaddr;break ; +case KVM_CAP_X86_BUS_LOCK_EXIT : + r = kvm_has_bus_lock_exit; +break ;default :break ; }
@@ -5033,6 +5039,12 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, kvm->arch.exception_payload_enabled = cap->args[0 ]; r =0 ;break ; +case KVM_CAP_X86_BUS_LOCK_EXIT : +if (!kvm_has_bus_lock_exit) +return -EINVAL ; + kvm->arch.bus_lock_exit = cap->args[0 ]; + r =0 ; +break ;default : r = -EINVAL ;break ;
@@ -7775,12 +7787,23 @@ static void post_kvm_run_save(struct kvm_vcpu *vcpu)struct kvm_run *kvm_run = vcpu->run; kvm_run->if_flag = (kvm_get_rflags (vcpu) &X86_EFLAGS_IF ) !=0 ; - kvm_run->flags =is_smm (vcpu) ?KVM_RUN_X86_SMM :0 ; kvm_run->cr8 =kvm_get_cr8 (vcpu); kvm_run->apic_base =kvm_get_apic_base (vcpu); kvm_run->ready_for_interrupt_injection =pic_in_kernel (vcpu->kvm) ||kvm_vcpu_ready_for_interrupt_injection (vcpu); + +if (is_smm (vcpu)) + kvm_run->flags |=KVM_RUN_X86_SMM ; +else + kvm_run->flags &= ~KVM_RUN_X86_SMM ; + +if (vcpu->arch.bus_lock_detected && + kvm_run->exit_reason !=KVM_EXIT_BUS_LOCK ) + kvm_run->flags |=KVM_RUN_BUS_LOCK ; +else + kvm_run->flags &= ~KVM_RUN_BUS_LOCK ; + }static void update_cr8_intercept (struct kvm_vcpu *vcpu)
@@ -8639,6 +8662,17 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)kvm_lapic_sync_from_vapic (vcpu); r = kvm_x86_ops.handle_exit (vcpu, exit_fastpath); + +/* +* Even when current exit reason is handled by KVM +* internally, we still needs to exit to user space +* when bus lock detected to inform that there is a +* bus lock in guest. +*/ +if (r >0 && vcpu->arch.bus_lock_detected) { + vcpu->run->exit_reason =KVM_EXIT_BUS_LOCK ; + r =0 ; + }return r; cancel_injection:
arch/x86/kvm/x86.h +5 -0
@@ -335,6 +335,11 @@ static inline bool kvm_cstate_in_guest(struct kvm *kvm)return kvm->arch.cstate_in_guest; } +static inline bool kvm_bus_lock_exit_enabled (struct kvm *kvm) +{ +return kvm->arch.bus_lock_exit; +} + DECLARE_PER_CPU(struct kvm_vcpu *, current_vcpu);static inline void kvm_before_interrupt (struct kvm_vcpu *vcpu)
include/uapi/linux/kvm.h +2 -0
@@ -248,6 +248,7 @@ struct kvm_hyperv_exit {#define KVM_EXIT_IOAPIC_EOI 26#define KVM_EXIT_HYPERV 27#define KVM_EXIT_ARM_NISV 28 +#define KVM_EXIT_BUS_LOCK 29/* For KVM_EXIT_INTERNAL_ERROR */ /* Emulate instruction failed. */
@@ -1035,6 +1036,7 @@ struct kvm_ppc_resize_hpt {#define KVM_CAP_LAST_CPU 184#define KVM_CAP_SMALLER_MAXPHYADDR 185#define KVM_CAP_S390_DIAG318 186 +#define KVM_CAP_X86_BUS_LOCK_EXIT 187#ifdef KVM_CAP_IRQ_ROUTING
Signature · 1 line
2.17.1
Ping for comments
Quoted 58 lines · Chenyi Qiang
Resend to rebase on 5.9-rc1.
---
Add the support for bus lock VM exit in KVM. It is a sub-feature of bus lock detection. Another sub-feature named bus lock debug exception is blocked due to requirement to rework the HW design:
https://lore.kernel.org/lkml/[email protected]/In this patch series, the first patch applies Sean's refactor to vcpu_vmx.exit_reason available at
https://patchwork.kernel.org/patch/11500659.
It is necessary as bus lock VM exit adds a new modifier bit(bit 26) inexit_reasonfield in VMCS.The second patch is the enabling work for bus lock VM exit. Add the support to set the capability to enable bus lock vm exit. The current implementation just exit to user space when handling the bus lock detected in guest.
The concrete throttling policy in user space still needs to be discussed. We can enforce ratelimit on bus lock in guest, just inject some sleep time, or any other ideas?
Document for Bus Lock Detection is now available at the latest "Intel Architecture Instruction Set Extensions Programming Reference".
v1->v2Changelogs:
- resolve Vitaly's comment to introduce theKVM_EXIT_BUS_LOCKand a capability to enable it.
- add the support to exit to user space when handling bus locks.
- extend thevcpu->run->flagsto indicate bus lock detected for other exit reasons when exiting to user space.Chenyi Qiang (1):
KVM: VMX: Enable bus lock VM exitSean Christopherson (1):
KVM: VMX: Convert vcpu_vmx.exit_reason to a unionarch/x86/include/asm/kvm_host.h | 9 +++ arch/x86/include/asm/vmx.h | 1 + arch/x86/include/asm/vmxfeatures.h | 1 + arch/x86/include/uapi/asm/kvm.h | 1 + arch/x86/include/uapi/asm/vmx.h | 4 +- arch/x86/kvm/vmx/capabilities.h | 6 ++ arch/x86/kvm/vmx/nested.c | 42 ++++++++----- arch/x86/kvm/vmx/vmx.c | 97 ++++++++++++++++++++---------- arch/x86/kvm/vmx/vmx.h | 25 +++++++- arch/x86/kvm/x86.c | 36 ++++++++++- arch/x86/kvm/x86.h | 5 ++ include/uapi/linux/kvm.h | 2 + 12 files changed, 179 insertions(+), 50 deletions(-)