Il 04/01/2014 18:47, Jan Kiszka ha scritto:
> From: Jan Kiszka <[email protected]>
>
> Move the check for leaving L2 on pending and intercepted IRQs or NMIs
> from the *_allowed handler into a dedicated callback. Invoke this
> callback at the relevant points before KVM checks if IRQs/NMIs can be
> injected. The callback has the task to switch from L2 to L1 if needed
> and inject the proper vmexit events.
>
> The rework fixes L2 wakeups from HLT and provides the foundation for
> preemption timer emulation.
>
> Signed-off-by: Jan Kiszka <[email protected]>
> ---
> arch/x86/include/asm/kvm_host.h | 2 ++
> arch/x86/kvm/vmx.c | 67
> +++++++++++++++++++++++------------------
> arch/x86/kvm/x86.c | 15 +++++++--
> 3 files changed, 53 insertions(+), 31 deletions(-)
>
> diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
> index e73651b..d195421 100644
> --- a/arch/x86/include/asm/kvm_host.h
> +++ b/arch/x86/include/asm/kvm_host.h
> @@ -764,6 +764,8 @@ struct kvm_x86_ops {
> struct x86_instruction_info *info,
> enum x86_intercept_stage stage);
> void (*handle_external_intr)(struct kvm_vcpu *vcpu);
> +
> + int (*check_nested_events)(struct kvm_vcpu *vcpu, bool external_intr);
> };
>
> struct kvm_arch_async_pf {
> diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
> index 1245ff1..ec8a976 100644
> --- a/arch/x86/kvm/vmx.c
> +++ b/arch/x86/kvm/vmx.c
> @@ -4620,22 +4620,8 @@ static void vmx_set_nmi_mask(struct kvm_vcpu *vcpu,
> bool masked)
>
> static int vmx_nmi_allowed(struct kvm_vcpu *vcpu)
> {
> - if (is_guest_mode(vcpu)) {
> - if (to_vmx(vcpu)->nested.nested_run_pending)
> - return 0;
> - if (nested_exit_on_nmi(vcpu)) {
> - nested_vmx_vmexit(vcpu, EXIT_REASON_EXCEPTION_NMI,
> - NMI_VECTOR | INTR_TYPE_NMI_INTR |
> - INTR_INFO_VALID_MASK, 0);
> - /*
> - * The NMI-triggered VM exit counts as injection:
> - * clear this one and block further NMIs.
> - */
> - vcpu->arch.nmi_pending = 0;
> - vmx_set_nmi_mask(vcpu, true);
> - return 0;
> - }
> - }
> + if (to_vmx(vcpu)->nested.nested_run_pending)
> + return 0;
>
> if (!cpu_has_virtual_nmis() && to_vmx(vcpu)->soft_vnmi_blocked)
> return 0;
> @@ -4647,19 +4633,8 @@ static int vmx_nmi_allowed(struct kvm_vcpu *vcpu)
>
> static int vmx_interrupt_allowed(struct kvm_vcpu *vcpu)
> {
> - if (is_guest_mode(vcpu)) {
> - if (to_vmx(vcpu)->nested.nested_run_pending)
> - return 0;
> - if (nested_exit_on_intr(vcpu)) {
> - nested_vmx_vmexit(vcpu, EXIT_REASON_EXTERNAL_INTERRUPT,
> - 0, 0);
> - /*
> - * fall through to normal code, but now in L1, not L2
> - */
> - }
> - }
> -
> - return (vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_IF) &&
> + return (!to_vmx(vcpu)->nested.nested_run_pending &&
> + vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_IF) &&
> !(vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) &
> (GUEST_INTR_STATE_STI | GUEST_INTR_STATE_MOV_SS));
> }
> @@ -8158,6 +8133,35 @@ static void vmcs12_save_pending_event(struct kvm_vcpu
> *vcpu,
> }
> }
>
> +static int vmx_check_nested_events(struct kvm_vcpu *vcpu, bool external_intr)
> +{
> + struct vcpu_vmx *vmx = to_vmx(vcpu);
> +
> + if (vcpu->arch.nmi_pending && nested_exit_on_nmi(vcpu)) {
> + if (vmx->nested.nested_run_pending)
> + return -EBUSY;
> + nested_vmx_vmexit(vcpu, EXIT_REASON_EXCEPTION_NMI,
> + NMI_VECTOR | INTR_TYPE_NMI_INTR |
> + INTR_INFO_VALID_MASK, 0);
> + /*
> + * The NMI-triggered VM exit counts as injection:
> + * clear this one and block further NMIs.
> + */
> + vcpu->arch.nmi_pending = 0;
> + vmx_set_nmi_mask(vcpu, true);
> + return 0;
> + }
> +
> + if ((kvm_cpu_has_interrupt(vcpu) || external_intr) &&
> + nested_exit_on_intr(vcpu)) {
> + if (vmx->nested.nested_run_pending)
> + return -EBUSY;
> + nested_vmx_vmexit(vcpu, EXIT_REASON_EXTERNAL_INTERRUPT, 0, 0);
> + }
> +
> + return 0;
> +}
> +
> /*
> * prepare_vmcs12 is part of what we need to do when the nested L2 guest
> exits
> * and we want to prepare to run its L1 parent. L1 keeps a vmcs for L2
> (vmcs12),
> @@ -8498,6 +8502,9 @@ static void nested_vmx_vmexit(struct kvm_vcpu *vcpu,
> u32 exit_reason,
> nested_vmx_succeed(vcpu);
> if (enable_shadow_vmcs)
> vmx->nested.sync_shadow_vmcs = true;
> +
> + /* in case we halted in L2 */
> + vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE;
> }
>
> /*
> @@ -8637,6 +8644,8 @@ static struct kvm_x86_ops vmx_x86_ops = {
>
> .check_intercept = vmx_check_intercept,
> .handle_external_intr = vmx_handle_external_intr,
> +
> + .check_nested_events = vmx_check_nested_events,
> };
>
> static int __init vmx_init(void)
> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index 559ae75..8746b7e 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
> @@ -5846,6 +5846,9 @@ static void inject_pending_event(struct kvm_vcpu *vcpu)
> return;
> }
>
> + if (is_guest_mode(vcpu) && kvm_x86_ops->check_nested_events)
> + kvm_x86_ops->check_nested_events(vcpu, false);
> +
> /* try to inject new event if pending */
> if (vcpu->arch.nmi_pending) {
> if (kvm_x86_ops->nmi_allowed(vcpu)) {
> @@ -5966,12 +5969,17 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
>
> inject_pending_event(vcpu);
>
> + if (is_guest_mode(vcpu) && kvm_x86_ops->check_nested_events)
> + req_immediate_exit |=
> + kvm_x86_ops->check_nested_events(vcpu,
> + req_int_win);
Please add "!= 0" like below. For now I only have this cosmetic
comment, I may have more questions when I port SVM to the new framework.
Thanks,
Paolo
> /* enable NMI/IRQ window open exits if needed */
> if (vcpu->arch.nmi_pending)
> - req_immediate_exit =
> + req_immediate_exit |=
> kvm_x86_ops->enable_nmi_window(vcpu) != 0;
> else if (kvm_cpu_has_injectable_intr(vcpu) || req_int_win)
> - req_immediate_exit =
> + req_immediate_exit |=
> kvm_x86_ops->enable_irq_window(vcpu) != 0;
>
> if (kvm_lapic_enabled(vcpu)) {
> @@ -7295,6 +7303,9 @@ void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
>
> int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu)
> {
> + if (is_guest_mode(vcpu) && kvm_x86_ops->check_nested_events)
> + kvm_x86_ops->check_nested_events(vcpu, false);
> +
> return (vcpu->arch.mp_state == KVM_MP_STATE_RUNNABLE &&
> !vcpu->arch.apf.halted)
> || !list_empty_careful(&vcpu->async_pf.done)
>
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html