diff options
author | ian <ian@FreeBSD.org> | 2013-12-14 01:35:57 +0000 |
---|---|---|
committer | ian <ian@FreeBSD.org> | 2013-12-14 01:35:57 +0000 |
commit | 38c5c4197ff5036bf7bcfb68e8195d653fd036ca (patch) | |
tree | f2e309d739271768137c4701861a5b44a7959e6e /sys/arm/at91 | |
parent | 6af39cbc397fecce9523f7c04f6dfd42b62c8bfb (diff) | |
download | FreeBSD-src-38c5c4197ff5036bf7bcfb68e8195d653fd036ca.zip FreeBSD-src-38c5c4197ff5036bf7bcfb68e8195d653fd036ca.tar.gz |
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.
Diffstat (limited to 'sys/arm/at91')
-rw-r--r-- | sys/arm/at91/if_ate.c | 37 |
1 files changed, 23 insertions, 14 deletions
diff --git a/sys/arm/at91/if_ate.c b/sys/arm/at91/if_ate.c index ea3d791..91b5738 100644 --- a/sys/arm/at91/if_ate.c +++ b/sys/arm/at91/if_ate.c @@ -947,10 +947,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); } } @@ -974,16 +972,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); } @@ -1118,12 +1114,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); @@ -1146,6 +1140,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], |