svn commit: r212246 - user/nwhitehorn/ps3/powerpc/ps3

Nathan Whitehorn nwhitehorn at FreeBSD.org
Mon Sep 6 04:15:00 UTC 2010


Author: nwhitehorn
Date: Mon Sep  6 04:14:59 2010
New Revision: 212246
URL: http://svn.freebsd.org/changeset/base/212246

Log:
  Add a watchdog timer and correct a misunderstaning about busdma that was
  causing random bits of the ring buffer to be overwritten. The PS3's
  network interface now runs stably even at high transfer rates.

Modified:
  user/nwhitehorn/ps3/powerpc/ps3/if_glc.c
  user/nwhitehorn/ps3/powerpc/ps3/if_glcreg.h

Modified: user/nwhitehorn/ps3/powerpc/ps3/if_glc.c
==============================================================================
--- user/nwhitehorn/ps3/powerpc/ps3/if_glc.c	Mon Sep  6 03:00:54 2010	(r212245)
+++ user/nwhitehorn/ps3/powerpc/ps3/if_glc.c	Mon Sep  6 04:14:59 2010	(r212246)
@@ -70,6 +70,7 @@ static int	glc_encap(struct glc_softc *s
 		    bus_addr_t *pktdesc);
 static int	glc_intr_filter(void *xsc);
 static void	glc_intr(void *xsc);
+static void	glc_tick(void *xsc);
 
 static MALLOC_DEFINE(M_GLC, "gelic", "PS3 GELIC ethernet");
 
@@ -151,7 +152,9 @@ glc_attach(device_t dev) 
 
 	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
 	    MTX_DEF);
+	callout_init_mtx(&sc->sc_tick_ch, &sc->sc_mtx, 0);
 	sc->next_txdma_slot = 0;
+	sc->bsy_txdma_slots = 0;
 	sc->first_used_txdma_slot = -1;
 
 	/*
@@ -318,6 +321,7 @@ glc_init_locked(struct glc_softc *sc)
 {
 	int i;
 	struct glc_rxsoft *rxs;
+	struct glc_txsoft *txs;
 
 	mtx_assert(&sc->sc_mtx, MA_OWNED);
 
@@ -342,12 +346,22 @@ glc_init_locked(struct glc_softc *sc)
 		    BUS_DMASYNC_PREREAD);
 	}
 
+	txs = STAILQ_FIRST(&sc->sc_txdirtyq);
+	if (txs != NULL) {
+		lv1_net_start_tx_dma(sc->sc_bus, sc->sc_dev,
+		    glc_map_addr(sc, sc->sc_txdmadesc_phys +
+		    txs->txs_firstdesc*sizeof(struct glc_dmadesc)), 0);
+	}
+
 	lv1_net_start_rx_dma(sc->sc_bus, sc->sc_dev,
 	    sc->sc_rxsoft[0].rxs_desc, 0);
 
 	sc->sc_ifp->if_drv_flags |= IFF_DRV_RUNNING;
 	sc->sc_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
 	sc->sc_ifpflags = sc->sc_ifp->if_flags;
+
+	sc->sc_wdog_timer = 0;
+	callout_reset(&sc->sc_tick_ch, hz, glc_tick, sc);
 }
 
 static void
@@ -361,6 +375,24 @@ glc_init(void *xsc)
 }
 
 static void
+glc_tick(void *xsc)
+{
+	struct glc_softc *sc = xsc;
+
+	mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+	if (sc->sc_wdog_timer == 0 || --sc->sc_wdog_timer != 0) {
+		callout_reset(&sc->sc_tick_ch, hz, glc_tick, sc);
+		return;
+	}
+
+	/* Problems */
+	device_printf(sc->sc_self, "device timeout\n");
+
+	glc_init_locked(sc);
+}
+
+static void
 glc_start_locked(struct ifnet *ifp)
 {
 	struct glc_softc *sc = ifp->if_softc;
@@ -371,6 +403,10 @@ glc_start_locked(struct ifnet *ifp)
 	mtx_assert(&sc->sc_mtx, MA_OWNED);
 	first = 0;
 
+	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
+	    IFF_DRV_RUNNING)
+		return;
+
 	if (STAILQ_EMPTY(&sc->sc_txdirtyq))
 		kickstart = 1;
 
