@@ -99,6 +99,7 @@ static inline int __apic_test_and_clear_vector(int vec, void *bitmap)
}
struct static_key_deferred apic_hw_disabled __read_mostly;
+EXPORT_SYMBOL_GPL(apic_hw_disabled);
struct static_key_deferred apic_sw_disabled __read_mostly;
static inline int apic_enabled(struct kvm_lapic *apic)
@@ -292,21 +293,6 @@ static inline int apic_lvt_vector(struct kvm_lapic *apic, int lvt_type)
return kvm_lapic_get_reg(apic, lvt_type) & APIC_VECTOR_MASK;
}
-static inline int apic_lvtt_oneshot(struct kvm_lapic *apic)
-{
- return apic->lapic_timer.timer_mode == APIC_LVT_TIMER_ONESHOT;
-}
-
-static inline int apic_lvtt_period(struct kvm_lapic *apic)
-{
- return apic->lapic_timer.timer_mode == APIC_LVT_TIMER_PERIODIC;
-}
-
-static inline int apic_lvtt_tscdeadline(struct kvm_lapic *apic)
-{
- return apic->lapic_timer.timer_mode == APIC_LVT_TIMER_TSCDEADLINE;
-}
-
static inline int apic_lvt_nmi_mode(u32 lvt_val)
{
return (lvt_val & (APIC_MODE_MASK | APIC_LVT_MASKED)) == APIC_DM_NMI;
@@ -2462,6 +2448,7 @@ void kvm_lapic_sync_to_vapic(struct kvm_vcpu *vcpu)
kvm_write_guest_cached(vcpu->kvm, &vcpu->arch.apic->vapic_cache, &data,
sizeof(u32));
}
+EXPORT_SYMBOL_GPL(kvm_lapic_sync_to_vapic);
int kvm_lapic_set_vapic_addr(struct kvm_vcpu *vcpu, gpa_t vapic_addr)
{
@@ -105,6 +105,7 @@ void kvm_set_lapic_tscdeadline_msr(struct kvm_vcpu *vcpu, u64 data);
void kvm_apic_write_nodecode(struct kvm_vcpu *vcpu, u32 offset);
void kvm_apic_set_eoi_accelerated(struct kvm_vcpu *vcpu, int vector);
+void kvm_update_cr8_intercept(struct kvm_vcpu *vcpu);
int kvm_lapic_set_vapic_addr(struct kvm_vcpu *vcpu, gpa_t vapic_addr);
void kvm_lapic_sync_from_vapic(struct kvm_vcpu *vcpu);
void kvm_lapic_sync_to_vapic(struct kvm_vcpu *vcpu);
@@ -234,4 +235,19 @@ static inline enum lapic_mode kvm_apic_mode(u64 apic_base)
return apic_base & (MSR_IA32_APICBASE_ENABLE | X2APIC_ENABLE);
}
+static inline int apic_lvtt_oneshot(struct kvm_lapic *apic)
+{
+ return apic->lapic_timer.timer_mode == APIC_LVT_TIMER_ONESHOT;
+}
+
+static inline int apic_lvtt_period(struct kvm_lapic *apic)
+{
+ return apic->lapic_timer.timer_mode == APIC_LVT_TIMER_PERIODIC;
+}
+
+static inline int apic_lvtt_tscdeadline(struct kvm_lapic *apic)
+{
+ return apic->lapic_timer.timer_mode == APIC_LVT_TIMER_TSCDEADLINE;
+}
+
#endif
@@ -9971,10 +9971,137 @@ static void vmx_arm_hv_timer(struct kvm_vcpu *vcpu)
vmcs_write32(VMX_PREEMPTION_TIMER_VALUE, delta_tsc);
}
+#ifdef CONFIG_X86_64
+static int vmx_set_hv_timer(struct kvm_vcpu *vcpu, u64 guest_deadline_tsc);
+static void vmx_cancel_hv_timer(struct kvm_vcpu *vcpu);
+#endif
+
+/*
+ * handle tsc deadline setting in fast path
+ */
+static int handle_wrmsr_tscdeadline_fast(struct kvm_vcpu *vcpu, u64 data)
+{
+ int ret = -EOPNOTSUPP;
+#ifdef CONFIG_X86_64
+ struct kvm_lapic *apic = vcpu->arch.apic;
+
+ if (!kvm_x86_ops->set_hv_timer ||
+ !lapic_in_kernel(vcpu) ||
+ apic_lvtt_oneshot(apic) ||
+ apic_lvtt_period(apic)) {
+ goto out;
+ }
+ /* simplified version of kvm_set_lapic_tscdeadline_msr(vcpu, msr.data)
+ * lapic timer advance should be addressed?
+ */
+ atomic_set(&apic->lapic_timer.pending, 0);
+ apic->lapic_timer.tscdeadline = data;
+ if (data) {
+ apic->lapic_timer.hv_timer_in_use = true;
+ ret = vmx_set_hv_timer(vcpu, data) < 0 ? -1 : 0;
+ }
+out:
+#endif
+ return ret;
+}
+
+static int handle_wrmsr_fast(struct kvm_vcpu *vcpu)
+{
+ /*
+ * fast path of handle_wrmsr() -> kvm_set_msr() -> vmx_set_msr()
+ */
+ int ret = -EOPNOTSUPP;
+
+ struct msr_data msr;
+ u32 ecx = vcpu->arch.regs[VCPU_REGS_RCX];
+ u64 data = (vcpu->arch.regs[VCPU_REGS_RAX] & -1u)
+ | ((u64)(vcpu->arch.regs[VCPU_REGS_RDX] & -1u) << 32);
+
+ msr.data = data;
+ msr.index = ecx;
+ msr.host_initiated = false;
+
+ /* simplified version of kvm_set_msr(vcpu, &msr) */
+ switch (msr.index) {
+ case MSR_IA32_TSCDEADLINE:
+ ret = handle_wrmsr_tscdeadline_fast(vcpu, data);
+ break;
+ /* more MSRs to be optimized? */
+ default:
+ break;
+ }
+
+ if (ret == 0)
+ ret = kvm_skip_emulated_instruction(vcpu) ? 0 : -EOPNOTSUPP;
+ return ret;
+}
+
+static int handle_preemption_timer_fast(struct kvm_vcpu *vcpu)
+{
+ int ret = -EOPNOTSUPP;
+#ifdef CONFIG_X86_64
+ struct vcpu_vmx *vmx = to_vmx(vcpu);
+ struct kvm_lapic *apic = vcpu->arch.apic;
+ struct kvm_timer *ktimer = &apic->lapic_timer;
+ u32 reg;
+ int vector;
+
+ /* don't care complicated cases */
+ if (!vmx_interrupt_allowed(vcpu) ||
+ vmx->rmode.vm86_active ||
+ !apic_lvtt_tscdeadline(apic) ||
+ !kvm_lapic_hv_timer_in_use(vcpu) ||
+ is_smm(vcpu) ||
+ atomic_read(&apic->lapic_timer.pending) ||
+ !kvm_apic_hw_enabled(apic))
+ goto out;
+ vmx_cancel_hv_timer(vcpu);
+ ktimer->hv_timer_in_use = false;
+ ktimer->expired_tscdeadline = ktimer->tscdeadline;
+ apic->lapic_timer.tscdeadline = 0;
+
+ reg = kvm_lapic_get_reg(apic, APIC_LVTT);
+ if (reg & APIC_LVT_MASKED)
+ goto out;
+ vector = reg & APIC_VECTOR_MASK;
+ kvm_lapic_set_vector(vector, apic->regs + APIC_TMR);
+ if (vcpu->arch.apicv_active) {
+ /* simplified version of
+ * vmx_deliver_posted_interrupt(vcpu, vector);
+ */
+ pi_test_and_set_pir(vector, &vmx->pi_desc);
+ pi_test_and_set_on(&vmx->pi_desc);
+ } else {
+ kvm_lapic_set_irr(vector, apic);
+
+ if (!vcpu->arch.apicv_active)
+ kvm_update_cr8_intercept(vcpu);
+ /* simplified version of vmx_inject_irq() */
+ vmcs_write32(VM_ENTRY_INTR_INFO_FIELD,
+ vector | INTR_INFO_VALID_MASK |
+ INTR_TYPE_EXT_INTR);
+ vmx_clear_hlt(vcpu);
+ }
+ ret = 0;
+out:
+#endif
+ return ret;
+}
+
+static int (*const kvm_vmx_exit_handlers_fast[]) (struct kvm_vcpu *vcpu) = {
+ [EXIT_REASON_MSR_WRITE] = handle_wrmsr_fast,
+ [EXIT_REASON_PREEMPTION_TIMER] = handle_preemption_timer_fast,
+};
+
+static const int kvm_vmx_max_exit_handlers_fast =
+ ARRAY_SIZE(kvm_vmx_exit_handlers_fast);
+
+
static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu)
{
struct vcpu_vmx *vmx = to_vmx(vcpu);
unsigned long cr3, cr4, evmcs_rsp;
+continue_vmx_vcpu_run:
/* Record the guest's net vcpu time for enforced NMI injections. */
if (unlikely(!enable_vnmi &&
@@ -10242,6 +10369,27 @@ static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu)
vmx_complete_atomic_exit(vmx);
vmx_recover_nmi_blocking(vmx);
+
+ if (!is_guest_mode(vcpu) &&
+ vmx->exit_reason < kvm_vmx_max_exit_handlers_fast &&
+ kvm_vmx_exit_handlers_fast[vmx->exit_reason]) {
+ bool idtv_info_valid =
+ vmx->idt_vectoring_info & VECTORING_INFO_VALID_MASK;
+ int type = vmx->idt_vectoring_info & VECTORING_INFO_TYPE_MASK;
+
+ if (!idtv_info_valid ||
+ !(type == INTR_TYPE_NMI_INTR ||
+ type == INTR_TYPE_SOFT_EXCEPTION ||
+ type == INTR_TYPE_SOFT_INTR ||
+ type == INTR_TYPE_EXT_INTR)) {
+ int ret = kvm_vmx_exit_handlers_fast[vmx->exit_reason](
+ vcpu);
+
+ if (ret == 0)
+ goto continue_vmx_vcpu_run;
+ }
+ }
+
vmx_complete_interrupts(vmx);
}
STACK_FRAME_NON_STANDARD(vmx_vcpu_run);
@@ -98,7 +98,6 @@ static u64 __read_mostly efer_reserved_bits = ~((u64)EFER_SCE);
#define KVM_X2APIC_API_VALID_FLAGS (KVM_X2APIC_API_USE_32BIT_IDS | \
KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK)
-static void update_cr8_intercept(struct kvm_vcpu *vcpu);
static void process_nmi(struct kvm_vcpu *vcpu);
static void enter_smm(struct kvm_vcpu *vcpu);
static void __kvm_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags);
@@ -3158,7 +3157,7 @@ static int kvm_vcpu_ioctl_set_lapic(struct kvm_vcpu *vcpu,
r = kvm_apic_set_state(vcpu, s);
if (r)
return r;
- update_cr8_intercept(vcpu);
+ kvm_update_cr8_intercept(vcpu);
return 0;
}
@@ -6775,7 +6774,7 @@ static void post_kvm_run_save(struct kvm_vcpu *vcpu)
kvm_vcpu_ready_for_interrupt_injection(vcpu);
}
-static void update_cr8_intercept(struct kvm_vcpu *vcpu)
+void kvm_update_cr8_intercept(struct kvm_vcpu *vcpu)
{
int max_irr, tpr;
@@ -6800,6 +6799,7 @@ static void update_cr8_intercept(struct kvm_vcpu *vcpu)
kvm_x86_ops->update_cr8_intercept(vcpu, tpr, max_irr);
}
+EXPORT_SYMBOL_GPL(kvm_update_cr8_intercept);
static int inject_pending_event(struct kvm_vcpu *vcpu, bool req_int_win)
{
@@ -7372,7 +7372,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
}
if (kvm_lapic_enabled(vcpu)) {
- update_cr8_intercept(vcpu);
+ kvm_update_cr8_intercept(vcpu);
kvm_lapic_sync_to_vapic(vcpu);
}
}
@@ -8069,7 +8069,7 @@ static int __set_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs)
kvm_set_segment(vcpu, &sregs->tr, VCPU_SREG_TR);
kvm_set_segment(vcpu, &sregs->ldt, VCPU_SREG_LDTR);
- update_cr8_intercept(vcpu);
+ kvm_update_cr8_intercept(vcpu);
/* Older userspace won't unhalt the vcpu on reset. */
if (kvm_vcpu_is_bsp(vcpu) && kvm_rip_read(vcpu) == 0xfff0 &&