From patchwork Thu Nov 26 12:05:37 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Woodhouse X-Patchwork-Id: 11933401 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-17.2 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_SANE_2 autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id E2945C56202 for ; Thu, 26 Nov 2020 12:05:49 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 39C6320857 for ; Thu, 26 Nov 2020 12:05:49 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=infradead.org header.i=@infradead.org header.b="MG6ZlfcF" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728364AbgKZMFs (ORCPT ); Thu, 26 Nov 2020 07:05:48 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:43606 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726321AbgKZMFs (ORCPT ); Thu, 26 Nov 2020 07:05:48 -0500 Received: from merlin.infradead.org (merlin.infradead.org [IPv6:2001:8b0:10b:1231::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9C63EC0613D4 for ; Thu, 26 Nov 2020 04:05:47 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=infradead.org; s=merlin.20170209; h=Mime-Version:Content-Type:References: In-Reply-To:Date:Cc:To:From:Subject:Message-ID:Sender:Reply-To: Content-Transfer-Encoding:Content-ID:Content-Description; bh=0U9ehtY9XGUy3iyYnIyiQ5B+rTrgXoQsjFNpkMxJDNY=; b=MG6ZlfcF53BNOdLR8cydrJ8fAI 5xwyeTCovYXydSanISXabf5SISO22WOyioNFEuAmTiROY0/niRLqcEbQqCxFSXs8emqGTI1Nolx6Q KOKBZeDl/oeKj2yD0tQdo+09MHxlslJ0KeiuUtNzVF+ARpuV7PJIafa1ir0e4KEc0rWMFZv0ZEoEp 53/VnI4n9JRi9nurxlhC63o9L9GyNgdCFku/PEreVQu9HC8mu23tCZ6iM2vq23zo57/zolOlmniPj i5V7068lnNqQaJeneHxIzV8tg9N3r8hky2r0Batx7Lpar6UzYyag6kj96Aec/1KvYZc+W1Z2SZ9MP zlV9n6Ug==; Received: from 54-240-197-230.amazon.com ([54.240.197.230] helo=freeip.amazon.com) by merlin.infradead.org with esmtpsa (Exim 4.92.3 #3 (Red Hat Linux)) id 1kiG1r-00060V-QS; Thu, 26 Nov 2020 12:05:40 +0000 Message-ID: <6a8897917188a3a23710199f8da3f5f33670b80f.camel@infradead.org> Subject: [PATCH] kvm/x86: Fix simultaneous ExtINT and lapic interrupt handling with APICv From: David Woodhouse To: Sean Christopherson , Paolo Bonzini Cc: kvm , "Sironi, Filippo" , "Raslan, KarimAllah" , Matt Gingell , Steve Rutherford , liran@amazon.com Date: Thu, 26 Nov 2020 12:05:37 +0000 In-Reply-To: <99a9c1dfbb21744e5081d924291d3b09ab055813.camel@infradead.org> References: <62918f65ec78f8990278a6a0db0567968fa23e49.camel@infradead.org> <017de9019136b5d2ec34132b96b9f0273c21d6f1.camel@infradead.org> <20201125211955.GA450871@google.com> <99a9c1dfbb21744e5081d924291d3b09ab055813.camel@infradead.org> X-Mailer: Evolution 3.28.5-0ubuntu0.18.04.2 Mime-Version: 1.0 X-SRS-Rewrite: SMTP reverse-path rewritten from by merlin.infradead.org. See http://www.infradead.org/rpr.html Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: David Woodhouse Booting a guest with 'noapic' with split irqchip and APICv enabled leads to a livelock in vcpu_run(). When the userspace PIC has an IRQ to deliver, the VMM sets kvm_run->request_interrupt_window and vcpu_enter_guest() duly enables the corresponding VMexit, which happens immediately. However, if an interrupt also exists in the local APIC, that causes kvm_cpu_has_interrupt() to be true and thus causes kvm_vcpu_ready_for_interrupt_injection() to return false; the intent being that the kernel will use this interrupt window to inject its own interrupt from the LAPIC. So vcpu_run() doesn't exit all the way to userspace, and just loops. However, when APICv is in use there is no interrupt to be injected since that happens automatically. So the guest vCPU is launched again, exits again immediately, and the loop repeats ad infinitum without making any progress. It looks like this was introduced in commit 782d422bcaee, when dm_request_for_irq_injection() started returning true based purely on the fact that userspace had requested the interrupt window, without heed to kvm_cpu_has_interrupt() also being true. Fix it by allowing userspace to use the interrupt window with priority over the interrupt that is already in the LAPIC, for both APICv and legacy injection alike. Instead of '!kvm_cpu_has_interrupt()', the condition becomes '!(lapic_in_kernel(vcpu) && kvm_cpu_has_extint(vcpu))' Fixes: 782d422bcaee ("KVM: x86: split kvm_vcpu_ready_for_interrupt_injection out of dm_request_for_irq_injection") Signed-off-by: David Woodhouse Cc: stable@vger.kernel.org --- arch/x86/include/asm/kvm_host.h | 1 + arch/x86/kvm/irq.c | 2 +- arch/x86/kvm/x86.c | 6 ++++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index f002cdb13a0b..e85e2f1c661c 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1656,6 +1656,7 @@ int kvm_unmap_hva_range(struct kvm *kvm, unsigned long start, unsigned long end, int kvm_age_hva(struct kvm *kvm, unsigned long start, unsigned long end); int kvm_test_age_hva(struct kvm *kvm, unsigned long hva); int kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte); +int kvm_cpu_has_extint(struct kvm_vcpu *v); int kvm_cpu_has_injectable_intr(struct kvm_vcpu *v); int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu); int kvm_arch_interrupt_allowed(struct kvm_vcpu *vcpu); diff --git a/arch/x86/kvm/irq.c b/arch/x86/kvm/irq.c index 99d118ffc67d..9c4ef1682b81 100644 --- a/arch/x86/kvm/irq.c +++ b/arch/x86/kvm/irq.c @@ -40,7 +40,7 @@ static int pending_userspace_extint(struct kvm_vcpu *v) * check if there is pending interrupt from * non-APIC source without intack. */ -static int kvm_cpu_has_extint(struct kvm_vcpu *v) +int kvm_cpu_has_extint(struct kvm_vcpu *v) { u8 accept = kvm_apic_accept_pic_intr(v); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index a3fdc16cfd6f..b50ae8ba66e9 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -4080,12 +4080,14 @@ static int kvm_cpu_accept_dm_intr(struct kvm_vcpu *vcpu) * if userspace requested an interrupt window, check that the * interrupt window is open. * - * No need to exit to userspace if we already have an interrupt queued. + * If there is already an event which needs reinjection or a + * pending ExtINT, allow that to be processed by the kernel + * before letting userspace have the opportunity. */ static int kvm_vcpu_ready_for_interrupt_injection(struct kvm_vcpu *vcpu) { return kvm_arch_interrupt_allowed(vcpu) && - !kvm_cpu_has_interrupt(vcpu) && + !(lapic_in_kernel(vcpu) && kvm_cpu_has_extint(vcpu)) && !kvm_event_needs_reinjection(vcpu) && kvm_cpu_accept_dm_intr(vcpu); }