@@ -380,15 +416,21 @@ glc_start_locked(struct ifnet *ifp)
 		if (mb_head == NULL)
 			break;
 
+		/* Check if the ring buffer is full */
+		if (sc->bsy_txdma_slots > 125) {
+			/* Put the packet back and stop */
+			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+			IFQ_DRV_PREPEND(&ifp->if_snd, mb_head);
+			break;
+		}
+
 		BPF_MTAP(ifp, mb_head);
 
 		if (sc->sc_tx_vlan >= 0)
 			mb_head = ether_vlanencap(mb_head, sc->sc_tx_vlan);
 
 		if (glc_encap(sc, &mb_head, &pktdesc)) {
-			/* Put the packet back and stop */
 			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
-			IFQ_DRV_PREPEND(&ifp->if_snd, mb_head);
 			break;
 		}
 
@@ -396,9 +438,6 @@ glc_start_locked(struct ifnet *ifp)
 			first = pktdesc;
 	}
 
-	bus_dmamap_sync(sc->sc_dmadesc_tag, sc->sc_txdmadesc_map,
-	    BUS_DMASYNC_PREREAD);
-
 	if (kickstart && first != 0) {
 		lv1_net_start_tx_dma(sc->sc_bus, sc->sc_dev, first, 0);
 		sc->sc_wdog_timer = 5;
@@ -516,50 +555,46 @@ glc_encap(struct glc_softc *sc, struct m
 	struct glc_txsoft *txs;
 	struct mbuf *m;
 	bus_addr_t firstslotphys;
-	int i, idx;
-	int nsegs = 16;
+	int i, idx, nsegs, nsegs_max;
 	int err = 0;
 
-	/* Check if the ring buffer is full */
-	if (sc->next_txdma_slot == sc->first_used_txdma_slot)
-		return (-1);
-
 	/* Max number of segments is the number of free DMA slots */
-	if (sc->next_txdma_slot >= sc->first_used_txdma_slot)
-		nsegs = 128 - sc->next_txdma_slot + sc->first_used_txdma_slot;
-	else
-		nsegs = sc->first_used_txdma_slot - sc->next_txdma_slot;
+	nsegs_max = 128 - sc->bsy_txdma_slots;
 
-	if (nsegs > 16)
-		nsegs = 16;
+	if (nsegs_max > 16 || sc->first_used_txdma_slot < 0)
+		nsegs_max = 16;
 
 	/* Get a work queue entry. */
 	if ((txs = STAILQ_FIRST(&sc->sc_txfreeq)) == NULL) {
 		/* Ran out of descriptors. */
 		return (ENOBUFS);
 	}
-	
-	err = bus_dmamap_load_mbuf_sg(sc->sc_txdma_tag, txs->txs_dmamap,
-	    *m_head, segs, &nsegs, BUS_DMA_NOWAIT);
 
-	if (err == EFBIG) {
-		m = m_collapse(*m_head, M_DONTWAIT, nsegs);
+	nsegs = 0;
+	for (m = *m_head; m != NULL; m = m->m_next)
+		nsegs++;
+
+	if (nsegs > nsegs_max) {
+		m = m_collapse(*m_head, M_DONTWAIT, nsegs_max);
 		if (m == NULL) {
 			m_freem(*m_head);
 			*m_head = NULL;
 			return (ENOBUFS);
 		}
 		*m_head = m;
-
-		err = bus_dmamap_load_mbuf_sg(sc->sc_txdma_tag,
-		    txs->txs_dmamap, *m_head, segs, &nsegs, BUS_DMA_NOWAIT);
-		if (err != 0) {
-			m_freem(*m_head);
-			*m_head = NULL;
-			return (err);
-		}
-	} else if (err != 0)
+	}
+	
+	err = bus_dmamap_load_mbuf_sg(sc->sc_txdma_tag, txs->txs_dmamap,
+	    *m_head, segs, &nsegs, BUS_DMA_NOWAIT);
+	if (err != 0) {
+		m_freem(*m_head);
+		*m_head = NULL;
 		return (err);
+	}
+
+	KASSERT(nsegs <= 128 - sc->bsy_txdma_slots,
+	    ("GLC: Mapped too many (%d) DMA segments with %d available",
+	    nsegs, 128 - sc->bsy_txdma_slots));
 
 	if (nsegs == 0) {
 		m_freem(*m_head);
@@ -594,21 +629,23 @@ glc_encap(struct glc_softc *sc, struct m
 		idx = (idx + 1) % GLC_MAX_TX_PACKETS;
 	}
 	sc->next_txdma_slot = idx;
-	idx = (txs->txs_firstdesc - 1) % GLC_MAX_TX_PACKETS;
-	sc->sc_txdmadesc[idx].next = firstslotphys;
+	sc->bsy_txdma_slots += nsegs;
+	if (txs->txs_firstdesc != 0)
+		idx = txs->txs_firstdesc - 1;
+	else
+		idx = GLC_MAX_TX_PACKETS - 1;
 
 	if (sc->first_used_txdma_slot < 0)
 		sc->first_used_txdma_slot = txs->txs_firstdesc;
 
 	bus_dmamap_sync(sc->sc_txdma_tag, txs->txs_dmamap,
 	    BUS_DMASYNC_PREWRITE);
+	sc->sc_txdmadesc[idx].next = firstslotphys;
 
 	STAILQ_REMOVE_HEAD(&sc->sc_txfreeq, txs_q);
 	STAILQ_INSERT_TAIL(&sc->sc_txdirtyq, txs, txs_q);
 	txs->txs_mbuf = *m_head;
-
-	if (pktdesc != NULL)
-		*pktdesc = firstslotphys;
+	*pktdesc = firstslotphys;
 
 	return (0);
 }
@@ -677,6 +714,7 @@ glc_txintr(struct glc_softc *sc)
 
 		STAILQ_REMOVE_HEAD(&sc->sc_txdirtyq, txs_q);
 		bus_dmamap_unload(sc->sc_txdma_tag, txs->txs_dmamap);
+		sc->bsy_txdma_slots -= txs->txs_ndescs;
 
 		if (txs->txs_mbuf != NULL) {
 			m_freem(txs->txs_mbuf);

Modified: user/nwhitehorn/ps3/powerpc/ps3/if_glcreg.h
==============================================================================
--- user/nwhitehorn/ps3/powerpc/ps3/if_glcreg.h	Mon Sep  6 03:00:54 2010	(r212245)
+++ user/nwhitehorn/ps3/powerpc/ps3/if_glcreg.h	Mon Sep  6 04:14:59 2010	(r212246)
@@ -84,7 +84,7 @@ struct glc_softc {
 	bus_dma_tag_t	sc_txdma_tag;
 	struct glc_txsoft sc_txsoft[GLC_MAX_TX_PACKETS];
 	struct glc_dmadesc *sc_txdmadesc;
-	int		next_txdma_slot, first_used_txdma_slot;
+	int		next_txdma_slot, first_used_txdma_slot, bsy_txdma_slots;
 	bus_dmamap_t	sc_txdmadesc_map;
 	bus_addr_t	sc_txdmadesc_phys;
 
@@ -102,6 +102,7 @@ struct glc_softc {
 
 	int		sc_bus, sc_dev;
 	int		sc_wdog_timer;
+	struct callout	sc_tick_ch;
 };
 
 #define GELIC_GET_MAC_ADDRESS   0x0001


More information about the svn-src-user mailing list