diff mbox

[4/4] powerpc/powernv: Register PCI dev specific reset handlers

Message ID 1423803299-22356-5-git-send-email-gwshan@linux.vnet.ibm.com (mailing list archive)
State Superseded
Delegated to: Benjamin Herrenschmidt
Headers show

Commit Message

Gavin Shan Feb. 13, 2015, 4:54 a.m. UTC
Currently, PCI VFIO infrastructure depends on pci_reset_function()
to ensure the PCI device in clean state when passing to guest, or
being returned back to host. However, the function doesn't work
(or well) on some PCI devices, which potentially brings pending
traffic over the boundary between host/guest and usually causes
memory corruption.

The patch registers PCI device specific reset handlers for those
PCI devices, pci_reset_function() doesn't work or not well, to
translate the request to EEH PE reset if possible.

Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
---
 arch/powerpc/platforms/powernv/pci.c | 61 ++++++++++++++++++++++++++++++++++++
 1 file changed, 61 insertions(+)
diff mbox

Patch

diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c
index e69142f..c68d508 100644
--- a/arch/powerpc/platforms/powernv/pci.c
+++ b/arch/powerpc/platforms/powernv/pci.c
@@ -727,6 +727,64 @@  static void pnv_pci_dma_fallback_setup(struct pci_controller *hose,
 	set_iommu_table_base_and_group(&pdev->dev, pdn->iommu_table);
 }
 
+/*
+ * VFIO infrastructure depends on pci_reset_function() to do
+ * reset on the PCI devices when their owership is changed to
+ * ensure consistent clean state on those devices when someone
+ * grabs them. However, pci_reset_function() doesn't work for
+ * some deivces that require EEH PE reset.
+ */
+static int pnv_pci_dev_specific_reset(struct pci_dev *pdev, int probe)
+{
+	struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev);
+	struct eeh_pe *pe = edev ? edev->pe : NULL;
+
+	if (!pe)
+		return -ENOTTY;
+
+	if (probe)
+		return 0;
+
+	pci_set_pcie_reset_state(pdev, pcie_hot_reset);
+	pci_set_pcie_reset_state(pdev, pcie_deassert_reset);
+	return 0;
+}
+
+static int pnv_pci_root_bridge_prepare(struct pci_host_bridge *bridge)
+{
+	static bool pnv_pci_reset_registered = false;
+	int ret = 0;
+
+	if (pnv_pci_reset_registered)
+		return 0;
+
+	pnv_pci_reset_registered = true;
+	ret += pci_dev_add_specific_reset(PCI_VENDOR_ID_IBM,
+					  PCI_ANY_ID,
+					  pnv_pci_dev_specific_reset);
+	ret += pci_dev_add_specific_reset(PCI_VENDOR_ID_BROADCOM,
+					  PCI_DEVICE_ID_NX2_57800,
+					  pnv_pci_dev_specific_reset);
+	ret += pci_dev_add_specific_reset(PCI_VENDOR_ID_BROADCOM,
+					  PCI_DEVICE_ID_NX2_57840,
+					  pnv_pci_dev_specific_reset);
+	ret += pci_dev_add_specific_reset(PCI_VENDOR_ID_BROADCOM,
+					  PCI_DEVICE_ID_NX2_57810,
+					  pnv_pci_dev_specific_reset);
+	ret += pci_dev_add_specific_reset(PCI_VENDOR_ID_MELLANOX,
+					  PCI_ANY_ID,
+					  pnv_pci_dev_specific_reset);
+	ret += pci_dev_add_specific_reset(PCI_VENDOR_ID_TI,
+					  PCI_ANY_ID,
+					  pnv_pci_dev_specific_reset);
+	if (ret)
+		pr_warn("%s: Failure adding PCI specific reset handlers\n",
+			__func__);
+
+	/* Don't return error to keep PCI core going */
+	return 0;
+}
+
 static void pnv_pci_dma_dev_setup(struct pci_dev *pdev)
 {
 	struct pci_controller *hose = pci_bus_to_host(pdev->bus);
@@ -820,6 +878,9 @@  void __init pnv_pci_init(void)
 	/* Setup the linkage between OF nodes and PHBs */
 	pci_devs_phb_init();
 
+	/* Setup root bridge */
+	ppc_md.pcibios_root_bridge_prepare = pnv_pci_root_bridge_prepare;
+
 	/* Configure IOMMU DMA hooks */
 	ppc_md.pci_dma_dev_setup = pnv_pci_dma_dev_setup;
 	ppc_md.tce_build = pnv_tce_build_vm;