[PATCH v2 10/16] PCI: Add pci_mfd_isolation()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Introduce a new ACS related quirk that indicates a single function in a
MFD is isolated from other functions in the MFD. In otherwords the
function has no internal loopback to other functions.

This is different from the existing ACS quirk since ACS only indicates if
the function's egress can reach other functions in the MFD for P2P, while
leaving the ingress question up to the ACS of the other
functions. pci_mfd_isolation() reports both the ingress and egress
behavior together, if both are blocked then it can return true.

This quirk matches the historical behavior of the
pci_dev_specific_acs_enabled() quirk that would allow 'adding' ACS to a
single function to force that function into its own iommu_group. Many
quirks were added on this assumption to give specific functions their own
IOMMU groups. For example some Intel systems have a NIC in a MFD with
other functions and no ACS capabilities. The NIC has been quirked, while
the rest of the MFD functions are unquirked.

Add an internal flag, PCI_ACS_QUIRK_ACS_ISOLATED, in the upper 16 bits of
the acs_flags. If the quirk implementation sees the flag and decides the
function should be isolated, then it can return success. The additional
flag automatically makes the existing quirks all return failure as it is
never masked off.

Have pci_quirk_mf_endpoint_acs() support the flag with some expectation
this will grown down the road.

Apply the MFD isolation override to a general case of host bridges inside
MFDs that match some AMD systems:

00:07.0 Host bridge: Advanced Micro Devices, Inc. [AMD] Starship/Matisse PCIe Dummy Host Bridge
        Control: I/O- Mem- BusMaster- SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx-
        Status: Cap- 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
        NUMA node: 3
        IOMMU group: 52

00:07.1 PCI bridge: Advanced Micro Devices, Inc. [AMD] Starship/Matisse Internal PCIe GPP Bridge 0 to bus[E:B] (prog-if 00 [Normal decode])
	[..]
        Bus: primary=00, secondary=01, subordinate=01, sec-latency=0
	[..]
        Capabilities: [2a0 v1] Access Control Services

The logic being that a host bridge with no mmio has no downstream devices
and cannot source or sink any P2P MMIO operations, thus it does not
contribute to the ACS isolation calculation for the MFD.

Signed-off-by: Jason Gunthorpe <jgg@xxxxxxxxxx>
---
 drivers/pci/pci.h    |  5 ++++
 drivers/pci/quirks.c | 58 +++++++++++++++++++++++++++++++++++---------
 drivers/pci/search.c | 30 +++++++++++++++++++++++
 include/linux/pci.h  |  4 +++
 4 files changed, 85 insertions(+), 12 deletions(-)

diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 12215ee72afb68..78651025096bcd 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -786,6 +786,7 @@ int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags);
 int pci_dev_specific_enable_acs(struct pci_dev *dev);
 int pci_dev_specific_disable_acs_redir(struct pci_dev *dev);
 int pcie_failed_link_retrain(struct pci_dev *dev);
+bool pci_dev_specific_mfd_isolated(struct pci_dev *dev);
 #else
 static inline int pci_dev_specific_acs_enabled(struct pci_dev *dev,
 					       u16 acs_flags)
@@ -804,6 +805,10 @@ static inline int pcie_failed_link_retrain(struct pci_dev *dev)
 {
 	return -ENOTTY;
 }
+static inline bool pci_dev_specific_mfd_isolated(struct pci_dev *dev)
+{
+	return false;
+}
 #endif
 
 /* PCI error reporting and recovery */
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 71b9550e46eb06..85c786d66646a8 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -4616,6 +4616,8 @@ static void quirk_chelsio_T5_disable_root_port_attributes(struct pci_dev *pdev)
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_CHELSIO, PCI_ANY_ID,
 			 quirk_chelsio_T5_disable_root_port_attributes);
 
+#define PCI_ACS_QUIRK_ACS_ISOLATED BIT(16)
+
 /*
  * pci_acs_ctrl_enabled - compare desired ACS controls with those provided
  *			  by a device
@@ -4948,6 +4950,13 @@ static int pci_quirk_intel_spt_pch_acs(struct pci_dev *dev, u32 acs_flags)
 
 static int pci_quirk_mf_endpoint_acs(struct pci_dev *dev, u32 acs_flags)
 {
+	/*
+	 * The function cannot get P2P MMIO from the other functions in the MFD
+	 * either even if the other functions do not have ACS or ACS quirks.
+	 */
+	if (acs_flags & PCI_ACS_QUIRK_ACS_ISOLATED)
+		return 1;
+
 	/*
 	 * SV, TB, and UF are not relevant to multifunction endpoints.
 	 *
@@ -5186,18 +5195,7 @@ static const struct pci_dev_acs_enabled {
 	{ 0 }
 };
 
-/*
- * pci_dev_specific_acs_enabled - check whether device provides ACS controls
- * @dev:	PCI device
- * @acs_flags:	Bitmask of desired ACS controls
- *
- * Returns:
- *   -ENOTTY:	No quirk applies to this device; we can't tell whether the
- *		device provides the desired controls
- *   0:		Device does not provide all the desired controls
- *   >0:	Device provides all the controls in @acs_flags
- */
-int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags)
+static int pci_dev_call_acs_enabled(struct pci_dev *dev, u32 acs_flags)
 {
 	const struct pci_dev_acs_enabled *i;
 	int ret;
@@ -5222,6 +5220,42 @@ int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags)
 	return -ENOTTY;
 }
 
