From patchwork Fri Feb 9 14:39:10 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andre Przywara X-Patchwork-Id: 127837 Delivered-To: patch@linaro.org Received: by 10.46.124.24 with SMTP id x24csp679787ljc; Fri, 9 Feb 2018 06:42:08 -0800 (PST) X-Google-Smtp-Source: AH8x225HmkjY5CiGGUMVIbiHiI8ZvhHSwcyjlCPJUwGyu4NMbQoPv5pIHF4IVYs0QKOYPGKJmSvv X-Received: by 10.107.20.194 with SMTP id 185mr3183990iou.127.1518187328476; Fri, 09 Feb 2018 06:42:08 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1518187328; cv=none; d=google.com; s=arc-20160816; b=zhK7kh/aMdoU/k9L4GQmKGVyLER4nPHbqjbYHEJDDg/YPMr8+VJDJAaUZBdEMwiLjg J5MFJN0l+e+U3cvk46sXLr2Jw7DxameZZfTDgv1meS6c70nNoBdancm9h2CrAD4FtxNd Ctd2Ri0sVckK0K6PJG5TauIq4oNePF2MydozeitJmwGhoFdPIxS7SEVYchZxhL7ZmqyN BFIukt4fWyzKgH5J1ZLYmMwS1ed4QQUyzizwHTDC8i4Wf2bF1o61e9V57rwQNAvkq/3u fLoasplCT6ATBLdCjKXYtpq+RTV/Lh+4RGiGYtTy4phEZjVgN69TBi5hufTb/WMQh4pT OyJw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version :list-subscribe:list-help:list-post:list-unsubscribe:list-id :precedence:subject:references:in-reply-to:message-id:date:to:from :dkim-signature:arc-authentication-results; bh=te2o17WRGuHjSLRFTpyXSz+6v7t3hI5FTeLLb5M2wCQ=; b=vUNkdqOme/ZdNx//kJQ303xGlulhq/we6eT3QhmAByzgGjTzbxWNajYB9ndVN6xTZ1 T4m0n6iW4ISLN5AvKsrCpNpJKROfA38D68XKKZQ5opzXhcLSra1fnkd+Kh8Jqjy9WoyC TpUR3+hZ8sZ7gSXkTzq8Pov04p4024vOmcPNxZVlurMdlMzlCvA1vAF7MqdwFI8273zP gmFfmlTgqpPQfh/PcnhZH+DIpoOIznuN4H91kHO8Wuj3NyIsVoKlL0IPtiCSi26Bre+G uShk0tmPxChgahsMSgu7Pb8Yx/6TmMAGJLEBD4vBiVqros8W+ecYXNIWo2vcb6QYIR8E Pxjg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org header.s=google header.b=FyUEoNVG; spf=pass (google.com: best guess record for domain of xen-devel-bounces@lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from lists.xenproject.org (lists.xenproject.org. [192.237.175.120]) by mx.google.com with ESMTPS id f75si1650166itc.100.2018.02.09.06.42.08 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 09 Feb 2018 06:42:08 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of xen-devel-bounces@lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org header.s=google header.b=FyUEoNVG; spf=pass (google.com: best guess record for domain of xen-devel-bounces@lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ek9qV-0000ge-NJ; Fri, 09 Feb 2018 14:40:11 +0000 Received: from us1-rack-dfw2.inumbo.com ([104.130.134.6]) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ek9qU-0000db-Lw for xen-devel@lists.xenproject.org; Fri, 09 Feb 2018 14:40:10 +0000 X-Inumbo-ID: 0f80d26e-0da7-11e8-ba59-bc764e045a96 Received: from mail-wr0-x22d.google.com (unknown [2a00:1450:400c:c0c::22d]) by us1-rack-dfw2.inumbo.com (Halon) with ESMTPS id 0f80d26e-0da7-11e8-ba59-bc764e045a96; Fri, 09 Feb 2018 15:39:41 +0100 (CET) Received: by mail-wr0-x22d.google.com with SMTP id y3so8471357wrh.3 for ; Fri, 09 Feb 2018 06:40:09 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:subject:date:message-id:in-reply-to:references; bh=oCeaR/X0LHg2SKPtqUCMvA4+NnZgcaGLG6kIMZBHAzM=; b=FyUEoNVGHKmaIeP1K56U/ROqlh64q6xDp/VuaC0OJEiMfNYPq5Pdv7pjBkx9bbQ84b Y3gLTZR8ValNPBRLfQPd4ITH4YmLIBR+uDS1qPI07GmN4HB+o7duNUdkuE0su6XzDaYk kFPEQ40NV5g7xvyC4kPUsd+vzHdzqTN9LkLkE= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=oCeaR/X0LHg2SKPtqUCMvA4+NnZgcaGLG6kIMZBHAzM=; b=GQB2Xw3OyZD2aip0rNA4Uxd1ud2kGyVCxq9FEiGWK5idgQGq/hJ21TxIhmy96hJVhY 2FUXM9PnFAFpgUcMjeX6XyMQ6Ex9LEk6bpowf5tmZ3OjOHiug/lcBgFbhSA269DRuLra vLWU54z3T9X1Rj9aqfIw82l9nJSYCLoARYwZ1zNGBMPOJ7F09bZ7XFZKTIpK9QSD+lBM HWBfYhgAH8jT9r298HjcCXUQ6H9Y0/4v1oXkVZQNBxKu5MYZvayV/CkwsufzdSkiPUYw mcAN89xaMpMAXQkQJgZmHf1wVx7mSRnz8r6GCZrkUIQIjv0cGHFDLclXmRdDzHt7Dpc9 hLXg== X-Gm-Message-State: APf1xPC2EX1wpBJzN83frLfLU2aoFQNb1IvqXTfd5QmyHIAJlgdIetlW BG/WDzpbmT6whVkZK9tZ2Vu8fNRLqBc= X-Received: by 10.223.199.207 with SMTP id y15mr2826670wrg.158.1518187208216; Fri, 09 Feb 2018 06:40:08 -0800 (PST) Received: from e104803-lin.lan (mail.andrep.de. [217.160.17.100]) by smtp.gmail.com with ESMTPSA id b35sm2552229wra.13.2018.02.09.06.40.07 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 09 Feb 2018 06:40:07 -0800 (PST) From: Andre Przywara To: Stefano Stabellini , Julien Grall , xen-devel@lists.xenproject.org Date: Fri, 9 Feb 2018 14:39:10 +0000 Message-Id: <20180209143937.28866-23-andre.przywara@linaro.org> X-Mailer: git-send-email 2.14.1 In-Reply-To: <20180209143937.28866-1-andre.przywara@linaro.org> References: <20180209143937.28866-1-andre.przywara@linaro.org> Subject: [Xen-devel] [RFC PATCH 22/49] ARM: new VGIC: Implement virtual IRQ injection X-BeenThere: xen-devel@lists.xenproject.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: xen-devel-bounces@lists.xenproject.org Sender: "Xen-devel" Provide a vgic_queue_irq_unlock() function which decides whether a given IRQ needs to be queued to a VCPU's ap_list. This should be called whenever an IRQ becomes pending or enabled, either as a result of a hardware IRQ injection, from devices emulated by Xen (like the architected timer) or from MMIO accesses to the distributor emulation. Also provides the necessary functions to allow to inject an IRQ to a guest. Since this is the first code that starts using our locking mechanism, we add some (hopefully) clear documentation of our locking strategy and requirements along with this patch. This is based on Linux commit 81eeb95ddbab, written by Christoffer Dall. Signed-off-by: Andre Przywara --- xen/arch/arm/vgic/vgic.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++ xen/arch/arm/vgic/vgic.h | 10 +++ 2 files changed, 234 insertions(+) diff --git a/xen/arch/arm/vgic/vgic.c b/xen/arch/arm/vgic/vgic.c index 3075091caa..f517df6d00 100644 --- a/xen/arch/arm/vgic/vgic.c +++ b/xen/arch/arm/vgic/vgic.c @@ -21,6 +21,32 @@ #include #include "vgic.h" +/* + * Locking order is always: + * kvm->lock (mutex) + * its->cmd_lock (mutex) + * its->its_lock (mutex) + * vgic_cpu->ap_list_lock + * kvm->lpi_list_lock + * vgic_irq->irq_lock + * + * If you need to take multiple locks, always take the upper lock first, + * then the lower ones, e.g. first take the its_lock, then the irq_lock. + * If you are already holding a lock and need to take a higher one, you + * have to drop the lower ranking lock first and re-aquire it after having + * taken the upper one. + * + * When taking more than one ap_list_lock at the same time, always take the + * lowest numbered VCPU's ap_list_lock first, so: + * vcpuX->vcpu_id < vcpuY->vcpu_id: + * spin_lock(vcpuX->arch.vgic_cpu.ap_list_lock); + * spin_lock(vcpuY->arch.vgic_cpu.ap_list_lock); + * + * Since the VGIC must support injecting virtual interrupts from ISRs, we have + * to use the spin_lock_irqsave/spin_unlock_irqrestore versions of outer + * spinlocks for any lock that may be taken while injecting an interrupt. + */ + /* * Iterate over the VM's list of mapped LPIs to find the one with a * matching interrupt ID and return a reference to the IRQ structure. @@ -97,6 +123,204 @@ void vgic_put_irq(struct domain *d, struct vgic_irq *irq) xfree(irq); } +/** + * vgic_target_oracle - compute the target vcpu for an irq + * + * @irq: The irq to route. Must be already locked. + * + * Based on the current state of the interrupt (enabled, pending, + * active, vcpu and target_vcpu), compute the next vcpu this should be + * given to. Return NULL if this shouldn't be injected at all. + * + * Requires the IRQ lock to be held. + */ +static struct vcpu *vgic_target_oracle(struct vgic_irq *irq) +{ + ASSERT(spin_is_locked(&irq->irq_lock)); + + /* If the interrupt is active, it must stay on the current vcpu */ + if ( irq->active ) + return irq->vcpu ? : irq->target_vcpu; + + /* + * If the IRQ is not active but enabled and pending, we should direct + * it to its configured target VCPU. + * If the distributor is disabled, pending interrupts shouldn't be + * forwarded. + */ + if ( irq->enabled && irq_is_pending(irq) ) + { + if ( unlikely(irq->target_vcpu && + !irq->target_vcpu->domain->arch.vgic.enabled) ) + return NULL; + + return irq->target_vcpu; + } + + /* If neither active nor pending and enabled, then this IRQ should not + * be queued to any VCPU. + */ + return NULL; +} + +/* + * Only valid injection if changing level for level-triggered IRQs or for a + * rising edge. + */ +static bool vgic_validate_injection(struct vgic_irq *irq, bool level) +{ + switch (irq->config) + { + case VGIC_CONFIG_LEVEL: + return irq->line_level != level; + case VGIC_CONFIG_EDGE: + return level; + } + + return false; +} + +/* + * Check whether an IRQ needs to (and can) be queued to a VCPU's ap list. + * Do the queuing if necessary, taking the right locks in the right order. + * Returns true when the IRQ was queued, false otherwise. + * + * Needs to be entered with the IRQ lock already held, but will return + * with all locks dropped. + */ +bool vgic_queue_irq_unlock(struct domain *d, struct vgic_irq *irq, + unsigned long flags) +{ + struct vcpu *vcpu; + bool running; + + ASSERT(spin_is_locked(&irq->irq_lock)); + +retry: + vcpu = vgic_target_oracle(irq); + if ( irq->vcpu || !vcpu ) + { + /* + * If this IRQ is already on a VCPU's ap_list, then it + * cannot be moved or modified and there is no more work for + * us to do. + * + * Otherwise, if the irq is not pending and enabled, it does + * not need to be inserted into an ap_list and there is also + * no more work for us to do. + */ + spin_unlock_irqrestore(&irq->irq_lock, flags); + + /* + * We have to kick the VCPU here, because we could be + * queueing an edge-triggered interrupt for which we + * get no EOI maintenance interrupt. In that case, + * while the IRQ is already on the VCPU's AP list, the + * VCPU could have EOI'ed the original interrupt and + * won't see this one until it exits for some other + * reason. + */ + if ( vcpu ) + vcpu_unblock(vcpu); + return false; + } + + /* + * We must unlock the irq lock to take the ap_list_lock where + * we are going to insert this new pending interrupt. + */ + spin_unlock_irqrestore(&irq->irq_lock, flags); + + /* someone can do stuff here, which we re-check below */ + + spin_lock_irqsave(&vcpu->arch.vgic_cpu.ap_list_lock, flags); + spin_lock(&irq->irq_lock); + + /* + * Did something change behind our backs? + * + * There are two cases: + * 1) The irq lost its pending state or was disabled behind our + * backs and/or it was queued to another VCPU's ap_list. + * 2) Someone changed the affinity on this irq behind our + * backs and we are now holding the wrong ap_list_lock. + * + * In both cases, drop the locks and retry. + */ + + if ( unlikely(irq->vcpu || vcpu != vgic_target_oracle(irq)) ) + { + spin_unlock(&irq->irq_lock); + spin_unlock_irqrestore(&vcpu->arch.vgic_cpu.ap_list_lock, flags); + + spin_lock_irqsave(&irq->irq_lock, flags); + goto retry; + } + + /* + * Grab a reference to the irq to reflect the fact that it is + * now in the ap_list. + */ + vgic_get_irq_kref(irq); + list_add_tail(&irq->ap_list, &vcpu->arch.vgic_cpu.ap_list_head); + irq->vcpu = vcpu; + + spin_unlock(&irq->irq_lock); + spin_unlock_irqrestore(&vcpu->arch.vgic_cpu.ap_list_lock, flags); + + running = vcpu->is_running; + vcpu_unblock(vcpu); + if ( running && vcpu != current ) + smp_send_event_check_mask(cpumask_of(vcpu->processor)); + + return true; +} + +/** + * vgic_inject_irq - Inject an IRQ from a device to the vgic + * @d: The domain pointer + * @vcpu: The vCPU for PPIs + * @intid: The INTID to inject a new state to. + * @level: Edge-triggered: true: to trigger the interrupt + * false: to ignore the call + * Level-sensitive true: raise the input signal + * false: lower the input signal + * + * The VGIC is not concerned with devices being active-LOW or active-HIGH for + * level-sensitive interrupts. You can think of the level parameter as 1 + * being HIGH and 0 being LOW and all devices being active-HIGH. + */ +int vgic_inject_irq(struct domain *d, struct vcpu *vcpu, unsigned int intid, + bool level) +{ + struct vgic_irq *irq; + unsigned long flags; + + irq = vgic_get_irq(d, vcpu, intid); + if ( !irq ) + return -EINVAL; + + spin_lock_irqsave(&irq->irq_lock, flags); + + if ( !vgic_validate_injection(irq, level) ) + { + /* Nothing to see here, move along... */ + spin_unlock_irqrestore(&irq->irq_lock, flags); + vgic_put_irq(d, irq); + return 0; + } + + if ( irq->config == VGIC_CONFIG_LEVEL ) + irq->line_level = level; + else + irq->pending_latch = true; + + vgic_queue_irq_unlock(d, irq, flags); + vgic_put_irq(d, irq); + + return 0; +} + /* * Local variables: * mode: C diff --git a/xen/arch/arm/vgic/vgic.h b/xen/arch/arm/vgic/vgic.h index 7a15cfdd79..5127739f0f 100644 --- a/xen/arch/arm/vgic/vgic.h +++ b/xen/arch/arm/vgic/vgic.h @@ -17,9 +17,19 @@ #ifndef __XEN_ARM_VGIC_NEW_H__ #define __XEN_ARM_VGIC_NEW_H__ +static inline bool irq_is_pending(struct vgic_irq *irq) +{ + if ( irq->config == VGIC_CONFIG_EDGE ) + return irq->pending_latch; + else + return irq->pending_latch || irq->line_level; +} + struct vgic_irq *vgic_get_irq(struct domain *d, struct vcpu *vcpu, u32 intid); void vgic_put_irq(struct domain *d, struct vgic_irq *irq); +bool vgic_queue_irq_unlock(struct domain *d, struct vgic_irq *irq, + unsigned long flags); static inline void vgic_get_irq_kref(struct vgic_irq *irq) {