diff options
| author | bouyer <bouyer@NetBSD.org> | 2005-05-16 15:56:38 +0000 |
|---|---|---|
| committer | bouyer <bouyer@NetBSD.org> | 2005-05-16 15:56:38 +0000 |
| commit | 78b08b48da786b29b5fd1dee71342dcbe1e02f68 (patch) | |
| tree | a51cff8ec9d491f5fc26b3ecf3b4b50ffb60574a /sys/dev | |
| parent | 04f2acc1f349364d68c14f88436ef762ba642527 (diff) | |
Pad small packets with a static buffer at the end of the S/G list.
Avoids leaking kernel memory when small packets are transmitted.
Tested on a ibook G4.
Diffstat (limited to 'sys/dev')
| -rw-r--r-- | sys/dev/ic/gem.c | 101 | ||||
| -rw-r--r-- | sys/dev/ic/gemvar.h | 4 |
2 files changed, 79 insertions, 26 deletions
diff --git a/sys/dev/ic/gem.c b/sys/dev/ic/gem.c index 16515fb1fb5..3566360efdb 100644 --- a/sys/dev/ic/gem.c +++ b/sys/dev/ic/gem.c @@ -1,4 +1,4 @@ -/* $NetBSD: gem.c,v 1.39 2005/05/02 15:34:31 yamt Exp $ */ +/* $NetBSD: gem.c,v 1.40 2005/05/16 15:56:38 bouyer Exp $ */ /* * @@ -34,7 +34,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: gem.c,v 1.39 2005/05/02 15:34:31 yamt Exp $"); +__KERNEL_RCSID(0, "$NetBSD: gem.c,v 1.40 2005/05/16 15:56:38 bouyer Exp $"); #include "opt_inet.h" #include "bpfilter.h" @@ -128,6 +128,8 @@ void gem_power(int, void *); #define DPRINTF(sc, x) /* nothing */ #endif +#define ETHER_MIN_TX (ETHERMIN + sizeof(struct ether_header)) + /* * gem_attach: @@ -145,6 +147,7 @@ gem_attach(sc, enaddr) struct ifmedia_entry *ifm; int i, error; u_int32_t v; + char *nullbuf; /* Make sure the chip is stopped. */ ifp->if_softc = sc; @@ -152,11 +155,12 @@ gem_attach(sc, enaddr) /* * Allocate the control data structures, and create and load the - * DMA map for it. + * DMA map for it. gem_control_data is 9216 bytes, we have space for + * the padding buffer in the bus_dmamem_alloc()'d memory. */ if ((error = bus_dmamem_alloc(sc->sc_dmatag, - sizeof(struct gem_control_data), PAGE_SIZE, 0, &sc->sc_cdseg, - 1, &sc->sc_cdnseg, 0)) != 0) { + sizeof(struct gem_control_data) + ETHER_MIN_TX, PAGE_SIZE, + 0, &sc->sc_cdseg, 1, &sc->sc_cdnseg, 0)) != 0) { aprint_error( "%s: unable to allocate control data, error = %d\n", sc->sc_dev.dv_xname, error); @@ -172,6 +176,9 @@ gem_attach(sc, enaddr) goto fail_1; } + nullbuf = + (caddr_t)sc->sc_control_data + sizeof(struct gem_control_data); + if ((error = bus_dmamap_create(sc->sc_dmatag, sizeof(struct gem_control_data), 1, sizeof(struct gem_control_data), 0, 0, &sc->sc_cddmamap)) != 0) { @@ -189,6 +196,25 @@ gem_attach(sc, enaddr) goto fail_3; } + memset(nullbuf, 0, ETHER_MIN_TX); + if ((error = bus_dmamap_create(sc->sc_dmatag, + ETHER_MIN_TX, 1, ETHER_MIN_TX, 0, 0, &sc->sc_nulldmamap)) != 0) { + aprint_error("%s: unable to create padding DMA map, " + "error = %d\n", sc->sc_dev.dv_xname, error); + goto fail_4; + } + + if ((error = bus_dmamap_load(sc->sc_dmatag, sc->sc_nulldmamap, + nullbuf, ETHER_MIN_TX, NULL, 0)) != 0) { + aprint_error( + "%s: unable to load padding DMA map, error = %d\n", + sc->sc_dev.dv_xname, error); + goto fail_5; + } + + bus_dmamap_sync(sc->sc_dmatag, sc->sc_nulldmamap, 0, ETHER_MIN_TX, + BUS_DMASYNC_PREWRITE); + /* * Initialize the transmit job descriptors. */ @@ -209,7 +235,7 @@ gem_attach(sc, enaddr) &txs->txs_dmamap)) != 0) { aprint_error("%s: unable to create tx DMA map %d, " "error = %d\n", sc->sc_dev.dv_xname, i, error); - goto fail_4; + goto fail_6; } SIMPLEQ_INSERT_TAIL(&sc->sc_txfreeq, txs, txs_q); } @@ -222,7 +248,7 @@ gem_attach(sc, enaddr) MCLBYTES, 0, 0, &sc->sc_rxsoft[i].rxs_dmamap)) != 0) { aprint_error("%s: unable to create rx DMA map %d, " "error = %d\n", sc->sc_dev.dv_xname, i, error); - goto fail_5; + goto fail_7; } sc->sc_rxsoft[i].rxs_mbuf = NULL; } @@ -416,19 +442,23 @@ gem_attach(sc, enaddr) * Free any resources we've allocated during the failed attach * attempt. Do this in reverse order and fall through. */ - fail_5: + fail_7: for (i = 0; i < GEM_NRXDESC; i++) { if (sc->sc_rxsoft[i].rxs_dmamap != NULL) bus_dmamap_destroy(sc->sc_dmatag, sc->sc_rxsoft[i].rxs_dmamap); } - fail_4: + fail_6: for (i = 0; i < GEM_TXQUEUELEN; i++) { if (sc->sc_txsoft[i].txs_dmamap != NULL) bus_dmamap_destroy(sc->sc_dmatag, sc->sc_txsoft[i].txs_dmamap); } bus_dmamap_unload(sc->sc_dmatag, sc->sc_cddmamap); + fail_5: + bus_dmamap_destroy(sc->sc_dmatag, sc->sc_nulldmamap); + fail_4: + bus_dmamem_unmap(sc->sc_dmatag, (caddr_t)nullbuf, ETHER_MIN_TX); fail_3: bus_dmamap_destroy(sc->sc_dmatag, sc->sc_cddmamap); fail_2: @@ -1002,6 +1032,7 @@ gem_start(ifp) struct gem_txsoft *txs, *last_txs; bus_dmamap_t dmamap; int error, firsttx, nexttx, lasttx = -1, ofree, seg; + uint64_t flags = 0; if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) return; @@ -1040,7 +1071,9 @@ gem_start(ifp) * again. */ if (bus_dmamap_load_mbuf(sc->sc_dmatag, dmamap, m0, - BUS_DMA_WRITE|BUS_DMA_NOWAIT) != 0) { + BUS_DMA_WRITE|BUS_DMA_NOWAIT) != 0 || + (m0->m_pkthdr.len < ETHER_MIN_TX && + dmamap->dm_nsegs == GEM_NTXSEGS)) { if (m0->m_pkthdr.len > MCLBYTES) { printf("%s: unable to allocate jumbo Tx " "cluster\n", sc->sc_dev.dv_xname); @@ -1079,7 +1112,8 @@ gem_start(ifp) * Ensure we have enough descriptors free to describe * the packet. */ - if (dmamap->dm_nsegs > sc->sc_txfree) { + if (dmamap->dm_nsegs > ((m0->m_pkthdr.len < ETHER_MIN_TX) ? + (sc->sc_txfree - 1) : sc->sc_txfree)) { /* * Not enough free descriptors to transmit this * packet. We haven't committed to anything yet, @@ -1117,7 +1151,6 @@ gem_start(ifp) for (nexttx = sc->sc_txnext, seg = 0; seg < dmamap->dm_nsegs; seg++, nexttx = GEM_NEXTTX(nexttx)) { - uint64_t flags; /* * If this is the first descriptor we're @@ -1169,14 +1202,42 @@ gem_start(ifp) } if (seg == dmamap->dm_nsegs - 1) { flags |= GEM_TD_END_OF_PACKET; + } else { + /* last flag set outside of loop */ + sc->sc_txdescs[nexttx].gd_flags = + GEM_DMA_WRITE(sc, flags); } - sc->sc_txdescs[nexttx].gd_flags = - GEM_DMA_WRITE(sc, flags); lasttx = nexttx; } + if (m0->m_pkthdr.len < ETHER_MIN_TX) { + /* add padding buffer at end of chain */ + flags &= ~GEM_TD_END_OF_PACKET; + sc->sc_txdescs[lasttx].gd_flags = + GEM_DMA_WRITE(sc, flags); + + sc->sc_txdescs[nexttx].gd_addr = + GEM_DMA_WRITE(sc, + sc->sc_nulldmamap->dm_segs[0].ds_addr); + flags = ((ETHER_MIN_TX - m0->m_pkthdr.len) & + GEM_TD_BUFSIZE) | GEM_TD_END_OF_PACKET; + lasttx = nexttx; + nexttx = GEM_NEXTTX(nexttx); + seg++; + } + sc->sc_txdescs[lasttx].gd_flags = GEM_DMA_WRITE(sc, flags); KASSERT(lasttx != -1); + /* + * Store a pointer to the packet so we can free it later, + * and remember what txdirty will be once the packet is + * done. + */ + txs->txs_mbuf = m0; + txs->txs_firstdesc = sc->sc_txnext; + txs->txs_lastdesc = lasttx; + txs->txs_ndescs = seg; + #ifdef GEM_DEBUG if (ifp->if_flags & IFF_DEBUG) { printf(" gem_start %p transmit chain:\n", txs); @@ -1196,18 +1257,8 @@ gem_start(ifp) GEM_CDTXSYNC(sc, sc->sc_txnext, dmamap->dm_nsegs, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); - /* - * Store a pointer to the packet so we can free it later, - * and remember what txdirty will be once the packet is - * done. - */ - txs->txs_mbuf = m0; - txs->txs_firstdesc = sc->sc_txnext; - txs->txs_lastdesc = lasttx; - txs->txs_ndescs = dmamap->dm_nsegs; - /* Advance the tx pointer. */ - sc->sc_txfree -= dmamap->dm_nsegs; + sc->sc_txfree -= txs->txs_ndescs; sc->sc_txnext = nexttx; SIMPLEQ_REMOVE_HEAD(&sc->sc_txfreeq, txs_q); diff --git a/sys/dev/ic/gemvar.h b/sys/dev/ic/gemvar.h index 4a5b6b9cdd4..a0b94c9e313 100644 --- a/sys/dev/ic/gemvar.h +++ b/sys/dev/ic/gemvar.h @@ -1,4 +1,4 @@ -/* $NetBSD: gemvar.h,v 1.11 2005/02/04 02:10:36 perry Exp $ */ +/* $NetBSD: gemvar.h,v 1.12 2005/05/16 15:56:38 bouyer Exp $ */ /* * @@ -150,6 +150,8 @@ struct gem_softc { bus_dmamap_t sc_cddmamap; /* control data DMA map */ #define sc_cddma sc_cddmamap->dm_segs[0].ds_addr + bus_dmamap_t sc_nulldmamap; /* for small packets padding */ + /* * Software state for transmit and receive descriptors. */ |
