Author: adrian
Date: Tue Mar 13 06:15:20 2012
New Revision: 232912
URL: http://svn.freebsd.org/changeset/base/232912

Log:
  Correctly (I hope) deallocate the if_arge RX buffer ring on arge_stop().
  
  I had some interesting hangs until I realised I should try flushing the
  DDR FIFO register and lo and behold, hangs stopped occuring.
  
  I've put in a few DDR flushes here and there in case people decide to
  reuse some of these functions.  It's very very likely they're almost
  all superflous.
  
  To test:
  
  * Connect to a network with a _lot_ of broadcast traffic
  * Do this:
    # while true; do ifconfig arge0 down; ifconfig arge0 up; done
  
  This fixes the mbuf exhaustion that has been reported when the interface
  state flaps up/down.

Modified:
  head/sys/mips/atheros/if_arge.c

Modified: head/sys/mips/atheros/if_arge.c
==============================================================================
--- head/sys/mips/atheros/if_arge.c     Tue Mar 13 05:21:14 2012        
(r232911)
+++ head/sys/mips/atheros/if_arge.c     Tue Mar 13 06:15:20 2012        
(r232912)
@@ -118,6 +118,7 @@ static int arge_probe(device_t);
 static void arge_reset_dma(struct arge_softc *);
 static int arge_resume(device_t);
 static int arge_rx_ring_init(struct arge_softc *);
+static void arge_rx_ring_free(struct arge_softc *sc);
 static int arge_tx_ring_init(struct arge_softc *);
 #ifdef DEVICE_POLLING
 static int arge_poll(struct ifnet *, enum poll_cmd, int);
@@ -807,6 +808,12 @@ arge_reset_dma(struct arge_softc *sc)
            DMA_RX_STATUS_BUS_ERROR | DMA_RX_STATUS_OVERFLOW);
        ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS,
            DMA_TX_STATUS_BUS_ERROR | DMA_TX_STATUS_UNDERRUN);
+
+       /*
+        * Force a DDR flush so any pending data is properly
+        * flushed to RAM before underlying buffers are freed.
+        */
+       arge_flush_ddr(sc);
 }
 
 
@@ -1083,6 +1090,10 @@ arge_stop(struct arge_softc *sc)
        ARGE_WRITE(sc, AR71XX_DMA_INTR, 0);
 
        arge_reset_dma(sc);
+
+       /* Flush FIFO and free any existing mbufs */
+       arge_flush_ddr(sc);
+       arge_rx_ring_free(sc);
 }
 
 
@@ -1531,6 +1542,12 @@ arge_rx_ring_init(struct arge_softc *sc)
        bzero(rd->arge_rx_ring, sizeof(rd->arge_rx_ring));
        for (i = 0; i < ARGE_RX_RING_COUNT; i++) {
                rxd = &sc->arge_cdata.arge_rxdesc[i];
+               if (rxd->rx_m != NULL) {
+                       device_printf(sc->arge_dev,
+                           "%s: ring[%d] rx_m wasn't free?\n",
+                           __func__,
+                           i);
+               }
                rxd->rx_m = NULL;
                rxd->desc = &rd->arge_rx_ring[i];
                if (i == ARGE_RX_RING_COUNT - 1)
@@ -1551,6 +1568,32 @@ arge_rx_ring_init(struct arge_softc *sc)
 }
 
 /*
+ * Free all the buffers in the RX ring.
+ *
+ * TODO: ensure that DMA is disabled and no pending DMA
+ * is lurking in the FIFO.
+ */
+static void
+arge_rx_ring_free(struct arge_softc *sc)
+{
+       int i;
+       struct arge_rxdesc      *rxd;
+
+       ARGE_LOCK_ASSERT(sc);
+
+       for (i = 0; i < ARGE_RX_RING_COUNT; i++) {
+               rxd = &sc->arge_cdata.arge_rxdesc[i];
+               /* Unmap the mbuf */
+               if (rxd->rx_m != NULL) {
+                       bus_dmamap_unload(sc->arge_cdata.arge_rx_tag,
+                           rxd->rx_dmamap);
+                       m_free(rxd->rx_m);
+                       rxd->rx_m = NULL;
+               }
+       }
+}
+
+/*
  * Initialize an RX descriptor and attach an MBUF cluster.
  */
 static int
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to