From d298d90df17c5440f9cad84d4ce98a7e2ae60221 Mon Sep 17 00:00:00 2001 From: imp Date: Sun, 22 Dec 2013 22:24:17 +0000 Subject: [PATCH] Merge from stable/10 r259381: MFC r259212, r259220: Fix one race and one fence post error. When the TX buffer was completely full, we'd not complete any of the mbufs due to the fence post error (this creates a large leak). When this is fixed, we still leak, but at a much smaller rate due to a race between ateintr and atestart_locked as well as an asymmetry where atestart_locked is called from elsewhere. Ensure that we free in-flight packets that have completed there as well. Also remove needless check for NULL on mb, checked earlier in the loop and simplify a redundant if. Approved by: re@ (gjb@) git-svn-id: svn://svn.freebsd.org/base/releng/10.0@259747 ccf9f872-aa2e-dd11-9fc8-001c23d0bc1f --- sys/arm/at91/if_ate.c | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/sys/arm/at91/if_ate.c b/sys/arm/at91/if_ate.c index d7c10194..698ac79c 100644 --- a/sys/arm/at91/if_ate.c +++ b/sys/arm/at91/if_ate.c @@ -946,10 +946,8 @@ ate_intr(void *xsc) } while (!done); - if (mb != NULL) { - ifp->if_ipackets++; - (*ifp->if_input)(ifp, mb); - } + ifp->if_ipackets++; + (*ifp->if_input)(ifp, mb); } } @@ -973,16 +971,14 @@ ate_intr(void *xsc) sc->tx_descs[sc->txtail + 1].status |= ETHB_TX_USED; } - while (sc->txtail != sc->txhead && - sc->tx_descs[sc->txtail].status & ETHB_TX_USED ) { - + while ((sc->tx_descs[sc->txtail].status & ETHB_TX_USED) && + sc->sent_mbuf[sc->txtail] != NULL) { bus_dmamap_sync(sc->mtag, sc->tx_map[sc->txtail], BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->mtag, sc->tx_map[sc->txtail]); m_freem(sc->sent_mbuf[sc->txtail]); sc->tx_descs[sc->txtail].addr = 0; sc->sent_mbuf[sc->txtail] = NULL; - ifp->if_opackets++; sc->txtail = NEXT_TX_IDX(sc, sc->txtail); } @@ -1117,12 +1113,10 @@ atestart_locked(struct ifnet *ifp) * xmit packets. We use OACTIVE to indicate "we can stuff more * into our buffers (clear) or not (set)." */ - if (!sc->is_emacb) { - /* RM9200 has only two hardware entries */ - if (!sc->is_emacb && (RD4(sc, ETH_TSR) & ETH_TSR_BNQ) == 0) { - ifp->if_drv_flags |= IFF_DRV_OACTIVE; - return; - } + /* RM9200 has only two hardware entries */ + if (!sc->is_emacb && (RD4(sc, ETH_TSR) & ETH_TSR_BNQ) == 0) { + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + return; } IFQ_DRV_DEQUEUE(&ifp->if_snd, m); @@ -1145,6 +1139,21 @@ atestart_locked(struct ifnet *ifp) m_freem(m); continue; } + + /* + * There's a small race between the loop in ate_intr finishing + * and the check above to see if the packet was finished, as well + * as when atestart gets called via other paths. Lose the race + * gracefully and free the mbuf... + */ + if (sc->sent_mbuf[sc->txhead] != NULL) { + bus_dmamap_sync(sc->mtag, sc->tx_map[sc->txtail], + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->mtag, sc->tx_map[sc->txtail]); + m_free(sc->sent_mbuf[sc->txhead]); + ifp->if_opackets++; + } + sc->sent_mbuf[sc->txhead] = m; bus_dmamap_sync(sc->mtag, sc->tx_map[sc->txhead], -- 2.42.0