From patchwork Tue Aug 2 17:23:36 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Auger X-Patchwork-Id: 9259363 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 649076089F for ; Tue, 2 Aug 2016 17:26:04 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4F714283E0 for ; Tue, 2 Aug 2016 17:26:04 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 43A7328489; Tue, 2 Aug 2016 17:26:04 +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=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9F57A283E0 for ; Tue, 2 Aug 2016 17:26:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932195AbcHBRZq (ORCPT ); Tue, 2 Aug 2016 13:25:46 -0400 Received: from mx1.redhat.com ([209.132.183.28]:35434 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756873AbcHBRZd (ORCPT ); Tue, 2 Aug 2016 13:25:33 -0400 Received: from int-mx11.intmail.prod.int.phx2.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.24]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id E48D04E034; Tue, 2 Aug 2016 17:24:38 +0000 (UTC) Received: from localhost.redhat.com (vpn1-7-179.ams2.redhat.com [10.36.7.179]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id u72HNgIV017433; Tue, 2 Aug 2016 13:24:34 -0400 From: Eric Auger To: eric.auger@redhat.com, eric.auger.pro@gmail.com, christoffer.dall@linaro.org, marc.zyngier@arm.com, robin.murphy@arm.com, alex.williamson@redhat.com, will.deacon@arm.com, joro@8bytes.org, tglx@linutronix.de, jason@lakedaemon.net, linux-arm-kernel@lists.infradead.org Cc: kvm@vger.kernel.org, drjones@redhat.com, linux-kernel@vger.kernel.org, Bharat.Bhushan@freescale.com, pranav.sawargaonkar@gmail.com, p.fedin@samsung.com, iommu@lists.linux-foundation.org, Jean-Philippe.Brucker@arm.com, yehuday@marvell.com, Manish.Jaggi@caviumnetworks.com, robert.richter@caviumnetworks.com, dennis.chen@arm.com Subject: [PATCH v12 10/11] genirq/msi: Map/unmap the MSI doorbells on msi_domain_alloc/free_irqs Date: Tue, 2 Aug 2016 17:23:36 +0000 Message-Id: <1470158617-7022-11-git-send-email-eric.auger@redhat.com> In-Reply-To: <1470158617-7022-1-git-send-email-eric.auger@redhat.com> References: <1470158617-7022-1-git-send-email-eric.auger@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.24 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.38]); Tue, 02 Aug 2016 17:24:39 +0000 (UTC) Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This patch handles the iommu mapping of MSI doorbells that require to be mapped in an iommu domain. This happens on msi_domain_alloc/free_irqs since this is called in code that can sleep (pci_enable/disable_msi): iommu_map/unmap is not stated as atomic. On msi_domain_(de)activate and msi_domain_set_affinity, which must be atomic, we just lookup for this pre-allocated/mapped IOVA. If we detect the device sending MSIs is in front of an IOMMU that do not bypass MSIs but we can't find any doorbell to map we fail. This means we currently do not support MSI controllers inbetween the device and the IOMMU. In the future, those controllers, typically integrated into the PCI host controller may also register a doorbell declared as not mappable. Signed-off-by: Eric Auger --- v11 -> v12: - introduce intermediate helpers: msi_get_doorbell_info, msi_map_global_doorbell, msi_map_percpu_doorbell - add kernel-doc comments - remove desc->irq reset and cleanup in case of failure and set MSI_DESC_FLAG_FUNCTION instead - add comments v10 -> v11: - restore v9 version based on irq_chip msi_doorbell_info v9 -> v10: - use irqchip API to lookup for the chip_data's doorbell v8 -> v9: - decouple irq_data parsing from the actual mapping/unmapping v7 -> v8: - new percpu pointer type - exit from the irq domain hierarchy parsing on first map/unmap success - reset desc->irq to 0 on mapping failure v7: creation --- kernel/irq/msi.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 148 insertions(+), 6 deletions(-) diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 9b93766..6a3cb14 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -14,6 +14,9 @@ #include #include #include +#include +#include +#include /* Temparory solution for building, will be removed later */ #include @@ -322,6 +325,131 @@ int msi_domain_populate_irqs(struct irq_domain *domain, struct device *dev, } /** + * msi_get_doorbell_info - return the MSI doorbell descriptor corresponding + * to an irq data + * @data: irq data handle + * + * Return: the doorbell descriptor pointer if any, NULL if none, an ERR_PTR + * otherwise + */ +static struct msi_doorbell_info *msi_get_doorbell_info(struct irq_data *data) +{ + struct irq_chip *chip; + + while (data) { + chip = irq_data_get_irq_chip(data); + if (chip->irq_get_msi_doorbell_info) + break; + data = data->parent_data; + } + + if (!data) + return NULL; + + return chip->irq_get_msi_doorbell_info(data); +} + +/** + * msi_map_global_doorbell - iommu map/unmap the global doorbell physical + * address + * @domain: iommu domain the mapping is associated to + * @dbi: doorbell descriptor + * @map: true if map operation, false if unmap operation + * + * Return: 0 on success or an error code + */ +static int msi_map_global_doorbell(struct iommu_domain *domain, + const struct msi_doorbell_info *dbi, bool map) +{ + dma_addr_t iova; + int ret = 0; + + if (map) + ret = iommu_msi_get_doorbell_iova(domain, dbi->global_doorbell, + dbi->size, dbi->prot, &iova); + else + iommu_msi_put_doorbell_iova(domain, dbi->global_doorbell); + return ret; +} + +/** + * msi_map_percpu_doorbell - iommu map/unmap the percpu doorbell physical + * addresses + * @domain: iommu domain the mapping is associated to + * @dbi: doorbell descriptor + * @map: true if map operation, false if unmap operation + * + * Return: 0 on success or an error code + */ +static int msi_map_percpu_doorbell(struct iommu_domain *domain, + const struct msi_doorbell_info *dbi, bool map) +{ + int cpu, ret; + + for_each_possible_cpu(cpu) { + phys_addr_t __percpu *db_addr; + dma_addr_t iova; + + db_addr = per_cpu_ptr(dbi->percpu_doorbells, cpu); + + if (map) { + ret = iommu_msi_get_doorbell_iova(domain, *db_addr, + dbi->size, dbi->prot, + &iova); + if (ret) + return ret; + } else { + iommu_msi_put_doorbell_iova(domain, *db_addr); + } + } + return 0; +} + +/** + * msi_handle_doorbell_mappings - IOMMU map/unmap any MSI doorbell associated + * to the irq data handle + * @data: irq data handle + * @map: true if map operation, false if unmap operation + * + * In case the irq data corresponds to an MSI sent by a device in front of + * an IOMMU and this latter does not bypass MSI transactions, + * traverse the irq domain hierarchy to retrieve the MSI doorbells and + * iommu_map/unmap them according to @map boolean. + * + * Return 0 on success or if no action is required, or an error code + */ +static int msi_handle_doorbell_mappings(struct irq_data *data, bool map) +{ + const struct msi_doorbell_info *dbi; + struct iommu_domain *domain; + struct device *dev; + + /* Is the MSI address translated by an IOMMU? */ + dev = msi_desc_to_dev(irq_data_get_msi_desc(data)); + domain = iommu_msi_domain(dev); + if (!domain) + return 0; + + /** + * Do we find a doorbell to IOMMU map? + * If we don't either the doorbell registration failed, or + * the actual MSI controller did not register its doorbell: + * either the MSI controller is behind the IOMMU and the MSI + * controller should have registered its doorbell; or the MSI + * controller is inbetween the device and the IOMMU. We currently + * do not support this case. + */ + dbi = msi_get_doorbell_info(data); + if (!dbi) + return -ENODEV; + + if (dbi->doorbell_is_percpu) + return msi_map_percpu_doorbell(domain, dbi, map); + else + return msi_map_global_doorbell(domain, dbi, map); +} + +/** * msi_domain_alloc_irqs - Allocate interrupts from a MSI interrupt domain * @domain: The domain to allocate from * @dev: Pointer to device struct of the device for which the interrupts @@ -354,18 +482,23 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, dev_to_node(dev), &arg, false); if (virq < 0) { ret = -ENOSPC; - if (ops->handle_error) - ret = ops->handle_error(domain, desc, ret); - if (ops->msi_finish) - ops->msi_finish(&arg, ret); - return ret; + goto error; } desc->flags |= MSI_DESC_FLAG_ALLOCATED; - desc->flags |= MSI_DESC_FLAG_FUNCTIONAL; for (i = 0; i < desc->nvec_used; i++) irq_set_msi_desc_off(virq, i, desc); + + for (i = 0; i < desc->nvec_used; i++) { + struct irq_data *d = irq_get_irq_data(virq + i); + + ret = msi_handle_doorbell_mappings(d, true); + if (ret) + goto error; + } + + desc->flags |= MSI_DESC_FLAG_FUNCTIONAL; } if (ops->msi_finish) @@ -380,6 +513,12 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, } return 0; +error: + if (ops->handle_error) + ret = ops->handle_error(domain, desc, ret); + if (ops->msi_finish) + ops->msi_finish(&arg, ret); + return ret; } /** @@ -399,6 +538,9 @@ void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev) * entry. If that's the case, don't do anything. */ if (desc->flags & MSI_DESC_FLAG_ALLOCATED) { + struct irq_data *d = irq_get_irq_data(desc->irq); + + msi_handle_doorbell_mappings(d, false); irq_domain_free_irqs(desc->irq, desc->nvec_used); desc->irq = 0; desc->flags &= ~MSI_DESC_FLAG_ALLOCATED;