+/*
+ * pci_dev_specific_acs_enabled - check whether device provides ACS controls
+ * @dev:	PCI device
+ * @acs_flags:	Bitmask of desired ACS controls
+ *
+ * Returns:
+ *   -ENOTTY:	No quirk applies to this device; we can't tell whether the
+ *		device provides the desired controls
+ *   0:		Device does not provide all the desired controls
+ *   >0:	Device provides all the controls in @acs_flags
+ */
+int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags)
+{
+	return pci_dev_call_acs_enabled(dev, acs_flags);
+}
+
+/*
+ * pci_dev_specific_mfd_isolated- check whether a MFD function is isolated
+ * @dev:	PCI device
+ *
+ * pci_dev_specific_acs_enabled() emulates the ACS flags using a quirk however
+ * historically Linux has not quirked every function in a MFD.
+ * pci_dev_specific_mfd_isolated() overrides the other function MFD checks and
+ * can consider a single function fully isolated from all other functions both
+ * for egress and ingress directions.
+ *
+ * Returns:
+ *   false:	No override, use normal PCI defined mechanisms
+ *   true:	Function is isolated from P2P to other functions in the device
+ */
+bool pci_dev_specific_mfd_isolated(struct pci_dev *dev)
+{
+	return pci_dev_call_acs_enabled(dev, PCI_ACS_QUIRK_ACS_ISOLATED |
+						     PCI_ACS_ISOLATED) > 0;
+}
+
 /* Config space offset of Root Complex Base Address register */
 #define INTEL_LPC_RCBA_REG 0xf0
 /* 31:14 RCBA address */
diff --git a/drivers/pci/search.c b/drivers/pci/search.c
index dc816dc4505c6d..2be6881087b335 100644
--- a/drivers/pci/search.c
+++ b/drivers/pci/search.c
@@ -277,6 +277,36 @@ enum pci_bus_isolation pci_bus_isolated(struct pci_bus *bus)
 	}
 }
 
+/*
+ * pci_mfd_isolated- check whether a MFD function is isolated
+ * @dev:	PCI device
+ *
+ * True if the dev function on a MFD should be considered isolated from all
+ * other functions in the MFD. This is used to override ACS checks that might
+ * otherwise indicate the MFD function participates in an internal loopback.
+ *
+ * Returns:
+ *   false:	No override, use normal PCI defined mechanisms
+ *   true:	Function is isolated from P2P to other functions in the device
+ */
+bool pci_mfd_isolated(struct pci_dev *dev)
+{
+	/*
+	 * For some reason AMD likes to put "dummy functions" in their PCI
+	 * hierarchy as part of a multi function device. These are notable
+	 * because they can't do anything. No BARs and no downstream bus. Since
+	 * they cannot accept P2P or initiate any MMIO we consider them to be
+	 * isolated from the rest of MFD. Since they often accompany a real PCI
+	 * bridge with downstream devices it is important that the MFD be
+	 * considered isolated. Annoyingly there is no ACS capability reported
+	 * so we assume that a host bridge in a MFD with no MMIO has the special
+	 * property of never accepting or initiating P2P operations.
+	 */
+	if (dev->class >> 8 == PCI_CLASS_BRIDGE_HOST && !pci_has_mmio(dev))
+		return true;
+	return pci_dev_specific_mfd_isolated(dev);
+}
+
 static struct pci_bus *pci_do_find_bus(struct pci_bus *bus, unsigned char busnr)
 {
 	struct pci_bus *child;
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 2e629087539101..d95a983c835666 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1256,6 +1256,7 @@ void pci_reachable_set(struct pci_dev *start, struct pci_reachable_set *devfns,
 		       bool (*reachable)(struct pci_dev *deva,
 					 struct pci_dev *devb));
 enum pci_bus_isolation pci_bus_isolated(struct pci_bus *bus);
+bool pci_mfd_isolated(struct pci_dev *dev);
 
 int pci_dev_present(const struct pci_device_id *ids);
 
@@ -2078,6 +2079,9 @@ pci_reachable_set(struct pci_dev *start, struct pci_reachable_set *devfns,
 static inline enum pci_bus_isolation pci_bus_isolated(struct pci_bus *bus)
 { return PCIE_NON_ISOLATED; }
 
+bool pci_mfd_isolated(struct pci_dev *dev)
+{ return false; }
+
 static inline int pci_dev_present(const struct pci_device_id *ids)
 { return 0; }
 
-- 
2.43.0





[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux