Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 18 Oct 2015 20:20:29 +0000 (UTC)
From:      "Conrad E. Meyer" <cem@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r289543 - head/sys/dev/ntb/ntb_hw
Message-ID:  <201510182020.t9IKKTMs092994@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: cem
Date: Sun Oct 18 20:20:29 2015
New Revision: 289543
URL: https://svnweb.freebsd.org/changeset/base/289543

Log:
  NTB: Flesh out the rest of the xeon_setup_b2b_mw changes
  
  Move all Xeon secondary register setup to the setup_b2b_mw routine.  We
  use subroutines to make it a bit less wordy than the Linux version.
  
  Adds a new tunable, 'hw.ntb.b2b_mw_share'.  By default, it is off
  (zero).  If both sides enable it (any non-zero value), the NTB driver
  attempts to use only half of a memory window for remote register MMIO
  access.
  
  This is still part of the large Linux rewrite (e26a5843).
  
  Authored by:	Allen Hubbe
  Obtained from:	Linux (e26a5843) (Dual BSD/GPL driver)
  Sponsored by:	EMC / Isilon Storage Division

Modified:
  head/sys/dev/ntb/ntb_hw/ntb_hw.c
  head/sys/dev/ntb/ntb_hw/ntb_regs.h

Modified: head/sys/dev/ntb/ntb_hw/ntb_hw.c
==============================================================================
--- head/sys/dev/ntb/ntb_hw/ntb_hw.c	Sun Oct 18 20:20:20 2015	(r289542)
+++ head/sys/dev/ntb/ntb_hw/ntb_hw.c	Sun Oct 18 20:20:29 2015	(r289543)
@@ -99,6 +99,13 @@ struct ntb_pci_bar_info {
 	vm_paddr_t		pbase;
 	void			*vbase;
 	u_long			size;
+
+	/* Configuration register offsets */
+	uint32_t		psz_off;
+	uint32_t		ssz_off;
+	uint32_t		sbarbase_off;
+	uint32_t		sbarlmt_off;
+	uint32_t		pbarxlat_off;
 };
 
 struct ntb_int_info {
@@ -178,6 +185,7 @@ struct ntb_softc {
 	/* Offset of peer bar0 in B2B BAR */
 	uint64_t			b2b_off;
 	/* Memory window used to access peer bar0 */
+#define B2B_MW_DISABLED			UINT8_MAX
 	uint8_t				b2b_mw_idx;
 
 	uint8_t				mw_count;
@@ -277,7 +285,12 @@ static int ntb_xeon_init_dev(struct ntb_
 static int ntb_soc_init_dev(struct ntb_softc *ntb);
 static void ntb_teardown_xeon(struct ntb_softc *ntb);
 static void configure_soc_secondary_side_bars(struct ntb_softc *ntb);
-static void configure_xeon_secondary_side_bars(struct ntb_softc *ntb);
+static void xeon_reset_sbar_size(struct ntb_softc *, enum ntb_bar idx,
+    enum ntb_bar regbar);
+static void xeon_set_sbar_base_and_limit(struct ntb_softc *,
+    uint64_t base_addr, enum ntb_bar idx, enum ntb_bar regbar);
+static void xeon_set_pbar_xlat(struct ntb_softc *, uint64_t base_addr,
+    enum ntb_bar idx);
 static int xeon_setup_b2b_mw(struct ntb_softc *,
     const struct ntb_b2b_addr *addr, const struct ntb_b2b_addr *peer_addr);
 static void soc_link_hb(void *arg);
@@ -418,7 +431,7 @@ ntb_attach(device_t device)
 	ntb->device = device;
 	ntb->type = p->type;
 	ntb->features = p->features;
-	ntb->b2b_mw_idx = UINT8_MAX;
+	ntb->b2b_mw_idx = B2B_MW_DISABLED;
 
 	/* Heartbeat timer for NTB_SOC since there is no link interrupt */
 	callout_init(&ntb->heartbeat_timer, 1);
@@ -488,7 +501,8 @@ static inline enum ntb_bar
 ntb_mw_to_bar(struct ntb_softc *ntb, unsigned mw)
 {
 
-	KASSERT(mw < ntb->mw_count || (mw != UINT8_MAX && mw == ntb->b2b_mw_idx),
+	KASSERT(mw < ntb->mw_count ||
+	    (mw != B2B_MW_DISABLED && mw == ntb->b2b_mw_idx),
 	    ("%s: mw:%u > count:%u", __func__, mw, (unsigned)ntb->mw_count));
 
 	return (ntb->reg->mw_bar[mw]);
@@ -508,12 +522,24 @@ ntb_map_pci_bars(struct ntb_softc *ntb)
 	rc = map_memory_window_bar(ntb, &ntb->bar_info[NTB_B2B_BAR_1]);
 	if (rc != 0)
 		goto out;
+	ntb->bar_info[NTB_B2B_BAR_1].psz_off = XEON_PBAR23SZ_OFFSET;
+	ntb->bar_info[NTB_B2B_BAR_1].ssz_off = XEON_SBAR23SZ_OFFSET;
+	ntb->bar_info[NTB_B2B_BAR_1].sbarbase_off = XEON_SBAR2BASE_OFFSET;
+	ntb->bar_info[NTB_B2B_BAR_1].sbarlmt_off = XEON_SBAR2LMT_OFFSET;
+	ntb->bar_info[NTB_B2B_BAR_1].pbarxlat_off = XEON_PBAR2XLAT_OFFSET;
 
 	ntb->bar_info[NTB_B2B_BAR_2].pci_resource_id = PCIR_BAR(4);
+	/* XXX Are shared MW B2Bs write-combining? */
 	if (HAS_FEATURE(NTB_SDOORBELL_LOCKUP) && !HAS_FEATURE(NTB_SPLIT_BAR))
 		rc = map_mmr_bar(ntb, &ntb->bar_info[NTB_B2B_BAR_2]);
 	else
 		rc = map_memory_window_bar(ntb, &ntb->bar_info[NTB_B2B_BAR_2]);
+	ntb->bar_info[NTB_B2B_BAR_2].psz_off = XEON_PBAR4SZ_OFFSET;
+	ntb->bar_info[NTB_B2B_BAR_2].ssz_off = XEON_SBAR4SZ_OFFSET;
+	ntb->bar_info[NTB_B2B_BAR_2].sbarbase_off = XEON_SBAR4BASE_OFFSET;
+	ntb->bar_info[NTB_B2B_BAR_2].sbarlmt_off = XEON_SBAR4LMT_OFFSET;
+	ntb->bar_info[NTB_B2B_BAR_2].pbarxlat_off = XEON_PBAR4XLAT_OFFSET;
+
 	if (!HAS_FEATURE(NTB_SPLIT_BAR))
 		goto out;
 
@@ -522,6 +548,11 @@ ntb_map_pci_bars(struct ntb_softc *ntb)
 		rc = map_mmr_bar(ntb, &ntb->bar_info[NTB_B2B_BAR_3]);
 	else
 		rc = map_memory_window_bar(ntb, &ntb->bar_info[NTB_B2B_BAR_3]);
+	ntb->bar_info[NTB_B2B_BAR_3].psz_off = XEON_PBAR5SZ_OFFSET;
+	ntb->bar_info[NTB_B2B_BAR_3].ssz_off = XEON_SBAR5SZ_OFFSET;
+	ntb->bar_info[NTB_B2B_BAR_3].sbarbase_off = XEON_SBAR5BASE_OFFSET;
+	ntb->bar_info[NTB_B2B_BAR_3].sbarlmt_off = XEON_SBAR5LMT_OFFSET;
+	ntb->bar_info[NTB_B2B_BAR_3].pbarxlat_off = XEON_PBAR5XLAT_OFFSET;
 
 out:
 	if (rc != 0)
@@ -1117,35 +1148,11 @@ ntb_xeon_init_dev(struct ntb_softc *ntb)
 	 * which may hang the system.  To workaround this use the second memory
 	 * window to access the interrupt and scratch pad registers on the
 	 * remote system.
-	 *
-	 * There is another HW errata on the limit registers -- they can only
-	 * be written when the base register is (?)4GB aligned and < 32-bit.
-	 * This should already be the case based on the driver defaults, but
-	 * write the limit registers first just in case.
 	 */
-	if (HAS_FEATURE(NTB_SDOORBELL_LOCKUP)) {
-		/* Reserve the last MW for mapping remote spad */
+	if (HAS_FEATURE(NTB_SDOORBELL_LOCKUP))
+		/* Use the last MW for mapping remote spad */
 		ntb->b2b_mw_idx = ntb->mw_count - 1;
-		ntb->mw_count--;
-		/*
-		 * Set the Limit register to 4k, the minimum size, to prevent
-		 * an illegal access.
-		 */
-		if (HAS_FEATURE(NTB_SPLIT_BAR)) {
-			ntb_reg_write(4, XEON_PBAR4LMT_OFFSET, 0);
-			ntb_reg_write(4, XEON_PBAR5LMT_OFFSET,
-			    ntb_get_mw_size(ntb, ntb->b2b_mw_idx) + 0x1000);
-		} else
-			ntb_reg_write(8, XEON_PBAR4LMT_OFFSET,
-			    ntb_get_mw_size(ntb, ntb->b2b_mw_idx) + 0x1000);
-	} else {
-		/*
-		 * Disable the limit register, just in case it is set to
-		 * something silly.  A 64-bit write will also clear PBAR5LMT in
-		 * split-bar mode, and this is desired.
-		 */
-		ntb_reg_write(8, XEON_PBAR4LMT_OFFSET, 0);
-
+	else if (HAS_FEATURE(NTB_B2BDOORBELL_BIT14))
 		/*
 		 * HW Errata on bit 14 of b2bdoorbell register.  Writes will not be
 		 * mirrored to the remote system.  Shrink the number of bits by one,
@@ -1154,9 +1161,7 @@ ntb_xeon_init_dev(struct ntb_softc *ntb)
 		 * On REGS_THRU_MW errata mode, we don't use the b2bdoorbell register
 		 * anyway.  Nor for non-B2B connection types.
 		 */
-		if (HAS_FEATURE(NTB_B2BDOORBELL_BIT14))
-			ntb->db_count = XEON_DB_COUNT - 1;
-	}
+		ntb->db_count = XEON_DB_COUNT - 1;
 
 	ntb->db_valid_mask = (1ull << ntb->db_count) - 1;
 
@@ -1244,44 +1249,198 @@ configure_soc_secondary_side_bars(struct
 	}
 }
 
+
+/*
+ * When working around Xeon SDOORBELL errata by remapping remote registers in a
+ * MW, limit the B2B MW to half a MW.  By sharing a MW, half the shared MW
+ * remains for use by a higher layer.
+ *
+ * Will only be used if working around SDOORBELL errata and the BIOS-configured
+ * MW size is sufficiently large.
+ */
+static unsigned int ntb_b2b_mw_share;
+SYSCTL_UINT(_hw_ntb, OID_AUTO, b2b_mw_share, CTLFLAG_RDTUN, &ntb_b2b_mw_share,
+    0, "If enabled (non-zero), prefer to share half of the B2B peer register "
+    "MW with higher level consumers.  Both sides of the NTB MUST set the same "
+    "value here.");
+
+static void
+xeon_reset_sbar_size(struct ntb_softc *ntb, enum ntb_bar idx,
+    enum ntb_bar regbar)
+{
+	struct ntb_pci_bar_info *bar;
+	uint8_t bar_sz;
+
+	if (!HAS_FEATURE(NTB_SPLIT_BAR) && idx >= NTB_B2B_BAR_3)
+		return;
+
+	bar = &ntb->bar_info[idx];
+	bar_sz = pci_read_config(ntb->device, bar->psz_off, 1);
+	if (idx == regbar) {
+		if (ntb->b2b_off != 0)
+			bar_sz--;
+		else
+			bar_sz = 0;
+	}
+	pci_write_config(ntb->device, bar->ssz_off, bar_sz, 1);
+	bar_sz = pci_read_config(ntb->device, bar->ssz_off, 1);
+	(void)bar_sz;
+}
+
+static void
+xeon_set_sbar_base_and_limit(struct ntb_softc *ntb, uint64_t base_addr,
+    enum ntb_bar idx, enum ntb_bar regbar)
+{
+	struct ntb_pci_bar_info *bar;
+	vm_paddr_t bar_addr;
+
+	bar = &ntb->bar_info[idx];
+	bar_addr = base_addr + ((idx == regbar) ? ntb->b2b_off : 0);
+
+	if (HAS_FEATURE(NTB_SPLIT_BAR) && idx >= NTB_B2B_BAR_2) {
+		ntb_reg_write(4, bar->sbarbase_off, bar_addr);
+		ntb_reg_write(4, bar->sbarlmt_off, bar_addr);
+		bar_addr = ntb_reg_read(4, bar->sbarbase_off);
+		(void)bar_addr;
+		bar_addr = ntb_reg_read(4, bar->sbarlmt_off);
+	} else {
+		ntb_reg_write(8, bar->sbarbase_off, bar_addr);
+		ntb_reg_write(8, bar->sbarlmt_off, bar_addr);
+		bar_addr = ntb_reg_read(8, bar->sbarbase_off);
+		(void)bar_addr;
+		bar_addr = ntb_reg_read(8, bar->sbarlmt_off);
+	}
+	(void)bar_addr;
+}
+
+static void
+xeon_set_pbar_xlat(struct ntb_softc *ntb, uint64_t base_addr, enum ntb_bar idx)
+{
+	struct ntb_pci_bar_info *bar;
+
+	bar = &ntb->bar_info[idx];
+	if (HAS_FEATURE(NTB_SPLIT_BAR) && idx >= NTB_B2B_BAR_2) {
+		ntb_reg_write(4, bar->pbarxlat_off, base_addr);
+		base_addr = ntb_reg_read(4, bar->pbarxlat_off);
+	} else {
+		ntb_reg_write(8, bar->pbarxlat_off, base_addr);
+		base_addr = ntb_reg_read(8, bar->pbarxlat_off);
+	}
+	(void)base_addr;
+}
+
 static int
 xeon_setup_b2b_mw(struct ntb_softc *ntb, const struct ntb_b2b_addr *addr,
     const struct ntb_b2b_addr *peer_addr)
 {
-
-	/* Local addresses */
-	ntb_reg_write(8, XEON_PBAR2XLAT_OFFSET, addr->bar2_addr64);
-	if (HAS_FEATURE(NTB_SDOORBELL_LOCKUP))
-		ntb_reg_write(8, XEON_PBAR4XLAT_OFFSET, addr->bar0_addr);
-	else {
-		if (HAS_FEATURE(NTB_SPLIT_BAR)) {
-			ntb_reg_write(4, XEON_PBAR4XLAT_OFFSET,
-			    addr->bar4_addr32);
-			ntb_reg_write(4, XEON_PBAR5XLAT_OFFSET,
-			    addr->bar5_addr32);
-		} else
-			ntb_reg_write(8, XEON_PBAR4XLAT_OFFSET,
-			    addr->bar4_addr64);
-		/*
-		 * B2B_XLAT_OFFSET is a 64-bit register but can only be
-		 * written 32 bits at a time.
-		 */
-		ntb_reg_write(4, XEON_B2B_XLAT_OFFSETL,
-		    addr->bar0_addr & 0xffffffff);
-		ntb_reg_write(4, XEON_B2B_XLAT_OFFSETU, addr->bar0_addr >> 32);
+	struct ntb_pci_bar_info *b2b_bar;
+	vm_size_t bar_size;
+	uint64_t bar_addr;
+	enum ntb_bar b2b_bar_num, i;
+
+	if (ntb->b2b_mw_idx == B2B_MW_DISABLED) {
+		b2b_bar = NULL;
+		b2b_bar_num = NTB_CONFIG_BAR;
+		ntb->b2b_off = 0;
+	} else {
+		b2b_bar_num = ntb_mw_to_bar(ntb, ntb->b2b_mw_idx);
+		KASSERT(b2b_bar_num > 0 && b2b_bar_num < NTB_MAX_BARS,
+		    ("invalid b2b mw bar"));
+
+		b2b_bar = &ntb->bar_info[b2b_bar_num];
+		bar_size = b2b_bar->size;
+
+		if (ntb_b2b_mw_share != 0 &&
+		    (bar_size >> 1) >= XEON_B2B_MIN_SIZE)
+			ntb->b2b_off = bar_size >> 1;
+		else if (bar_size >= XEON_B2B_MIN_SIZE) {
+			ntb->b2b_off = 0;
+			ntb->mw_count--;
+		} else {
+			device_printf(ntb->device,
+			    "B2B bar size is too small!\n");
+			return (EIO);
+		}
 	}
 
-	/* Peer addresses */
-	ntb_reg_write(8, XEON_SBAR0BASE_OFFSET, peer_addr->bar0_addr);
-	ntb_reg_write(8, XEON_SBAR2BASE_OFFSET, peer_addr->bar2_addr64);
+	/*
+	 * Reset the secondary bar sizes to match the primary bar sizes.
+	 * (Except, disable or halve the size of the B2B secondary bar.)
+	 */
+	for (i = NTB_B2B_BAR_1; i < NTB_MAX_BARS; i++)
+		xeon_reset_sbar_size(ntb, i, b2b_bar_num);
+
+	bar_addr = 0;
+	if (b2b_bar_num == NTB_CONFIG_BAR)
+		bar_addr = addr->bar0_addr;
+	else if (b2b_bar_num == NTB_B2B_BAR_1)
+		bar_addr = addr->bar2_addr64;
+	else if (b2b_bar_num == NTB_B2B_BAR_2 && !HAS_FEATURE(NTB_SPLIT_BAR))
+		bar_addr = addr->bar4_addr64;
+	else if (b2b_bar_num == NTB_B2B_BAR_2)
+		bar_addr = addr->bar4_addr32;
+	else if (b2b_bar_num == NTB_B2B_BAR_3)
+		bar_addr = addr->bar5_addr32;
+	else
+		KASSERT(false, ("invalid bar"));
+
+	ntb_reg_write(8, XEON_SBAR0BASE_OFFSET, bar_addr);
+
+	/*
+	 * Other SBARs are normally hit by the PBAR xlat, except for the b2b
+	 * register BAR.  The B2B BAR is either disabled above or configured
+	 * half-size.  It starts at PBAR xlat + offset.
+	 *
+	 * Also set up incoming BAR limits == base (zero length window).
+	 */
+	xeon_set_sbar_base_and_limit(ntb, addr->bar2_addr64, NTB_B2B_BAR_1,
+	    b2b_bar_num);
+	if (HAS_FEATURE(NTB_SPLIT_BAR)) {
+		xeon_set_sbar_base_and_limit(ntb, addr->bar4_addr32,
+		    NTB_B2B_BAR_2, b2b_bar_num);
+		xeon_set_sbar_base_and_limit(ntb, addr->bar5_addr32,
+		    NTB_B2B_BAR_3, b2b_bar_num);
+	} else
+		xeon_set_sbar_base_and_limit(ntb, addr->bar4_addr64,
+		    NTB_B2B_BAR_2, b2b_bar_num);
+
+	/* Zero incoming translation addrs */
+	ntb_reg_write(8, XEON_SBAR2XLAT_OFFSET, 0);
+	ntb_reg_write(8, XEON_SBAR4XLAT_OFFSET, 0);
+
+	/* Zero outgoing translation limits (whole bar size windows) */
+	ntb_reg_write(8, XEON_PBAR2LMT_OFFSET, 0);
+	ntb_reg_write(8, XEON_PBAR4LMT_OFFSET, 0);
+
+	/* Set outgoing translation offsets */
+	xeon_set_pbar_xlat(ntb, peer_addr->bar2_addr64, NTB_B2B_BAR_1);
 	if (HAS_FEATURE(NTB_SPLIT_BAR)) {
-		ntb_reg_write(4, XEON_SBAR4BASE_OFFSET,
-		    peer_addr->bar4_addr32);
-		ntb_reg_write(4, XEON_SBAR5BASE_OFFSET,
-		    peer_addr->bar5_addr32);
+		xeon_set_pbar_xlat(ntb, peer_addr->bar4_addr32, NTB_B2B_BAR_2);
+		xeon_set_pbar_xlat(ntb, peer_addr->bar5_addr32, NTB_B2B_BAR_3);
 	} else
-		ntb_reg_write(8, XEON_SBAR4BASE_OFFSET,
-		    peer_addr->bar4_addr64);
+		xeon_set_pbar_xlat(ntb, peer_addr->bar4_addr64, NTB_B2B_BAR_2);
+
+	/* Set the translation offset for B2B registers */
+	bar_addr = 0;
+	if (b2b_bar_num == NTB_CONFIG_BAR)
+		bar_addr = peer_addr->bar0_addr;
+	else if (b2b_bar_num == NTB_B2B_BAR_1)
+		bar_addr = peer_addr->bar2_addr64;
+	else if (b2b_bar_num == NTB_B2B_BAR_2 && !HAS_FEATURE(NTB_SPLIT_BAR))
+		bar_addr = peer_addr->bar4_addr64;
+	else if (b2b_bar_num == NTB_B2B_BAR_2)
+		bar_addr = peer_addr->bar4_addr32;
+	else if (b2b_bar_num == NTB_B2B_BAR_3)
+		bar_addr = peer_addr->bar5_addr32;
+	else
+		KASSERT(false, ("invalid bar"));
+
+	/*
+	 * B2B_XLAT_OFFSET is a 64-bit register but can only be written 32 bits
+	 * at a time.
+	 */
+	ntb_reg_write(4, XEON_B2B_XLAT_OFFSETL, bar_addr & 0xffffffff);
+	ntb_reg_write(4, XEON_B2B_XLAT_OFFSETU, bar_addr >> 32);
 	return (0);
 }
 
@@ -1966,7 +2125,8 @@ ntb_get_peer_db_addr(struct ntb_softc *n
 		KASSERT((HAS_FEATURE(NTB_SPLIT_BAR) && ntb->mw_count == 2) ||
 		    (!HAS_FEATURE(NTB_SPLIT_BAR) && ntb->mw_count == 1),
 		    ("mw_count invalid after setup"));
-		KASSERT(ntb->b2b_mw_idx != UINT8_MAX, ("invalid b2b idx"));
+		KASSERT(ntb->b2b_mw_idx != B2B_MW_DISABLED,
+		    ("invalid b2b idx"));
 
 		bar = &ntb->bar_info[ntb_mw_to_bar(ntb, ntb->b2b_mw_idx)];
 		regoff = XEON_SHADOW_PDOORBELL_OFFSET;

Modified: head/sys/dev/ntb/ntb_hw/ntb_regs.h
==============================================================================
--- head/sys/dev/ntb/ntb_hw/ntb_regs.h	Sun Oct 18 20:20:20 2015	(r289542)
+++ head/sys/dev/ntb/ntb_hw/ntb_regs.h	Sun Oct 18 20:20:29 2015	(r289543)
@@ -135,6 +135,11 @@
 
 #define XEON_PBAR23SZ_OFFSET	0x00d0
 #define XEON_PBAR45SZ_OFFSET	0x00d1
+#define XEON_PBAR4SZ_OFFSET	0x00d1
+#define XEON_PBAR5SZ_OFFSET	0x00d5
+#define XEON_SBAR23SZ_OFFSET	0x00d2
+#define XEON_SBAR4SZ_OFFSET	0x00d3
+#define XEON_SBAR5SZ_OFFSET	0x00d6
 #define NTB_PPD_OFFSET		0x00d4
 #define XEON_PPD_CONN_TYPE	0x0003
 #define XEON_PPD_DEV_TYPE	0x0010



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201510182020.t9IKKTMs092994>