Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 9 Apr 2008 13:16:20 -0700
From:      "David Christensen" <davidch@broadcom.com>
To:        "Peter Wemm" <peter@wemm.org>
Cc:        "cvs-src@freebsd.org" <cvs-src@freebsd.org>, "src-committers@freebsd.org" <src-committers@freebsd.org>, "cvs-all@freebsd.org" <cvs-all@freebsd.org>
Subject:   RE: cvs commit: src/sys/dev/bce if_bce.c if_bcefw.h if_bcereg.h
Message-ID:  <5D267A3F22FD854F8F48B3D2B523819324EF6DEE3B@IRVEXCHCCR01.corp.ad.broadcom.com>
In-Reply-To: <e7db6d980804030521h7af1ecd4pc492caa0bce14138@mail.gmail.com>
References:  <200802220046.m1M0kMPM008814@repoman.freebsd.org> <e7db6d980803310032p7b3eec0et355919afdb147218@mail.gmail.com> <5D267A3F22FD854F8F48B3D2B523819324EF633FCC@IRVEXCHCCR01.corp.ad.broadcom.com> <e7db6d980803311234q1602d7a4ncf9e3fadaa5f679b@mail.gmail.com> <e7db6d980804030521h7af1ecd4pc492caa0bce14138@mail.gmail.com>

next in thread | previous in thread | raw e-mail | index | archive | help

