From patchwork Fri Sep 15 22:19:54 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christoffer Dall X-Patchwork-Id: 9953889 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id C93D9601C0 for ; Fri, 15 Sep 2017 22:24:16 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B344629580 for ; Fri, 15 Sep 2017 22:24:16 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id A812B29584; Fri, 15 Sep 2017 22:24:16 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.2 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,RCVD_IN_DNSWL_MED autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [65.50.211.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id EBED229581 for ; Fri, 15 Sep 2017 22:24:15 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=38ReLXXe6+hDgGlEsINJoLGJCPJKgFypJ3illk3MbUg=; b=DCmZloVhV+4pbj/0Zy/pU52IBM 3IDETiPBbMTwjEml4FllLFInuQ+SLK+XZdQRWjWK/gjizGfY0zJuEPpRyP03lCjcf3P75wACcVm0x OITam1obuQpdV5pxMhR/QQdFQqgyCzKFgTyz9BJfMiEzzDqf5NbL9dwiZIPjpRXPZJ2wM+uBzJPEb oYwqcx9Rxi4AbZmuLMCm75ytABi0oZW6r8fH8KZYr6oZ/ZwXivxZlr2SnK3pgfHwITNvgqgqrnKXU Vj2VJHuhEcYtADO/MNineKFh//W1vNjAQapYsiPgeKUrZN3wg+uG43PEOr8ny4g00F2skLLY2YyCP qcf9fH5w==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.87 #1 (Red Hat Linux)) id 1dsz1Z-00068V-VV; Fri, 15 Sep 2017 22:23:50 +0000 Received: from mail-ua0-x233.google.com ([2607:f8b0:400c:c08::233]) by bombadil.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1dsyyR-00036B-M6 for linux-arm-kernel@lists.infradead.org; Fri, 15 Sep 2017 22:20:49 +0000 Received: by mail-ua0-x233.google.com with SMTP id l24so1929745uaa.5 for ; Fri, 15 Sep 2017 15:20:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=nt3231oHDUiI4bIyGHfC0e7oOs2YN5Liio3iwycpV5Q=; b=VfuPguk+qHdcm3n+u7hnPPTYkJLziICPYYhL9/XVTWdtdqljmoc7oj4uj1AzHIIzyK 5Sh9JAd7urSkvVYGVoxx7jXTOepJpOmljDEgKyab46WKcx4S3B1Qu9OHZo7reWPkGrS2 3+2Nz26blZs9iiew91dQMxUfL5abmMEWs5pyc= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=nt3231oHDUiI4bIyGHfC0e7oOs2YN5Liio3iwycpV5Q=; b=ToEevTAdF6qqI1n3b5E5IoW+SfO9LIjHXW65wxRX5KVmsEh5VUvIbggaG55zxsepwd teQ5Z/1AbvECg7L+8kOWDBJMkiC9mZAbUrtyNHlnPzVLyEX2+4hdlubugf6GFNZDjTZh m3IXuxFTteU1nlDT7oX6yo+hrPT7hB8r1rudk9Wl3iTPUBexLiGkIXjcSSl2FKrNS8M/ Kndqekiv2xQVQE6wYhBu9ZaOMm39ceErugZBVimmn1cIEjMr19/tc87RnOzJWF5717NU Sbnqgw/3WDCltPOdFP8X7+1IKFC6aQ/0ptO9XLcpeApRMqxscM3Z5OGNRwCPALHLc4PO 532g== X-Gm-Message-State: AHPjjUgLuzhN+6JuuG+Zxhyr50r6YYctbOIRfs8GPtOUety7QdMcZyS6 GlsuqZid0oBdCHaAv9O/SA== X-Google-Smtp-Source: AOwi7QCeq1tCz1pUz3mu71osyItP/WnjJ6PK0X8i5RUpBPP3Q+dAf4zC35sUfMAgqWXiGajAd6ExYA== X-Received: by 10.176.2.87 with SMTP id 81mr22552145uas.162.1505514014213; Fri, 15 Sep 2017 15:20:14 -0700 (PDT) Received: from localhost.localdomain ([12.145.98.253]) by smtp.gmail.com with ESMTPSA id q64sm399435uaq.13.2017.09.15.15.20.12 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 15 Sep 2017 15:20:13 -0700 (PDT) From: Christoffer Dall To: kvmarm@lists.cs.columbia.edu, linux-arm-kernel@lists.infradead.org Subject: [PATCH v4 7/7] KVM: arm/arm64: Support VGIC dist pend/active changes for mapped IRQs Date: Fri, 15 Sep 2017 15:19:54 -0700 Message-Id: <1505513994-77939-8-git-send-email-christoffer.dall@linaro.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1505513994-77939-1-git-send-email-christoffer.dall@linaro.org> References: <1505513994-77939-1-git-send-email-christoffer.dall@linaro.org> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20170915_152036_217453_54DDFC9E X-CRM114-Status: GOOD ( 23.39 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Marc Zyngier , Eric Auger , kvm@vger.kernel.org, Christoffer Dall MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP From: Christoffer Dall For mapped IRQs (with the HW bit set in the LR) we have to follow some rules of the architecture. One of these rules is that VM must not be allowed to deactivate a virtual interrupt with the HW bit set unless the physical interrupt is also active. This works fine when injecting mapped interrupts, because we leave it up to the injector to either set EOImode==1 or manually set the active state of the physical interrupt. However, the guest can set virtual interrupt to be pending or active by writing to the virtual distributor, which could lead to deactivating a virtual interrupt with the HW bit set without the physical interrupt being active. We could set the physical interrupt to active whenever we are about to enter the VM with a HW interrupt either pending or active, but that would be really slow, especially on GICv2. So we take the long way around and do the hard work when needed, which is expected to be extremely rare. When the VM sets the pending state for a HW interrupt on the virtual distributor we set the active state on the physical distributor, because the virtual interrupt can become active and then the guest can deactivate it. When the VM clears the pending state we also clear it on the physical side, because the injector might otherwise raise the interrupt. We also clear the physical active state when the virtual interrupt is not active, since otherwise a SPEND/CPEND sequence from the guest would prevent signaling of future interrupts. Changing the state of mapped interrupts from userspace is not supported, and it's expected that userspace unmaps devices from VFIO before attempting to set the interrupt state, because the interrupt state is driven by hardware. One annoying exception with this is the arch timer, which is mapped directly from the kernel without userspace knowing when that happens. Since we already support saving/restoring the timer IRQ state while it is mapped, and we rely on the timer code to set the physical active state on VCPU entry, we have to preserve this behavior and specifically check if the interrupt is for the timer. Oh well. Signed-off-by: Christoffer Dall Reviewed-by: Marc Zyngier --- virt/kvm/arm/vgic/vgic-mmio.c | 79 ++++++++++++++++++++++++++++++++++++++----- virt/kvm/arm/vgic/vgic.c | 7 ++++ virt/kvm/arm/vgic/vgic.h | 1 + 3 files changed, 79 insertions(+), 8 deletions(-) diff --git a/virt/kvm/arm/vgic/vgic-mmio.c b/virt/kvm/arm/vgic/vgic-mmio.c index f3087f6..3a8c7ae 100644 --- a/virt/kvm/arm/vgic/vgic-mmio.c +++ b/virt/kvm/arm/vgic/vgic-mmio.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include "vgic.h" @@ -140,10 +141,24 @@ static struct kvm_vcpu *vgic_get_mmio_requester_vcpu(void) return vcpu; } +/* Must be called with irq->irq_lock held */ +static void vgic_hw_irq_spending(struct kvm_vcpu *vcpu, struct vgic_irq *irq, + bool is_uaccess) +{ + bool is_timer = irq->get_input_level == kvm_arch_timer_get_input_level; + + if (!is_uaccess || is_timer) + irq->pending_latch = true; + + if (!is_uaccess) + vgic_irq_set_phys_active(irq, true); +} + void vgic_mmio_write_spending(struct kvm_vcpu *vcpu, gpa_t addr, unsigned int len, unsigned long val) { + bool is_uaccess = !vgic_get_mmio_requester_vcpu(); u32 intid = VGIC_ADDR_TO_INTID(addr, 1); int i; @@ -151,17 +166,47 @@ void vgic_mmio_write_spending(struct kvm_vcpu *vcpu, struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); spin_lock(&irq->irq_lock); - irq->pending_latch = true; - + if (irq->hw) + vgic_hw_irq_spending(vcpu, irq, is_uaccess); + else + irq->pending_latch = true; vgic_queue_irq_unlock(vcpu->kvm, irq); vgic_put_irq(vcpu->kvm, irq); } } +/* Must be called with irq->irq_lock held */ +static void vgic_hw_irq_cpending(struct kvm_vcpu *vcpu, struct vgic_irq *irq, + bool is_uaccess) +{ + bool is_timer = irq->get_input_level == kvm_arch_timer_get_input_level; + + if (!is_uaccess || is_timer) + irq->pending_latch = false;; + + if (!is_uaccess) { + /* + * We don't want the guest to effectively mask the physical + * interrupt by doing a write to SPENDR followed by a write to + * CPENDR for HW interrupts, so we clear the active state on + * the physical side if the virtual interrupt is not active. + * This may lead to taking an additional interrupt on the + * host, but that should not be a problem as the worst that + * can happen is an additional vgic injection. We also clear + * the pending state to maintain proper semantics for edge HW + * interrupts. + */ + vgic_irq_set_phys_pending(irq, false); + if (!irq->active) + vgic_irq_set_phys_active(irq, false); + } +} + void vgic_mmio_write_cpending(struct kvm_vcpu *vcpu, gpa_t addr, unsigned int len, unsigned long val) { + bool is_uaccess = !vgic_get_mmio_requester_vcpu(); u32 intid = VGIC_ADDR_TO_INTID(addr, 1); int i; @@ -169,9 +214,10 @@ void vgic_mmio_write_cpending(struct kvm_vcpu *vcpu, struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); spin_lock(&irq->irq_lock); - - irq->pending_latch = false; - + if (irq->hw) + vgic_hw_irq_cpending(vcpu, irq, is_uaccess); + else + irq->pending_latch = false; spin_unlock(&irq->irq_lock); vgic_put_irq(vcpu->kvm, irq); } @@ -197,8 +243,21 @@ unsigned long vgic_mmio_read_active(struct kvm_vcpu *vcpu, return value; } +/* Must be called with irq->irq_lock held */ +static void vgic_hw_irq_change_active(struct kvm_vcpu *vcpu, struct vgic_irq *irq, + bool active, bool is_uaccess) +{ + bool is_timer = irq->get_input_level == kvm_arch_timer_get_input_level; + + if (!is_uaccess || is_timer) + irq->active = active;; + + if (!is_uaccess) + vgic_irq_set_phys_active(irq, active); +} + static void vgic_mmio_change_active(struct kvm_vcpu *vcpu, struct vgic_irq *irq, - bool new_active_state) + bool active) { struct kvm_vcpu *requester_vcpu = vgic_get_mmio_requester_vcpu(); @@ -225,8 +284,12 @@ static void vgic_mmio_change_active(struct kvm_vcpu *vcpu, struct vgic_irq *irq, irq->vcpu->cpu != -1) /* VCPU thread is running */ cond_resched_lock(&irq->irq_lock); - irq->active = new_active_state; - if (new_active_state) + if (irq->hw) + vgic_hw_irq_change_active(vcpu, irq, active, !requester_vcpu); + else + irq->active = active; + + if (irq->active) vgic_queue_irq_unlock(vcpu->kvm, irq); else spin_unlock(&irq->irq_lock); diff --git a/virt/kvm/arm/vgic/vgic.c b/virt/kvm/arm/vgic/vgic.c index af0de3d..0ca2b3d 100644 --- a/virt/kvm/arm/vgic/vgic.c +++ b/virt/kvm/arm/vgic/vgic.c @@ -140,6 +140,13 @@ void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq) kfree(irq); } +void vgic_irq_set_phys_pending(struct vgic_irq *irq, bool pending) +{ + WARN_ON(irq_set_irqchip_state(irq->host_irq, + IRQCHIP_STATE_PENDING, + pending)); +} + /* Get the input level of a mapped IRQ directly from the physical GIC */ bool vgic_get_phys_line_level(struct vgic_irq *irq) { diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h index 7bdcda2..498ee05 100644 --- a/virt/kvm/arm/vgic/vgic.h +++ b/virt/kvm/arm/vgic/vgic.h @@ -146,6 +146,7 @@ struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu, u32 intid); void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq); bool vgic_get_phys_line_level(struct vgic_irq *irq); +void vgic_irq_set_phys_pending(struct vgic_irq *irq, bool pending); void vgic_irq_set_phys_active(struct vgic_irq *irq, bool active); bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq); void vgic_kick_vcpus(struct kvm *kvm);