[Skiboot] [PATCH v2] phb4: Enable PCI peer-to-peer

Frederic Barrat fbarrat at linux.vnet.ibm.com
Tue Jun 27 04:00:08 AEST 2017


P9 supports PCI peer-to-peer: a PCI device can write directly to the
mmio space of another PCI device. It completely by-passes the CPU.

It requires some configuration on the PHBs involved:

1. on the initiating side, the address for the read/write operation is
in the mmio space of the target, i.e. well outside the range normally
allowed. So we disable range-checking on the TVT entry in bypass mode.

2. on the target side, we need to explicitly enable p2p by setting a
bit in a configuration register. It has the side-effect of reserving
an outbound (as seen from the CPU) store queue for p2p. Therefore we
only enable p2p on the PHBs using it, as we don't want to waste the
resource if we don't have to.

P9 supports p2p mmio writes. Reads are currently only supported if the
two devices are under the same PHB, but that is expected to change in
the future.

The patch adds a new OPAL call to allow the OS to declare a p2p
(initiator, target) pair.

Signed-off-by: Frederic Barrat <fbarrat at linux.vnet.ibm.com>
---
Changelog:
 - change of API
 - allow disabling of p2p config
 
 core/pci-opal.c                       | 52 ++++++++++++++++++++
 doc/opal-api/opal-pci-set-p2p-148.rst | 53 ++++++++++++++++++++
 hw/phb4.c                             | 92 +++++++++++++++++++++++++++++++++++
 include/opal-api.h                    | 13 ++++-
 include/pci.h                         |  4 ++
 include/phb4-regs.h                   |  1 +
 include/phb4.h                        |  1 +
 7 files changed, 215 insertions(+), 1 deletion(-)
 create mode 100644 doc/opal-api/opal-pci-set-p2p-148.rst

diff --git a/core/pci-opal.c b/core/pci-opal.c
index 5d58a88f..54132a7d 100644
--- a/core/pci-opal.c
+++ b/core/pci-opal.c
@@ -74,6 +74,7 @@ opal_call(OPAL_PCI_CONFIG_WRITE_BYTE, opal_pci_config_write_byte, 4);
 opal_call(OPAL_PCI_CONFIG_WRITE_HALF_WORD, opal_pci_config_write_half_word, 4);
 opal_call(OPAL_PCI_CONFIG_WRITE_WORD, opal_pci_config_write_word, 4);
 
+static struct lock opal_p2p_oper_lock = LOCK_UNLOCKED;
 static struct lock opal_eeh_evt_lock = LOCK_UNLOCKED;
 static uint64_t opal_eeh_evt = 0;
 
@@ -977,3 +978,54 @@ static int64_t opal_pci_set_phb_capi_mode(uint64_t phb_id, uint64_t mode, uint64
 	return rc;
 }
 opal_call(OPAL_PCI_SET_PHB_CAPI_MODE, opal_pci_set_phb_capi_mode, 3);