[-- Attachment #1 --]
> On 8.0/i386, with PAE enabled, I get messages on the console and the
> system hangs when trying to do a nfs mount.  Backing out the driver
> fixes it.  The same driver doesn't cause quite as spectacular a
> failure on 8.0/amd64, but it isn't exactly happy..
>
> Additional IP options:.^M
> Mounting NFS file systebcms:e1: link state changed to UP^M
> bce1: discard frame w/o leading ethernet header (len 0 pkt len 0)^M
> bce1: discard frame w/o leading ethernet header (len 0 pkt len 0)^M
> bce1: discard frame w/o leading ethernet header (len 0 pkt len 0)^M
> bce1: discard frame w/o leading ethernet header (len 0 pkt len 0)^M
> [..forever..]
>
> NFS over UDP, fwiw.  Server is a netapp.

I may have found the performance problem.  It looks like an issue
with handling the producer/consumer indices for the send ring that
would cause outgoing frames to just sit there until a received
frame came in and kickstarted the interrupt handler.  This is
consistent with your observation that enabling promiscuous mode
seemed to make things better, there's more received traffic.  I
found that pinging the SUT from another system has the same
effect.  Can you try the attached patch to head and let me know?

I tested on RELENG_7 and I haven't seen the discard errors yet.

Dave

[-- Attachment #2 --]
--- if_bcereg.h.head	2008-04-03 13:58:19.000000000 -0700
+++ if_bcereg.h	2008-04-09 13:09:04.000000000 -0700
@@ -132,6 +132,9 @@
 	"\02b1"					\
 	"\01b0"
 
+/* DRC - Start */
+/* #define BCE_DEBUG 1 */
+/* DRC - End */
 /****************************************************************************/
 /* Debugging macros and definitions.                                        */
 /****************************************************************************/
@@ -5134,6 +5137,8 @@ struct bce_softc
 	/* TX DMA mapping failure counter. */
 	u32 tx_dma_map_failures;
 
+	u64 rx_intr_time;
+
 #ifdef BCE_DEBUG
 	/* Track the number of enqueued mbufs. */
 	int	debug_tx_mbuf_alloc;
@@ -5147,7 +5152,6 @@ struct bce_softc
 	u32 tx_interrupts;
 
 	/* Track interrupt time (25MHz clock). */
-	u64 rx_intr_time;
 	u64 tx_intr_time;
 
 	u32	rx_low_watermark;			/* Lowest number of rx_bd's free. */

[-- Attachment #3 --]
--- if_bce.c.head	2008-04-03 13:58:19.000000000 -0700
+++ if_bce.c	2008-04-09 10:39:47.000000000 -0700
@@ -54,7 +54,7 @@ __FBSDID("$FreeBSD: src/sys/dev/bce/if_b
 /* BCE Debug Options                                                        */
 /****************************************************************************/
 #ifdef BCE_DEBUG
-	u32 bce_debug = BCE_WARN;
+	u32 bce_debug = BCE_WARN_SEND | BCE_WARN_RECV;
 
 	/*          0 = Never              */
 	/*          1 = 1 in 2,147,483,648 */
@@ -4392,7 +4392,7 @@ bce_free_pg_chain(struct bce_softc *sc)
 {
 	int i;
 
-	DBPRINT(sc, BCE_VERBOSE_RESET, "Entering %s()\n", __FUNCTION__);
+	DBPRINT(sc, BCE_EXCESSIVE_RESET, "Entering %s()\n", __FUNCTION__);
 
 	/* Free any mbufs still in the mbuf page chain. */
 	for (i = 0; i < TOTAL_PG_BD; i++) {
@@ -4417,7 +4417,7 @@ bce_free_pg_chain(struct bce_softc *sc)
 		BCE_PRINTF("%s(): Memory leak! Lost %d mbufs from page chain!\n",
 			__FUNCTION__, sc->debug_pg_mbuf_alloc));
 
-	DBPRINT(sc, BCE_VERBOSE_RESET, "Exiting %s()\n", __FUNCTION__);
+	DBPRINT(sc, BCE_EXCESSIVE_RESET, "Exiting %s()\n", __FUNCTION__);
 }
 
 
@@ -4561,6 +4561,7 @@ bce_get_hw_rx_cons(struct bce_softc *sc)
 	return hw_cons;
 }
 
+
 /****************************************************************************/
 /* Handles received frame interrupt events.                                 */
 /*                                                                          */
@@ -4576,9 +4577,10 @@ bce_rx_intr(struct bce_softc *sc)
 	u16 sw_rx_cons, sw_rx_cons_idx, sw_pg_cons, sw_pg_cons_idx, hw_rx_cons;
 	u32 status;
 
+	u32 timer_start, timer_end;
+	timer_start = REG_RD(sc, BCE_TIMER_25MHZ_FREE_RUN);
+
 #ifdef BCE_DEBUG
-	u32 rx_intr_start, rx_intr_end;
-	rx_intr_start = REG_RD(sc, BCE_TIMER_25MHZ_FREE_RUN);
 	sc->rx_interrupts++;
 #endif
 
@@ -4599,6 +4601,10 @@ bce_rx_intr(struct bce_softc *sc)
 	sw_rx_cons = sc->rx_cons;
 	sw_pg_cons = sc->pg_cons;
 
+	DBPRINT(sc, BCE_INFO_RECV, "%s(enter): rx_prod = 0x%04X, "
+		"rx_cons = 0x%04X, rx_prod_bseq = 0x%08X\n",
+		__FUNCTION__, sc->rx_prod, sc->rx_cons, sc->rx_prod_bseq);
+
 	/* Update some debug statistics counters */
 	DBRUNIF((sc->free_rx_bd < sc->rx_low_watermark),
 		sc->rx_low_watermark = sc->free_rx_bd);
@@ -4608,7 +4614,7 @@ bce_rx_intr(struct bce_softc *sc)
 	/* ToDo: Consider setting a limit on the number of packets processed. */
 	while (sw_rx_cons != hw_rx_cons) {
 		struct mbuf *m0;
-
+		
 		/* Convert the producer/consumer indices to an actual rx_bd index. */
 		sw_rx_cons_idx = RX_CHAIN_IDX(sw_rx_cons);
 
@@ -4681,6 +4687,10 @@ bce_rx_intr(struct bce_softc *sc)
 			 * is filled and the remaining bytes are placed 
 			 * in the page chain.
 			 */
+
+			DBPRINT(sc, BCE_INFO_RECV, "%s(): Found a large packet.\n",
+				__FUNCTION__);
+
 		 	if (status & L2_FHDR_STATUS_SPLIT)
 				m0->m_len = l2fhdr->l2_fhdr_ip_xsum;
 
@@ -4736,6 +4746,9 @@ bce_rx_intr(struct bce_softc *sc)
 			 * 154 bytes or less in size.
 			 */
 
+			DBPRINT(sc, BCE_INFO_RECV, "%s(): Found a small packet.\n",
+				__FUNCTION__);
+
 			/* Set the total packet length. */
 			m0->m_pkthdr.len = m0->m_len = pkt_len;
 		}
@@ -4746,6 +4759,12 @@ bce_rx_intr(struct bce_softc *sc)
 		/* Check that the resulting mbuf chain is valid. */
 		DBRUN(m_sanity(m0, FALSE));
 
+		DBRUNIF((m0->m_len < ETHER_HDR_LEN),
+			BCE_PRINTF("%s(): Unexpected length = %d!.\n", 
+				__FUNCTION__, m0->m_len);
+			bce_breakpoint(sc));
+
+			
 		DBRUNIF(DB_RANDOMTRUE(bce_debug_l2fhdr_status_check),
 			BCE_PRINTF("Simulating l2_fhdr status error.\n");
 			status = status | L2_FHDR_ERRORS_PHY_DECODE);
@@ -4856,8 +4875,28 @@ bce_rx_int_next_rx:
 		"rx_cons = 0x%04X, rx_prod_bseq = 0x%08X\n",
 		__FUNCTION__, sc->rx_prod, sc->rx_cons, sc->rx_prod_bseq);
 
-	DBRUN(rx_intr_end = REG_RD(sc, BCE_TIMER_25MHZ_FREE_RUN);
-		sc->rx_intr_time += (u64) BCE_TIME_DELTA(rx_intr_start, rx_intr_end));
+	timer_end = REG_RD(sc, BCE_TIMER_25MHZ_FREE_RUN);
+	sc->rx_intr_time += (u64) (timer_start > timer_end ? 
+		(timer_start - timer_end) : (~timer_start + timer_end + 1));
+}
+
+
+/****************************************************************************/
+/* Reads the receive consumer value from the status block (skipping over    */
+/* chain page pointer if necessary).                                        */
+/*                                                                          */
+/* Returns:                                                                 */
+/*   hw_cons                                                                */
+/****************************************************************************/
+static inline u16
+bce_get_hw_tx_cons(struct bce_softc *sc)
+{
+	u16 hw_cons = sc->status_block->status_tx_quick_consumer_index0;
+
+	if ((hw_cons & USABLE_TX_BD_PER_PAGE) == USABLE_TX_BD_PER_PAGE)
+		hw_cons++;
+
+	return hw_cons;
 }
 
 
@@ -4870,7 +4909,6 @@ bce_rx_int_next_rx:
 static void
 bce_tx_intr(struct bce_softc *sc)
 {
-	struct status_block *sblk = sc->status_block;
 	struct ifnet *ifp = sc->bce_ifp;
 	u16 hw_tx_cons, sw_tx_cons, sw_tx_chain_cons;
 
@@ -4883,12 +4921,7 @@ bce_tx_intr(struct bce_softc *sc)
 	BCE_LOCK_ASSERT(sc);
 
 	/* Get the hardware's view of the TX consumer index. */
-	hw_tx_cons = sc->hw_tx_cons = sblk->status_tx_quick_consumer_index0;
-
-	/* Skip to the next entry if this is a chain page pointer. */
-	if ((hw_tx_cons & USABLE_TX_BD_PER_PAGE) == USABLE_TX_BD_PER_PAGE)
-		hw_tx_cons++;
-
+	hw_tx_cons = sc->hw_tx_cons = bce_get_hw_tx_cons(sc);
 	sw_tx_cons = sc->tx_cons;
 
 	/* Prevent speculative reads from getting ahead of the status block. */
@@ -4957,9 +4990,7 @@ bce_tx_intr(struct bce_softc *sc)
 		sw_tx_cons = NEXT_TX_BD(sw_tx_cons);
 
 		/* Refresh hw_cons to see if there's new work. */
-		hw_tx_cons = sc->hw_tx_cons = sblk->status_tx_quick_consumer_index0;
-		if ((hw_tx_cons & USABLE_TX_BD_PER_PAGE) == USABLE_TX_BD_PER_PAGE)
-			hw_tx_cons++;
+		hw_tx_cons = sc->hw_tx_cons = bce_get_hw_tx_cons(sc);
 
 		/* Prevent speculative reads from getting ahead of the status block. */
 		bus_space_barrier(sc->bce_btag, sc->bce_bhandle, 0, 0, 
@@ -5366,7 +5397,7 @@ bce_tx_encap_skip_tso:
 #endif
 
 	DBPRINT(sc, BCE_INFO_SEND,
-		"%s(): Start: prod = 0x%04X, chain_prod = %04X, "
+		"%s(start): prod = 0x%04X, chain_prod = 0x%04X, "
 		"prod_bseq = 0x%08X\n",
 		__FUNCTION__, prod, chain_prod, prod_bseq);
 
@@ -5398,7 +5429,7 @@ bce_tx_encap_skip_tso:
 	DBRUNMSG(BCE_EXCESSIVE_SEND, bce_dump_tx_chain(sc, debug_prod, nsegs));
 
 	DBPRINT(sc, BCE_INFO_SEND,
-		"%s(): End: prod = 0x%04X, chain_prod = %04X, "
+		"%s( end ): prod = 0x%04X, chain_prod = 0x%04X, "
 		"prod_bseq = 0x%08X\n",
 		__FUNCTION__, prod, chain_prod, prod_bseq);
 
@@ -5444,22 +5475,28 @@ bce_start_locked(struct ifnet *ifp)
 	int count = 0;
 	u16 tx_prod, tx_chain_prod;
 
-	/* If there's no link or the transmit queue is empty then just exit. */
-	if (!sc->bce_link || IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
-		DBPRINT(sc, BCE_INFO_SEND, "%s(): No link or transmit queue empty.\n", 
-			__FUNCTION__);
-		goto bce_start_locked_exit;
-	}
-
 	/* prod points to the next free tx_bd. */
 	tx_prod = sc->tx_prod;
 	tx_chain_prod = TX_CHAIN_IDX(tx_prod);
 
 	DBPRINT(sc, BCE_INFO_SEND,
-		"%s(): Start: tx_prod = 0x%04X, tx_chain_prod = %04X, "
+		"%s(enter): tx_prod = 0x%04X, tx_chain_prod = 0x%04X, "
 		"tx_prod_bseq = 0x%08X\n",
 		__FUNCTION__, tx_prod, tx_chain_prod, sc->tx_prod_bseq);
 
+	/* If there's no link or the transmit queue is empty then just exit. */
+	if (!sc->bce_link) {
+		DBPRINT(sc, BCE_INFO_SEND, "%s(): No link.\n", 
+			__FUNCTION__);
+		goto bce_start_locked_exit;
+	}
+
+	if (IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
+		DBPRINT(sc, BCE_INFO_SEND, "%s(): Transmit queue empty.\n", 
+			__FUNCTION__);
+		goto bce_start_locked_exit;
+	}
+
 	/*
 	 * Keep adding entries while there is space in the ring.
 	 */
@@ -5502,11 +5539,6 @@ bce_start_locked(struct ifnet *ifp)
 	/* Update the driver's counters. */
 	tx_chain_prod = TX_CHAIN_IDX(sc->tx_prod);
 
-	DBPRINT(sc, BCE_INFO_SEND,
-		"%s(): End: tx_prod = 0x%04X, tx_chain_prod = 0x%04X, "
-		"tx_prod_bseq = 0x%08X\n",
-		__FUNCTION__, tx_prod, tx_chain_prod, sc->tx_prod_bseq);
-
 	/* Start the transmit. */
 	REG_WR16(sc, MB_TX_CID_ADDR + BCE_L2CTX_TX_HOST_BIDX, sc->tx_prod);
 	REG_WR(sc, MB_TX_CID_ADDR + BCE_L2CTX_TX_HOST_BSEQ, sc->tx_prod_bseq);
@@ -5515,6 +5547,11 @@ bce_start_locked(struct ifnet *ifp)
 	sc->watchdog_timer = BCE_TX_TIMEOUT;
 
 bce_start_locked_exit:
+	DBPRINT(sc, BCE_INFO_SEND,
+		"%s(exit ): tx_prod = 0x%04X, tx_chain_prod = 0x%04X, "
+		"tx_prod_bseq = 0x%08X\n",
+		__FUNCTION__, tx_prod, tx_chain_prod, sc->tx_prod_bseq);
+
 	return;
 }
 
@@ -5776,68 +5813,6 @@ bce_watchdog(struct bce_softc *sc)
 }
 
 
-#ifdef DEVICE_POLLING
-static void
-bce_poll_locked(struct ifnet *ifp, enum poll_cmd cmd, int count)
-{
-	struct bce_softc *sc = ifp->if_softc;
-
-	BCE_LOCK_ASSERT(sc);
-
-	sc->bce_rxcycles = count;
-
-	bus_dmamap_sync(sc->status_tag, sc->status_map,
-	    BUS_DMASYNC_POSTWRITE);
-
-	/* Check for any completed RX frames. */
-	if (sc->status_block->status_rx_quick_consumer_index0 != 
-		sc->hw_rx_cons)
-		bce_rx_intr(sc);
-
-	/* Check for any completed TX frames. */
-	if (sc->status_block->status_tx_quick_consumer_index0 != 
-		sc->hw_tx_cons)
-		bce_tx_intr(sc);
-
-	/* Check for new frames to transmit. */
-	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
-		bce_start_locked(ifp);
-
-}
-
-
-static void
-bce_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
-{
-	struct bce_softc *sc = ifp->if_softc;
-
-	BCE_LOCK(sc);
-	if (ifp->if_drv_flags & IFF_DRV_RUNNING)
-		bce_poll_locked(ifp, cmd, count);
-	BCE_UNLOCK(sc);
-}
-#endif /* DEVICE_POLLING */
-
-
-#if 0
-static inline int
-bce_has_work(struct bce_softc *sc)
-{
-	struct status_block *stat = sc->status_block;
-
-	if ((stat->status_rx_quick_consumer_index0 != sc->hw_rx_cons) ||
-	    (stat->status_tx_quick_consumer_index0 != sc->hw_tx_cons))
-		return 1;
-
-	if (((stat->status_attn_bits & STATUS_ATTN_BITS_LINK_STATE) != 0) !=
-	    bp->link_up)
-		return 1;
-
-	return 0;
-}
-#endif
-
-
 /*
  * Interrupt handler.
  */
@@ -5855,6 +5830,7 @@ bce_intr(void *xsc)
 	struct bce_softc *sc;
 	struct ifnet *ifp;
 	u32 status_attn_bits;
+	u16 hw_rx_cons, hw_tx_cons;
 
 	sc = xsc;
 	ifp = sc->bce_ifp;
@@ -5864,13 +5840,6 @@ bce_intr(void *xsc)
 
 	DBRUN(sc->interrupts_generated++);
 
-#ifdef DEVICE_POLLING
-	if (ifp->if_capenable & IFCAP_POLLING) {
-		DBPRINT(sc, BCE_INFO_MISC, "Polling enabled!\n");
-		goto bce_intr_exit;
-	}
-#endif
-
 	bus_dmamap_sync(sc->status_tag, sc->status_map,
 	    BUS_DMASYNC_POSTWRITE);
 
@@ -5889,6 +5858,9 @@ bce_intr(void *xsc)
 		BCE_PCICFG_INT_ACK_CMD_USE_INT_HC_PARAM |
 		BCE_PCICFG_INT_ACK_CMD_MASK_INT);
 
