From patchwork Thu Apr 30 13:15:19 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Max Gurtovoy X-Patchwork-Id: 1280414 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [203.11.71.2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 49Cbkz1R5wz9sSd for ; Thu, 30 Apr 2020 23:26:51 +1000 (AEST) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=mellanox.com Received: from bilbo.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 49Cbkz07YgzDrGx for ; Thu, 30 Apr 2020 23:26:51 +1000 (AEST) X-Original-To: linuxppc-dev@lists.ozlabs.org Delivered-To: linuxppc-dev@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=mellanox.com (client-ip=193.47.165.129; helo=mellanox.co.il; envelope-from=maxg@mellanox.com; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=pass (p=none dis=none) header.from=mellanox.com Received: from mellanox.co.il (mail-il-dmz.mellanox.com [193.47.165.129]) by lists.ozlabs.org (Postfix) with ESMTP id 49CbdT6tZ0zDrFb for ; Thu, 30 Apr 2020 23:22:04 +1000 (AEST) Received: from Internal Mail-Server by MTLPINE2 (envelope-from maxg@mellanox.com) with ESMTPS (AES256-SHA encrypted); 30 Apr 2020 16:15:21 +0300 Received: from mtr-vdi-031.wap.labs.mlnx. (mtr-vdi-031.wap.labs.mlnx [10.209.102.136]) by labmailer.mlnx (8.13.8/8.13.8) with ESMTP id 03UDFKRV027016; Thu, 30 Apr 2020 16:15:20 +0300 From: Max Gurtovoy To: hch@lst.de, linux-pci@vger.kernel.org, oohall@gmail.com, linuxppc-dev@lists.ozlabs.org Subject: [PATCH 1/2 v2] powerpc/dma: Define map/unmap mmio resource callbacks Date: Thu, 30 Apr 2020 16:15:19 +0300 Message-Id: <20200430131520.51211-1-maxg@mellanox.com> X-Mailer: git-send-email 2.21.0 MIME-Version: 1.0 X-BeenThere: linuxppc-dev@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Linux on PowerPC Developers Mail List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: vladimirk@mellanox.com, clsoto@us.ibm.com, israelr@mellanox.com, shlomin@mellanox.com, fbarrat@linux.ibm.com, Max Gurtovoy , idanw@mellanox.com, aneela@mellanox.com Errors-To: linuxppc-dev-bounces+patchwork-incoming=ozlabs.org@lists.ozlabs.org Sender: "Linuxppc-dev" Define the map_resource/unmap_resource callbacks for the dma_iommu_ops used by several powerpc platforms. The map_resource callback is called when trying to map a mmio resource through the dma_map_resource() driver API. For now, the callback returns an invalid address for devices using translations, but will "direct" map the resource when in bypass mode. Previous behavior for dma_map_resource() was to always return an invalid address. We also call an optional platform-specific controller op in case some setup is needed for the platform. Signed-off-by: Frederic Barrat Signed-off-by: Max Gurtovoy Reviewed-by: Oliver O'Halloran --- changes from v1: - rename pci_controller_ops callback to dma_direct_map_resource/dma_direct_unmap_resource - cosmetic changes to make the code more readable --- arch/powerpc/include/asm/pci-bridge.h | 7 +++++++ arch/powerpc/kernel/dma-iommu.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index 69f4cb3..aca3724 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h @@ -44,6 +44,13 @@ struct pci_controller_ops { #endif void (*shutdown)(struct pci_controller *hose); + int (*dma_direct_map_resource)(struct pci_dev *pdev, + phys_addr_t phys_addr, + size_t size, + enum dma_data_direction dir); + void (*dma_direct_unmap_resource)(struct pci_dev *pdev, + dma_addr_t addr, size_t size, + enum dma_data_direction dir); }; /* diff --git a/arch/powerpc/kernel/dma-iommu.c b/arch/powerpc/kernel/dma-iommu.c index e486d1d..049d000 100644 --- a/arch/powerpc/kernel/dma-iommu.c +++ b/arch/powerpc/kernel/dma-iommu.c @@ -108,6 +108,35 @@ static void dma_iommu_unmap_sg(struct device *dev, struct scatterlist *sglist, dma_direct_unmap_sg(dev, sglist, nelems, direction, attrs); } +static dma_addr_t dma_iommu_map_resource(struct device *dev, + phys_addr_t phys_addr, size_t size, + enum dma_data_direction dir, + unsigned long attrs) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct pci_controller *phb = pci_bus_to_host(pdev->bus); + struct pci_controller_ops *ops = &phb->controller_ops; + + if (!dma_iommu_map_bypass(dev, attrs) || + !ops->dma_direct_map_resource || + ops->dma_direct_map_resource(pdev, phys_addr, size, dir)) + return DMA_MAPPING_ERROR; + + return dma_direct_map_resource(dev, phys_addr, size, dir, attrs); +} + +static void dma_iommu_unmap_resource(struct device *dev, dma_addr_t dma_handle, + size_t size, enum dma_data_direction dir, + unsigned long attrs) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct pci_controller *phb = pci_bus_to_host(pdev->bus); + struct pci_controller_ops *ops = &phb->controller_ops; + + if (dma_iommu_map_bypass(dev, attrs) && ops->dma_direct_unmap_resource) + ops->dma_direct_unmap_resource(pdev, dma_handle, size, dir); +} + static bool dma_iommu_bypass_supported(struct device *dev, u64 mask) { struct pci_dev *pdev = to_pci_dev(dev); @@ -199,6 +228,8 @@ extern void dma_iommu_sync_sg_for_device(struct device *dev, .free = dma_iommu_free_coherent, .map_sg = dma_iommu_map_sg, .unmap_sg = dma_iommu_unmap_sg, + .map_resource = dma_iommu_map_resource, + .unmap_resource = dma_iommu_unmap_resource, .dma_supported = dma_iommu_dma_supported, .map_page = dma_iommu_map_page, .unmap_page = dma_iommu_unmap_page, From patchwork Thu Apr 30 13:15:20 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Max Gurtovoy X-Patchwork-Id: 1280409 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [203.11.71.2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 49CbhJ1ySLz9sSd for ; Thu, 30 Apr 2020 23:24:32 +1000 (AEST) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=mellanox.com Received: from bilbo.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 49CbhH3ZNQzDqPC for ; Thu, 30 Apr 2020 23:24:31 +1000 (AEST) X-Original-To: linuxppc-dev@lists.ozlabs.org Delivered-To: linuxppc-dev@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=mellanox.com (client-ip=193.47.165.129; helo=mellanox.co.il; envelope-from=maxg@mellanox.com; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=pass (p=none dis=none) header.from=mellanox.com X-Greylist: delayed 397 seconds by postgrey-1.36 at bilbo; Thu, 30 Apr 2020 23:22:05 AEST Received: from mellanox.co.il (mail-il-dmz.mellanox.com [193.47.165.129]) by lists.ozlabs.org (Postfix) with ESMTP id 49CbdT6L16zDrD1 for ; Thu, 30 Apr 2020 23:22:04 +1000 (AEST) Received: from Internal Mail-Server by MTLPINE1 (envelope-from maxg@mellanox.com) with ESMTPS (AES256-SHA encrypted); 30 Apr 2020 16:15:21 +0300 Received: from mtr-vdi-031.wap.labs.mlnx. (mtr-vdi-031.wap.labs.mlnx [10.209.102.136]) by labmailer.mlnx (8.13.8/8.13.8) with ESMTP id 03UDFKRW027016; Thu, 30 Apr 2020 16:15:20 +0300 From: Max Gurtovoy To: hch@lst.de, linux-pci@vger.kernel.org, oohall@gmail.com, linuxppc-dev@lists.ozlabs.org Subject: [PATCH 2/2 v2] powerpc/powernv: Enable and setup PCI P2P Date: Thu, 30 Apr 2020 16:15:20 +0300 Message-Id: <20200430131520.51211-2-maxg@mellanox.com> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20200430131520.51211-1-maxg@mellanox.com> References: <20200430131520.51211-1-maxg@mellanox.com> MIME-Version: 1.0 X-BeenThere: linuxppc-dev@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Linux on PowerPC Developers Mail List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: vladimirk@mellanox.com, clsoto@us.ibm.com, israelr@mellanox.com, shlomin@mellanox.com, fbarrat@linux.ibm.com, Max Gurtovoy , idanw@mellanox.com, aneela@mellanox.com Errors-To: linuxppc-dev-bounces+patchwork-incoming=ozlabs.org@lists.ozlabs.org Sender: "Linuxppc-dev" Implement the generic dma_map_resource callback on the PCI controller for powernv. This will enable PCI P2P on POWER9 architecture. It will allow catching a cross-PHB mmio mapping, which needs to be setup in hardware by calling opal. Both the initiator and target PHBs need to be configured, so we look for which PHB owns the mmio address being mapped. Signed-off-by: Frederic Barrat [maxg: added CONFIG_PCI_P2PDMA wrappers] Signed-off-by: Max Gurtovoy --- changes from v1: - remove CONFIG_PCI_P2PDMA around opal_pci_set_p2p decleration - divide pnv_pci_ioda_set_p2p to pnv_pci_ioda_enable_p2p/pnv_pci_ioda_disable_p2p - added pnv_pci_dma_dir_to_opal_p2p static helper --- arch/powerpc/include/asm/opal.h | 3 +- arch/powerpc/platforms/powernv/opal-call.c | 1 + arch/powerpc/platforms/powernv/pci-ioda.c | 212 +++++++++++++++++++++++++++-- arch/powerpc/platforms/powernv/pci.h | 9 ++ 4 files changed, 213 insertions(+), 12 deletions(-) diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h index 9986ac3..362f54b 100644 --- a/arch/powerpc/include/asm/opal.h +++ b/arch/powerpc/include/asm/opal.h @@ -284,7 +284,8 @@ int64_t opal_xive_set_queue_state(uint64_t vp, uint32_t prio, uint32_t qtoggle, uint32_t qindex); int64_t opal_xive_get_vp_state(uint64_t vp, __be64 *out_w01); - +int64_t opal_pci_set_p2p(uint64_t phb_init, uint64_t phb_target, + uint64_t desc, uint16_t pe_number); int64_t opal_imc_counters_init(uint32_t type, uint64_t address, uint64_t cpu_pir); int64_t opal_imc_counters_start(uint32_t type, uint64_t cpu_pir); diff --git a/arch/powerpc/platforms/powernv/opal-call.c b/arch/powerpc/platforms/powernv/opal-call.c index 5cd0f52..442d5445c 100644 --- a/arch/powerpc/platforms/powernv/opal-call.c +++ b/arch/powerpc/platforms/powernv/opal-call.c @@ -273,6 +273,7 @@ int64_t name(int64_t a0, int64_t a1, int64_t a2, int64_t a3, \ OPAL_CALL(opal_imc_counters_init, OPAL_IMC_COUNTERS_INIT); OPAL_CALL(opal_imc_counters_start, OPAL_IMC_COUNTERS_START); OPAL_CALL(opal_imc_counters_stop, OPAL_IMC_COUNTERS_STOP); +OPAL_CALL(opal_pci_set_p2p, OPAL_PCI_SET_P2P); OPAL_CALL(opal_get_powercap, OPAL_GET_POWERCAP); OPAL_CALL(opal_set_powercap, OPAL_SET_POWERCAP); OPAL_CALL(opal_get_power_shift_ratio, OPAL_GET_POWER_SHIFT_RATIO); diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index 57d3a6a..9ecc576 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -3706,18 +3706,208 @@ static void pnv_pci_ioda_dma_bus_setup(struct pci_bus *bus) } } +#ifdef CONFIG_PCI_P2PDMA +static DEFINE_MUTEX(p2p_mutex); + +static bool pnv_pci_controller_owns_addr(struct pci_controller *hose, + phys_addr_t addr, size_t size) +{ + int i; + + /* + * It seems safe to assume the full range is under the same PHB, so we + * can ignore the size. + */ + for (i = 0; i < ARRAY_SIZE(hose->mem_resources); i++) { + struct resource *res = &hose->mem_resources[i]; + + if (res->flags && addr >= res->start && addr < res->end) + return true; + } + return false; +} + +/* + * find the phb owning a mmio address if not owned locally + */ +static struct pnv_phb *pnv_pci_find_owning_phb(struct pci_dev *pdev, + phys_addr_t addr, size_t size) +{ + struct pci_controller *hose; + + /* fast path */ + if (pnv_pci_controller_owns_addr(pdev->bus->sysdata, addr, size)) + return NULL; + + list_for_each_entry(hose, &hose_list, list_node) { + struct pnv_phb *phb = hose->private_data; + + if (phb->type != PNV_PHB_NPU_NVLINK && + phb->type != PNV_PHB_NPU_OCAPI) { + if (pnv_pci_controller_owns_addr(hose, addr, size)) + return phb; + } + } + return NULL; +} + +static u64 pnv_pci_dma_dir_to_opal_p2p(enum dma_data_direction dir) +{ + if (dir == DMA_TO_DEVICE) + return OPAL_PCI_P2P_STORE; + else if (dir == DMA_FROM_DEVICE) + return OPAL_PCI_P2P_LOAD; + else if (dir == DMA_BIDIRECTIONAL) + return OPAL_PCI_P2P_LOAD | OPAL_PCI_P2P_STORE; + else + return 0; +} + +static int pnv_pci_ioda_enable_p2p(struct pci_dev *initiator, + struct pnv_phb *phb_target, + enum dma_data_direction dir) +{ + struct pci_controller *hose; + struct pnv_phb *phb_init; + struct pnv_ioda_pe *pe_init; + u64 desc; + int rc; + + if (!opal_check_token(OPAL_PCI_SET_P2P)) + return -ENXIO; + + hose = pci_bus_to_host(initiator->bus); + phb_init = hose->private_data; + + pe_init = pnv_ioda_get_pe(initiator); + if (!pe_init) + return -ENODEV; + + if (!pe_init->tce_bypass_enabled) + return -EINVAL; + + /* + * Configuring the initiator's PHB requires to adjust its TVE#1 + * setting. Since the same device can be an initiator several times for + * different target devices, we need to keep a reference count to know + * when we can restore the default bypass setting on its TVE#1 when + * disabling. Opal is not tracking PE states, so we add a reference + * count on the PE in linux. + * + * For the target, the configuration is per PHB, so we keep a + * target reference count on the PHB. + */ + mutex_lock(&p2p_mutex); + + desc = OPAL_PCI_P2P_ENABLE | pnv_pci_dma_dir_to_opal_p2p(dir); + /* always go to opal to validate the configuration */ + rc = opal_pci_set_p2p(phb_init->opal_id, phb_target->opal_id, desc, + pe_init->pe_number); + if (rc != OPAL_SUCCESS) { + rc = -EIO; + goto out; + } + + pe_init->p2p_initiator_count++; + phb_target->p2p_target_count++; + + + rc = 0; +out: + mutex_unlock(&p2p_mutex); + return rc; +} + +static int pnv_pci_dma_map_resource(struct pci_dev *pdev, + phys_addr_t phys_addr, size_t size, + enum dma_data_direction dir) +{ + struct pnv_phb *target_phb; + + target_phb = pnv_pci_find_owning_phb(pdev, phys_addr, size); + if (!target_phb) + return 0; + + return pnv_pci_ioda_enable_p2p(pdev, target_phb, dir); +} + +static int pnv_pci_ioda_disable_p2p(struct pci_dev *initiator, + struct pnv_phb *phb_target) +{ + struct pci_controller *hose; + struct pnv_phb *phb_init; + struct pnv_ioda_pe *pe_init; + int rc; + + if (!opal_check_token(OPAL_PCI_SET_P2P)) + return -ENXIO; + + hose = pci_bus_to_host(initiator->bus); + phb_init = hose->private_data; + + pe_init = pnv_ioda_get_pe(initiator); + if (!pe_init) + return -ENODEV; + + mutex_lock(&p2p_mutex); + + if (!pe_init->p2p_initiator_count || !phb_target->p2p_target_count) { + rc = -EINVAL; + goto out; + } + + if (--pe_init->p2p_initiator_count == 0) + pnv_pci_ioda2_set_bypass(pe_init, true); + + if (--phb_target->p2p_target_count == 0) { + rc = opal_pci_set_p2p(phb_init->opal_id, phb_target->opal_id, + 0, pe_init->pe_number); + if (rc != OPAL_SUCCESS) { + rc = -EIO; + goto out; + } + } + + rc = 0; +out: + mutex_unlock(&p2p_mutex); + return rc; +} + +static void pnv_pci_dma_unmap_resource(struct pci_dev *pdev, + dma_addr_t addr, size_t size, + enum dma_data_direction dir) +{ + struct pnv_phb *target_phb; + int rc; + + target_phb = pnv_pci_find_owning_phb(pdev, addr, size); + if (!target_phb) + return; + + rc = pnv_pci_ioda_disable_p2p(pdev, target_phb); + if (rc) + dev_err(&pdev->dev, "Failed to undo PCI peer-to-peer setup for address %llx: %d\n", + addr, rc); +} +#endif + static const struct pci_controller_ops pnv_pci_ioda_controller_ops = { - .dma_dev_setup = pnv_pci_ioda_dma_dev_setup, - .dma_bus_setup = pnv_pci_ioda_dma_bus_setup, - .iommu_bypass_supported = pnv_pci_ioda_iommu_bypass_supported, - .setup_msi_irqs = pnv_setup_msi_irqs, - .teardown_msi_irqs = pnv_teardown_msi_irqs, - .enable_device_hook = pnv_pci_enable_device_hook, - .release_device = pnv_pci_release_device, - .window_alignment = pnv_pci_window_alignment, - .setup_bridge = pnv_pci_setup_bridge, - .reset_secondary_bus = pnv_pci_reset_secondary_bus, - .shutdown = pnv_pci_ioda_shutdown, + .dma_dev_setup = pnv_pci_ioda_dma_dev_setup, + .dma_bus_setup = pnv_pci_ioda_dma_bus_setup, + .iommu_bypass_supported = pnv_pci_ioda_iommu_bypass_supported, + .setup_msi_irqs = pnv_setup_msi_irqs, + .teardown_msi_irqs = pnv_teardown_msi_irqs, + .enable_device_hook = pnv_pci_enable_device_hook, + .release_device = pnv_pci_release_device, + .window_alignment = pnv_pci_window_alignment, + .setup_bridge = pnv_pci_setup_bridge, + .reset_secondary_bus = pnv_pci_reset_secondary_bus, + .shutdown = pnv_pci_ioda_shutdown, +#ifdef CONFIG_PCI_P2PDMA + .dma_direct_map_resource = pnv_pci_dma_map_resource, + .dma_direct_unmap_resource = pnv_pci_dma_unmap_resource, +#endif }; static const struct pci_controller_ops pnv_npu_ioda_controller_ops = { diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h index d3bbdea..5f85d9c 100644 --- a/arch/powerpc/platforms/powernv/pci.h +++ b/arch/powerpc/platforms/powernv/pci.h @@ -79,6 +79,10 @@ struct pnv_ioda_pe { struct pnv_ioda_pe *master; struct list_head slaves; +#ifdef CONFIG_PCI_P2PDMA + /* PCI peer-to-peer*/ + int p2p_initiator_count; +#endif /* Link in list of PE#s */ struct list_head list; }; @@ -168,6 +172,11 @@ struct pnv_phb { /* PHB and hub diagnostics */ unsigned int diag_data_size; u8 *diag_data; + +#ifdef CONFIG_PCI_P2PDMA + /* PCI peer-to-peer*/ + int p2p_target_count; +#endif }; extern struct pci_ops pnv_pci_ops;