+
+static int64_t opal_pci_set_p2p(uint64_t phbid_init, uint64_t phbid_target,
+				uint64_t desc, uint16_t pe_number)
+{
+	struct phb *phb_init = pci_get_phb(phbid_init);
+	struct phb *phb_target = pci_get_phb(phbid_target);
+	int64_t rc;
+
+	if (!phb_init || !phb_target)
+		return OPAL_PARAMETER;
+	if (!phb_init->ops->set_p2p || !phb_target->ops->set_p2p)
+		return OPAL_UNSUPPORTED;
+	/*
+	 * On p9, loads are only supported if initiator and target
+	 * devices are on the same PHB.
+	 */
+	if ((desc & OPAL_PCI_P2P_LOAD) && (phb_init != phb_target))
+		return OPAL_UNSUPPORTED;
+
+	/*
+	 * We need to modify the state of 2 PHBs. Only allow one
+	 * p2p operation at a time to simplify error handling.
+	 * Start with the target PHB, as it's easier to roll-back in case
+	 * of errors.
+	 */
+	lock(&opal_p2p_oper_lock);
+
+	phb_lock(phb_target);
+	rc = phb_target->ops->set_p2p(phb_target, OPAL_PCI_P2P_TARGET, desc,
+				pe_number);
+	phb_unlock(phb_target);
+	if (rc)
+		goto out;
+
+	phb_lock(phb_init);
+	rc = phb_init->ops->set_p2p(phb_init, OPAL_PCI_P2P_INITIATOR, desc,
+				pe_number);
+	phb_unlock(phb_init);
+	if (rc) {
+		/* revert enablement bit */
+		desc = desc ^ OPAL_PCI_P2P_ENABLE;
+		phb_lock(phb_target);
+		phb_init->ops->set_p2p(phb_target, OPAL_PCI_P2P_TARGET,
+				desc, pe_number);
+		phb_unlock(phb_target);
+	}
+out:
+	unlock(&opal_p2p_oper_lock);
+	return rc;
+}
+opal_call(OPAL_PCI_SET_P2P, opal_pci_set_p2p, 4);
diff --git a/doc/opal-api/opal-pci-set-p2p-148.rst b/doc/opal-api/opal-pci-set-p2p-148.rst
new file mode 100644
index 00000000..ea2125d6
--- /dev/null
+++ b/doc/opal-api/opal-pci-set-p2p-148.rst
@@ -0,0 +1,53 @@
+OPAL_PCI_SET_P2P
+================
+::
+
+   #define OPAL_PCI_SET_P2P			149
+
+   int64_t opal_pci_set_p2p(uint64_t phbid_init, uint64_t phbid_target,
+				uint64_t desc, uint16_t pe_number)
+
+   /* PCI p2p descriptor */
+   #define OPAL_PCI_P2P_ENABLE		0x1
+   #define OPAL_PCI_P2P_LOAD		0x2
+   #define OPAL_PCI_P2P_STORE		0x4
+
+The host calls this function to enable PCI peer-to-peer on the PHBs.
+
+Parameters
+----------
+::
+
+   uint64_t phbid_init
+   uint64_t phbid_target
+   uint64_t desc
+   uint16_t pe_number
+
+
+``phbid_init``
+  is the value from the PHB node ibm,opal-phbid property for the device initiating the p2p operation
+
+``phbid_target``
+  is the value from the PHB node ibm,opal-phbid property for the device targeted by the p2p operation
+
+``desc``
+  tells whether the p2p operation is a store (OPAL_PCI_P2P_STORE) or load (OPAL_PCI_P2P_LOAD). Can be both.
+  OPAL_PCI_P2P_ENABLE enables/disables the setting
+
+``pe_number``
+  PE number for the initiating device
+
+Return Values
+-------------
+
+``OPAL_SUCCESS``
+  Configuration was successful
+
+``OPAL_PARAMETER``
+  Invalid PHB or mode parameter
+
+``OPAL_UNSUPPORTED``
+  Not supported by hardware
+
+``OPAL_WRONG_STATE``
+  PHB can't be configured to expected state
diff --git a/hw/phb4.c b/hw/phb4.c
index 4d93b813..814be837 100644
--- a/hw/phb4.c
+++ b/hw/phb4.c
@@ -2322,6 +2322,7 @@ static int64_t phb4_creset(struct pci_slot *slot)
 		PHBDBG(p, "CRESET: Reinitialization\n");
 		p->flags &= ~PHB4_AIB_FENCED;
 		p->flags &= ~PHB4_CAPP_RECOVERY;
+		p->p2p_target_count = 0;
 		phb4_init_hw(p, false);
 		pci_slot_set_state(slot, PHB4_SLOT_CRESET_FRESET);
 		return pci_slot_set_sm_timeout(slot, msecs_to_tb(100));
@@ -3251,6 +3252,96 @@ static int64_t phb4_set_capi_mode(struct phb *phb, uint64_t mode,
 	return OPAL_UNSUPPORTED;
 }
 