+	hw_rx_cons = bce_get_hw_rx_cons(sc);
+	hw_tx_cons = bce_get_hw_tx_cons(sc);
+
 	/* Keep processing data as long as there is work to do. */
 	for (;;) {
 
@@ -5922,12 +5894,22 @@ bce_intr(void *xsc)
 		}
 
 		/* Check for any completed RX frames. */
-		if (sc->status_block->status_rx_quick_consumer_index0 != sc->hw_rx_cons)
+		if (hw_rx_cons != sc->hw_rx_cons) {
+			DBPRINT(sc, BCE_INFO_RECV, "%s(): hw_rx_cons = 0x%04X, "
+				"sc->hw_rx_cons = 0x%04X\n", __FUNCTION__, 
+				hw_rx_cons, sc->hw_rx_cons);
+
 			bce_rx_intr(sc);
+		}
 
 		/* Check for any completed TX frames. */
-		if (sc->status_block->status_tx_quick_consumer_index0 != sc->hw_tx_cons)
+		if (hw_tx_cons != sc->hw_tx_cons) {
+			DBPRINT(sc, BCE_INFO_SEND, "%s(): hw_tx_cons = 0x%04X, "
+				"sc->hw_tx_cons = 0x%04X\n", __FUNCTION__, 
+				hw_tx_cons, sc->hw_tx_cons);
+
 			bce_tx_intr(sc);
+		}
 
 		/* Save the status block index value for use during the next interrupt. */
 		sc->last_status_idx = sc->status_block->status_idx;
