[RFC PATCH] powerpc/powernv: Fix dma_ops for bypass window
Gavin Shan
gwshan at linux.vnet.ibm.com
Thu Sep 25 14:14:20 EST 2014
When using bypass window on IODA2, the incorrect DMA operations
"dma_iommu_ops" is used by devices. The device driver calls
dma_get_required_mask() to determine using 32-bits or bypass DMA
window. Unfortunately, the returned DMA mask always forces the
driver to use 32-bits DMA window. The problem was reported on
the device as follows:
0004:03:00.0 0107: 1000:0087 (rev 05)
0004:03:00.0 Serial Attached SCSI controller: LSI Logic / Symbios \
Logic SAS2308 PCI-Express Fusion-MPT SAS-2 (rev 05)
The patch fixes above issue by keeping things consistent: when
enabling bypass window, we switch to "dma_direct_ops". Instead,
switch to "pci_dma_ops" when disabling bypass window.
Reported-by: Murali N. Iyer <mniyer at us.ibm.com>
Signed-off-by: Gavin Shan <gwshan at linux.vnet.ibm.com>
---
arch/powerpc/platforms/powernv/pci-ioda.c | 76 +++++++++++++++++++------------
1 file changed, 46 insertions(+), 30 deletions(-)
diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c
index 36b1a7a..60e44d9 100644
--- a/arch/powerpc/platforms/powernv/pci-ioda.c
+++ b/arch/powerpc/platforms/powernv/pci-ioda.c
@@ -883,13 +883,29 @@ static int pnv_pci_ioda_dma_set_mask(struct pnv_phb *phb,
set_dma_offset(&pdev->dev, pe->tce_bypass_base);
} else {
dev_info(&pdev->dev, "Using 32-bit DMA via iommu\n");
- set_dma_ops(&pdev->dev, &dma_iommu_ops);
+ set_dma_ops(&pdev->dev, get_pci_dma_ops());
set_iommu_table_base(&pdev->dev, &pe->tce32_table);
}
*pdev->dev.dma_mask = dma_mask;
return 0;
}
+static void pnv_ioda_setup_dev_dma(struct pnv_ioda_pe *pe,
+ struct pci_dev *pdev,
+ bool add_to_iommu_group)
+{
+ if (pe->tce_bypass_enabled) {
+ set_dma_ops(&pdev->dev, &dma_direct_ops);
+ set_dma_offset(&pdev->dev, pe->tce_bypass_base);
+ } else {
+ set_dma_ops(&pdev->dev, get_pci_dma_ops());
+ set_iommu_table_base(&pdev->dev, &pe->tce32_table);
+ }
+
+ if (add_to_iommu_group)
+ iommu_add_device(&pdev->dev);
+}
+
static void pnv_ioda_setup_bus_dma(struct pnv_ioda_pe *pe,
struct pci_bus *bus,
bool add_to_iommu_group)
@@ -897,11 +913,7 @@ static void pnv_ioda_setup_bus_dma(struct pnv_ioda_pe *pe,
struct pci_dev *dev;
list_for_each_entry(dev, &bus->devices, bus_list) {
- if (add_to_iommu_group)
- set_iommu_table_base_and_group(&dev->dev,
- &pe->tce32_table);
- else
- set_iommu_table_base(&dev->dev, &pe->tce32_table);
+ pnv_ioda_setup_dev_dma(pe, dev, add_to_iommu_group);
if (dev->subordinate)
pnv_ioda_setup_bus_dma(pe, dev->subordinate,
@@ -909,6 +921,15 @@ static void pnv_ioda_setup_bus_dma(struct pnv_ioda_pe *pe,
}
}
+static void pnv_ioda_setup_pe_dma(struct pnv_ioda_pe *pe,
+ bool add_to_iommu_group)
+{
+ if (pe->pdev)
+ pnv_ioda_setup_dev_dma(pe, pe->pdev, add_to_iommu_group);
+ else
+ pnv_ioda_setup_bus_dma(pe, pe->pbus, add_to_iommu_group);
+}
+
static void pnv_pci_ioda1_tce_invalidate(struct pnv_ioda_pe *pe,
struct iommu_table *tbl,
__be64 *startp, __be64 *endp, bool rm)
@@ -1080,11 +1101,7 @@ static void pnv_pci_ioda_setup_dma_pe(struct pnv_phb *phb,
iommu_init_table(tbl, phb->hose->node);
iommu_register_group(tbl, phb->hose->global_number, pe->pe_number);
- if (pe->pdev)
- set_iommu_table_base_and_group(&pe->pdev->dev, tbl);
- else
- pnv_ioda_setup_bus_dma(pe, pe->pbus, true);
-
+ pnv_ioda_setup_pe_dma(pe, true);
return;
fail:
/* XXX Failure: Try to fallback to 64-bit only ? */
@@ -1101,7 +1118,13 @@ static void pnv_pci_ioda2_set_bypass(struct iommu_table *tbl, bool enable)
uint16_t window_id = (pe->pe_number << 1 ) + 1;
int64_t rc;
+ /* Check if we really need do something */
+ if (pe->tce_bypass_enabled == enable)
+ return;
+
pe_info(pe, "%sabling 64-bit DMA bypass\n", enable ? "En" : "Dis");
+ pe->tce_bypass_enabled = enable;
+
if (enable) {
phys_addr_t top = memblock_end_of_DRAM();
@@ -1117,22 +1140,15 @@ static void pnv_pci_ioda2_set_bypass(struct iommu_table *tbl, bool enable)
window_id,
pe->tce_bypass_base,
0);
-
- /*
- * EEH needs the mapping between IOMMU table and group
- * of those VFIO/KVM pass-through devices. We can postpone
- * resetting DMA ops until the DMA mask is configured in
- * host side.
- */
- if (pe->pdev)
- set_iommu_table_base(&pe->pdev->dev, tbl);
- else
- pnv_ioda_setup_bus_dma(pe, pe->pbus, false);
}
- if (rc)
+ if (rc != OPAL_SUCCESS) {
pe_err(pe, "OPAL error %lld configuring bypass window\n", rc);
- else
- pe->tce_bypass_enabled = enable;
+ return;
+ }
+
+ /* Update base and operations */
+ pe->tce_bypass_enabled = enable;
+ pnv_ioda_setup_pe_dma(pe, false);
}
static void pnv_pci_ioda2_setup_bypass_pe(struct pnv_phb *phb,
@@ -1213,12 +1229,12 @@ static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb,
iommu_init_table(tbl, phb->hose->node);
iommu_register_group(tbl, phb->hose->global_number, pe->pe_number);
- if (pe->pdev)
- set_iommu_table_base_and_group(&pe->pdev->dev, tbl);
- else
- pnv_ioda_setup_bus_dma(pe, pe->pbus, true);
+ /* If the bypass window fails to be created, we still
+ * can use 32-bits window.
+ */
+ pnv_ioda_setup_pe_dma(pe, true);
- /* Also create a bypass window */
+ /* Create bypass window */
pnv_pci_ioda2_setup_bypass_pe(phb, pe);
return;
fail:
--
1.8.3.2
More information about the Linuxppc-dev
mailing list