+static int64_t phb4_p2p_enable_initiator(struct phb4 *p, uint16_t pe_number)
+{
+	uint64_t tve;
+	uint16_t window_id = (pe_number << 1) + 1;
+
+	/*
+	 * initiator needs access to the MMIO space of the target,
+	 * which is well beyond the 'normal' memory area. Set the TVE
+	 * with no range checking.
+	 */
+	PHBDBG(p, "Setting TVE#1 for peer-to-peer for pe %d\n", pe_number);
+	tve = PPC_BIT(51);
+	phb4_ioda_sel(p, IODA3_TBL_TVT, window_id, false);
+	out_be64(p->regs + PHB_IODA_DATA0, tve);
+	p->tve_cache[window_id] = tve;
+	return OPAL_SUCCESS;
+}
+
+static int64_t phb4_p2p_enable_target(struct phb4 *p)
+{
+	uint64_t val;
+
+	/*
+	 * Enabling p2p on a target PHB reserves an outbound (as seen
+	 * from the CPU) store queue for p2p.  Keep a reference count
+	 * as the phb could be used by multiple targets.
+	 */
+	if (p->p2p_target_count++ == 0) {
+		PHBDBG(p, "Enabling peer-to-peer\n");
+		xscom_read(p->chip_id,
+			p->pe_stk_xscom + XPEC_NEST_STK_PBCQ_MODE, &val);
+		val |= XPEC_NEST_STK_PBCQ_MODE_P2P;
+		xscom_write(p->chip_id,
+			p->pe_stk_xscom + XPEC_NEST_STK_PBCQ_MODE, val);
+	}
+	return OPAL_SUCCESS;
+}
+
+static int64_t phb4_p2p_disable_target(struct phb4 *p)
+{
+	uint64_t val;
+
+	if (p->p2p_target_count == 0)
+		return OPAL_WRONG_STATE;
+
+	if (--p->p2p_target_count == 0) {
+		PHBDBG(p, "Disabling peer-to-peer\n");
+		xscom_read(p->chip_id,
+			p->pe_stk_xscom + XPEC_NEST_STK_PBCQ_MODE, &val);
+		val &= ~XPEC_NEST_STK_PBCQ_MODE_P2P;
+		xscom_write(p->chip_id,
+			p->pe_stk_xscom + XPEC_NEST_STK_PBCQ_MODE, val);
+	}
+	return OPAL_SUCCESS;
+}
+
+static int64_t phb4_set_p2p(struct phb *phb, uint64_t mode, uint64_t flags,
+			uint16_t pe_number)
+{
+	struct phb4 *p = phb_to_phb4(phb);
+	int64_t rc = OPAL_SUCCESS;
+
+	switch (mode) {
+	case OPAL_PCI_P2P_INITIATOR:
+		if (flags & OPAL_PCI_P2P_ENABLE)
+			rc = phb4_p2p_enable_initiator(p, pe_number);
+		/*
+		 * Don't touch the initiator PHB when disabling p2p.
+		 * We should reset the TVE to its default bypass
+		 * setting.  However, it is more easily done from the
+		 * OS, as it knows the the start and end address. And
+		 * if the initiator is used more than once, with a
+		 * different target, we cannot reset the TVE setting
+		 * just yet. We need a p2p ref count on the PE, which
+		 * is more easily handled by the OS, as skiboot
+		 * doesn't track PEs.
+		 */
+		break;
+	case OPAL_PCI_P2P_TARGET:
+		if (flags & OPAL_PCI_P2P_ENABLE)
+			rc = phb4_p2p_enable_target(p);
+		else
+			rc = phb4_p2p_disable_target(p);
+		break;
+	default:
+		rc = OPAL_PARAMETER;
+	}
+	return rc;
+}
+
 static const struct phb_ops phb4_ops = {
 	.cfg_read8		= phb4_pcicfg_read8,
 	.cfg_read16		= phb4_pcicfg_read16,
@@ -3284,6 +3375,7 @@ static const struct phb_ops phb4_ops = {
 	.get_diag_data2		= phb4_get_diag_data,
 	.tce_kill		= phb4_tce_kill,
 	.set_capi_mode		= phb4_set_capi_mode,
+	.set_p2p		= phb4_set_p2p,
 };
 
 static void phb4_init_ioda3(struct phb4 *p)
diff --git a/include/opal-api.h b/include/opal-api.h
index 8ffdd4b1..58d727c5 100644
--- a/include/opal-api.h
+++ b/include/opal-api.h
@@ -204,7 +204,8 @@
 #define OPAL_NPU_INIT_CONTEXT			146
 #define OPAL_NPU_DESTROY_CONTEXT		147
 #define OPAL_NPU_MAP_LPAR			148
-#define OPAL_LAST				148
+#define OPAL_PCI_SET_P2P			149
+#define OPAL_LAST				149
 
 /* Device tree flags */
 
@@ -1245,6 +1246,16 @@ enum {
 	XIVE_DUMP_EMU_STATE	= 5,
 };
 
+/* PCI p2p descriptor */
+#define OPAL_PCI_P2P_ENABLE		0x1
+#define OPAL_PCI_P2P_LOAD		0x2
+#define OPAL_PCI_P2P_STORE		0x4
+
+enum {
+	OPAL_PCI_P2P_INITIATOR	= 0,
+	OPAL_PCI_P2P_TARGET	= 1,
+};
+
 #endif /* __ASSEMBLY__ */
 
 #endif /* __OPAL_API_H */
diff --git a/include/pci.h b/include/pci.h
index f216594c..6373ab02 100644
--- a/include/pci.h
+++ b/include/pci.h
@@ -323,6 +323,10 @@ struct phb_ops {
 				 uint64_t pe_number);
 
 	int64_t (*set_capp_recovery)(struct phb *phb);
+
+	/* PCI peer-to-peer setup */
+	int64_t (*set_p2p)(struct phb *phb, uint64_t mode, uint64_t flags,
+			uint16_t pe_number);
 };
 
 enum phb_type {
diff --git a/include/phb4-regs.h b/include/phb4-regs.h
index d22acf4d..cee50e6c 100644
--- a/include/phb4-regs.h
+++ b/include/phb4-regs.h
@@ -334,6 +334,7 @@
 #define XPEC_NEST_STK_ERR_RPT1			0xb
 #define XPEC_NEST_STK_PBCQ_STAT			0xc
 #define XPEC_NEST_STK_PBCQ_MODE			0xd
+#define   XPEC_NEST_STK_PBCQ_MODE_P2P		PPC_BIT(0)
 #define XPEC_NEST_STK_MMIO_BAR0			0xe
 #define XPEC_NEST_STK_MMIO_BAR0_MASK		0xf
 #define XPEC_NEST_STK_MMIO_BAR1			0x10
diff --git a/include/phb4.h b/include/phb4.h
index 360889a5..e6617a9d 100644
--- a/include/phb4.h
+++ b/include/phb4.h
@@ -279,6 +279,7 @@ struct phb4 {
 	uint64_t		nfir_cache;	/* Used by complete reset */
 	bool			err_pending;
 	struct phb4_err		err;
+	uint32_t		p2p_target_count;
 
 	/* Cache some RC registers that need to be emulated */
 	uint32_t		rc_cache[4];
-- 
2.11.0



More information about the Skiboot mailing list