@@ -5937,8 +5919,10 @@ bce_intr(void *xsc)
 			BUS_SPACE_BARRIER_READ);
 
 		/* If there's no work left then exit the interrupt service routine. */
-		if ((sc->status_block->status_rx_quick_consumer_index0 == sc->hw_rx_cons) &&
-	    	(sc->status_block->status_tx_quick_consumer_index0 == sc->hw_tx_cons))
+		hw_rx_cons = bce_get_hw_rx_cons(sc);
+		hw_tx_cons = bce_get_hw_tx_cons(sc);
+
+		if ((hw_rx_cons == sc->hw_rx_cons) && (hw_tx_cons == sc->hw_tx_cons))
 			break;
 	
 	}
@@ -6314,6 +6298,7 @@ bce_tick(void *xsc)
 	struct bce_softc *sc = xsc;
 	struct mii_data *mii;
 	struct ifnet *ifp;
+	u16 hw_rx_cons, hw_tx_cons;
 
 	ifp = sc->bce_ifp;
 
@@ -6332,6 +6317,20 @@ bce_tick(void *xsc)
 	/* Check that chip hasn't hung. */
 	bce_watchdog(sc);
 
+	hw_rx_cons = bce_get_hw_rx_cons(sc);
+	if (hw_rx_cons != sc->hw_rx_cons) {
+		DBPRINT(sc, BCE_INFO_RECV, "%s(): status_block->rx_cons0 = 0x%04X, "
+			"sc->hw_rx_cons = 0x%04X\n", __FUNCTION__, 
+			hw_rx_cons, sc->hw_rx_cons);
+	}
+
+	hw_tx_cons = bce_get_hw_tx_cons(sc);
+	if (hw_tx_cons != sc->hw_tx_cons) {
+		DBPRINT(sc, BCE_INFO_SEND, "%s(): status_block->tx_cons0 = 0x%04X, "
+			"sc->hw_tx_cons = 0x%04X\n", __FUNCTION__, 
+			hw_tx_cons, sc->hw_tx_cons);
+	}
+
 	/* If link is up already up then we're done. */
 	if (sc->bce_link)
 		goto bce_tick_locked_exit;
@@ -6347,6 +6346,7 @@ bce_tick(void *xsc)
 		    IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_SX) &&
 		    bootverbose)
 			BCE_PRINTF("Gigabit link up\n");
+
 		/* Now that link is up, handle any outstanding TX traffic. */
 		if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
 			bce_start_locked(ifp);
@@ -6693,17 +6693,17 @@ bce_add_sysctls(struct bce_softc *sc)
 		0, "Number of TX interrupts");
 
 	SYSCTL_ADD_ULONG(ctx, children, OID_AUTO, 
-		"rx_intr_time",
-		CTLFLAG_RD, &sc->rx_intr_time,
-		"RX interrupt time");
-
-	SYSCTL_ADD_ULONG(ctx, children, OID_AUTO, 
 		"tx_intr_time",
 		CTLFLAG_RD, &sc->tx_intr_time,
 		"TX interrupt time");
 
 #endif 
 
+	SYSCTL_ADD_ULONG(ctx, children, OID_AUTO, 
+		"rx_intr_time",
+		CTLFLAG_RD, &sc->rx_intr_time,
+		"RX interrupt time");
+
 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, 
 		"mbuf_alloc_failed",
 		CTLFLAG_RD, &sc->mbuf_alloc_failed,

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