diff options
author | Reyk Floeter <reyk@cvs.openbsd.org> | 2006-12-12 10:24:39 +0000 |
---|---|---|
committer | Reyk Floeter <reyk@cvs.openbsd.org> | 2006-12-12 10:24:39 +0000 |
commit | e2fd5f0218b82d1a430b0389a32ff36ab5b6f660 (patch) | |
tree | 856814ceeedea9424879ace0a35b4e575baa691c | |
parent | 62f81511589288ad78d3eaa3a7c12e4d540e3deb (diff) |
add a workaround for hardware ip4csum-tx bug on re(4) chips. the
hardware checksum could be mangled with 28 byte or less IP packets.
>From tsutsui@NetBSD
Tested by otto@ brad@ and Han Boetes <han at mijncomputer dot nl>
Tested by maja@ with the 8139C+ chipset
Tested on amd64/i386/sparc64
ok mglocker@ brad@ otto@
-rw-r--r-- | sys/dev/ic/re.c | 87 | ||||
-rw-r--r-- | sys/dev/ic/rtl81x9reg.h | 20 |
2 files changed, 77 insertions, 30 deletions
diff --git a/sys/dev/ic/re.c b/sys/dev/ic/re.c index 85fb00a2ca3..77b00da2ea0 100644 --- a/sys/dev/ic/re.c +++ b/sys/dev/ic/re.c @@ -1,4 +1,4 @@ -/* $OpenBSD: re.c,v 1.57 2006/12/01 01:13:01 todd Exp $ */ +/* $OpenBSD: re.c,v 1.58 2006/12/12 10:24:37 reyk Exp $ */ /* $FreeBSD: if_re.c,v 1.31 2004/09/04 07:54:05 ru Exp $ */ /* * Copyright (c) 1997, 1998-2003 @@ -154,6 +154,8 @@ int redebug = 0; #define DPRINTF(x) if (redebug) printf x +inline void re_set_bufaddr(struct rl_desc *, bus_addr_t); + int re_encap(struct rl_softc *, struct mbuf *, int *); int re_newbuf(struct rl_softc *, int, struct mbuf *); @@ -224,6 +226,16 @@ static const struct re_revision { }; +inline void +re_set_bufaddr(struct rl_desc *d, bus_addr_t addr) +{ + d->rl_bufaddr_lo = htole32((uint32_t)addr); + if (sizeof(bus_addr_t) == sizeof(uint64_t)) + d->rl_bufaddr_hi = htole32((uint64_t)addr >> 32); + else + d->rl_bufaddr_hi = 0; +} + /* * Send a read command and address to the EEPROM, check for ACK. */ @@ -915,7 +927,7 @@ re_attach(struct rl_softc *sc, const char *intrstr) } /* Allocate DMA'able memory for the RX ring */ - if ((error = bus_dmamem_alloc(sc->sc_dmat, RL_RX_LIST_SZ, + if ((error = bus_dmamem_alloc(sc->sc_dmat, RL_RX_DMAMEM_SZ, RL_RING_ALIGN, 0, &sc->rl_ldata.rl_rx_listseg, 1, &sc->rl_ldata.rl_rx_listnseg, BUS_DMA_NOWAIT)) != 0) { printf("%s: can't allocate rx listnseg, error = %d\n", @@ -925,7 +937,7 @@ re_attach(struct rl_softc *sc, const char *intrstr) /* Load the map for the RX ring. */ if ((error = bus_dmamem_map(sc->sc_dmat, &sc->rl_ldata.rl_rx_listseg, - sc->rl_ldata.rl_rx_listnseg, RL_RX_LIST_SZ, + sc->rl_ldata.rl_rx_listnseg, RL_RX_DMAMEM_SZ, (caddr_t *)&sc->rl_ldata.rl_rx_list, BUS_DMA_COHERENT | BUS_DMA_NOWAIT)) != 0) { printf("%s: can't map rx list, error = %d\n", @@ -933,10 +945,10 @@ re_attach(struct rl_softc *sc, const char *intrstr) goto fail_5; } - memset(sc->rl_ldata.rl_rx_list, 0, RL_RX_LIST_SZ); + memset(sc->rl_ldata.rl_rx_list, 0, RL_RX_DMAMEM_SZ); - if ((error = bus_dmamap_create(sc->sc_dmat, RL_RX_LIST_SZ, 1, - RL_RX_LIST_SZ, 0, 0, + if ((error = bus_dmamap_create(sc->sc_dmat, RL_RX_DMAMEM_SZ, 1, + RL_RX_DMAMEM_SZ, 0, 0, &sc->rl_ldata.rl_rx_list_map)) != 0) { printf("%s: can't create rx list map, error = %d\n", sc->sc_dev.dv_xname, error); @@ -945,7 +957,7 @@ re_attach(struct rl_softc *sc, const char *intrstr) if ((error = bus_dmamap_load(sc->sc_dmat, sc->rl_ldata.rl_rx_list_map, sc->rl_ldata.rl_rx_list, - RL_RX_LIST_SZ, NULL, BUS_DMA_NOWAIT)) != 0) { + RL_RX_DMAMEM_SZ, NULL, BUS_DMA_NOWAIT)) != 0) { printf("%s: can't load rx list, error = %d\n", sc->sc_dev.dv_xname, error); goto fail_7; @@ -975,12 +987,8 @@ re_attach(struct rl_softc *sc, const char *intrstr) IFQ_SET_MAXLEN(&ifp->if_snd, RL_TX_QLEN); IFQ_SET_READY(&ifp->if_snd); - ifp->if_capabilities = IFCAP_VLAN_MTU; - -#ifdef RE_CSUM_OFFLOAD - ifp->if_capabilities |= IFCAP_CSUM_IPv4|IFCAP_CSUM_TCPv4| - IFCAP_CSUM_UDPv4; -#endif + ifp->if_capabilities = IFCAP_VLAN_MTU | IFCAP_CSUM_IPv4 | + IFCAP_CSUM_TCPv4 | IFCAP_CSUM_UDPv4; #ifdef RE_VLAN ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING; @@ -1046,7 +1054,7 @@ fail_7: bus_dmamap_destroy(sc->sc_dmat, sc->rl_ldata.rl_rx_list_map); fail_6: bus_dmamem_unmap(sc->sc_dmat, - (caddr_t)sc->rl_ldata.rl_rx_list, RL_RX_LIST_SZ); + (caddr_t)sc->rl_ldata.rl_rx_list, RL_RX_DMAMEM_SZ); fail_5: bus_dmamem_free(sc->sc_dmat, &sc->rl_ldata.rl_rx_listseg, sc->rl_ldata.rl_rx_listnseg); @@ -1140,8 +1148,7 @@ re_newbuf(struct rl_softc *sc, int idx, struct mbuf *m) cmdstat = map->dm_segs[0].ds_len; if (idx == (RL_RX_DESC_CNT - 1)) cmdstat |= RL_RDESC_CMD_EOR; - d->rl_bufaddr_lo = htole32(RL_ADDR_LO(map->dm_segs[0].ds_addr)); - d->rl_bufaddr_hi = htole32(RL_ADDR_HI(map->dm_segs[0].ds_addr)); + re_set_bufaddr(d, map->dm_segs[0].ds_addr); d->rl_cmdstat = htole32(cmdstat); RL_RXDESCSYNC(sc, idx, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); cmdstat |= RL_RDESC_CMD_OWN; @@ -1398,7 +1405,7 @@ re_txeof(struct rl_softc *sc) if (txstat & RL_TDESC_CMD_OWN) break; - sc->rl_ldata.rl_tx_free += txq->txq_dmamap->dm_nsegs; + sc->rl_ldata.rl_tx_free += txq->txq_nsegs; KASSERT(sc->rl_ldata.rl_tx_free <= RL_TX_DESC_CNT(sc)); bus_dmamap_sync(sc->sc_dmat, txq->txq_dmamap, 0, txq->txq_dmamap->dm_mapsize, BUS_DMASYNC_POSTWRITE); @@ -1530,7 +1537,7 @@ int re_encap(struct rl_softc *sc, struct mbuf *m, int *idx) { bus_dmamap_t map; - int error, seg, uidx, startidx, curidx, lastidx; + int error, seg, nsegs, uidx, startidx, curidx, lastidx, pad; #ifdef RE_VLAN struct m_tag *mtag; #endif @@ -1541,7 +1548,6 @@ re_encap(struct rl_softc *sc, struct mbuf *m, int *idx) if (sc->rl_ldata.rl_tx_free <= RL_NTXDESC_RSVD) return (EFBIG); -#ifdef RE_CSUM_OFFLOAD /* * Set up checksum offload. Note: checksum offload bits must * appear in all descriptors of a multi-descriptor transmit @@ -1563,7 +1569,6 @@ re_encap(struct rl_softc *sc, struct mbuf *m, int *idx) if (m->m_pkthdr.csum_flags & M_UDPV4_CSUM_OUT) rl_flags |= RL_TDESC_CMD_UDPCSUM; } -#endif txq = &sc->rl_ldata.rl_txq[*idx]; map = txq->txq_dmamap; @@ -1576,7 +1581,15 @@ re_encap(struct rl_softc *sc, struct mbuf *m, int *idx) return (error); } - if (map->dm_nsegs > sc->rl_ldata.rl_tx_free - RL_NTXDESC_RSVD) { + nsegs = map->dm_nsegs; + pad = 0; + if (m->m_pkthdr.len <= RL_IP4CSUMTX_PADLEN && + (rl_flags & RL_TDESC_CMD_IPCSUM) != 0) { + pad = 1; + nsegs++; + } + + if (nsegs > sc->rl_ldata.rl_tx_free - RL_NTXDESC_RSVD) { error = EFBIG; goto fail_unload; } @@ -1622,24 +1635,39 @@ re_encap(struct rl_softc *sc, struct mbuf *m, int *idx) goto fail_unload; } - cmdstat = map->dm_segs[seg].ds_len; + re_set_bufaddr(d, map->dm_segs[seg].ds_addr); + cmdstat = rl_flags | map->dm_segs[seg].ds_len; if (seg == 0) cmdstat |= RL_TDESC_CMD_SOF; else cmdstat |= RL_TDESC_CMD_OWN; - if (seg == map->dm_nsegs - 1) { + if (curidx == (RL_TX_DESC_CNT(sc) - 1)) + cmdstat |= RL_TDESC_CMD_EOR; + if (seg == nsegs - 1) { cmdstat |= RL_TDESC_CMD_EOF; lastidx = curidx; } + d->rl_cmdstat = htole32(cmdstat); + RL_TXDESCSYNC(sc, curidx, + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); + } + if (pad) { + bus_addr_t paddaddr; + + printf("%s: re_encap: padding\n", sc->sc_dev.dv_xname); + d = &sc->rl_ldata.rl_tx_list[curidx]; + paddaddr = RL_TXPADDADDR(sc); + re_set_bufaddr(d, paddaddr); + cmdstat = rl_flags | + RL_TDESC_CMD_OWN | RL_TDESC_CMD_EOF | + (RL_IP4CSUMTX_PADLEN + 1 - m->m_pkthdr.len); if (curidx == (RL_TX_DESC_CNT(sc) - 1)) cmdstat |= RL_TDESC_CMD_EOR; - d->rl_cmdstat = htole32(cmdstat | rl_flags); - d->rl_bufaddr_lo = - htole32(RL_ADDR_LO(map->dm_segs[seg].ds_addr)); - d->rl_bufaddr_hi = - htole32(RL_ADDR_HI(map->dm_segs[seg].ds_addr)); + d->rl_cmdstat = htole32(cmdstat); RL_TXDESCSYNC(sc, curidx, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); + lastidx = curidx; + curidx = RL_NEXT_TX_DESC(sc, curidx); } KASSERT(lastidx != -1); @@ -1666,8 +1694,9 @@ re_encap(struct rl_softc *sc, struct mbuf *m, int *idx) /* update info of TX queue and descriptors */ txq->txq_mbuf = m; txq->txq_descidx = lastidx; + txq->txq_nsegs = nsegs; - sc->rl_ldata.rl_tx_free -= map->dm_nsegs; + sc->rl_ldata.rl_tx_free -= nsegs; sc->rl_ldata.rl_tx_nextfree = curidx; *idx = RL_NEXT_TXQ(sc, *idx); diff --git a/sys/dev/ic/rtl81x9reg.h b/sys/dev/ic/rtl81x9reg.h index 1a0634161c1..b01be4b1d1b 100644 --- a/sys/dev/ic/rtl81x9reg.h +++ b/sys/dev/ic/rtl81x9reg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: rtl81x9reg.h,v 1.35 2006/12/01 01:13:01 todd Exp $ */ +/* $OpenBSD: rtl81x9reg.h,v 1.36 2006/12/12 10:24:38 reyk Exp $ */ /* * Copyright (c) 1997, 1998 @@ -684,6 +684,7 @@ struct rl_txq { struct mbuf *txq_mbuf; bus_dmamap_t txq_dmamap; int txq_descidx; + int txq_nsegs; }; struct rl_list_data { @@ -739,6 +740,23 @@ struct rl_softc { int rl_link; }; +/* + * re(4) hardware ip4csum-tx could be mangled with 28 byte or less IP packets + */ +#define RL_IP4CSUMTX_MINLEN 28 +#define RL_IP4CSUMTX_PADLEN (ETHER_HDR_LEN + RL_IP4CSUMTX_MINLEN) +/* + * XXX + * We are allocating pad DMA buffer after RX DMA descs for now + * because RL_TX_LIST_SZ(sc) always occupies whole page but + * RL_RX_LIST_SZ is less than PAGE_SIZE so there is some unused region. + */ +#define RL_RX_DMAMEM_SZ (RL_RX_LIST_SZ + RL_IP4CSUMTX_PADLEN) +#define RL_TXPADOFF RL_RX_LIST_SZ +#define RL_TXPADDADDR(sc) \ + ((sc)->rl_ldata.rl_rx_list_map->dm_segs[0].ds_addr + RL_TXPADOFF) + + #define RL_ATTACHED 0x00000001 /* attach has succeeded */ #define RL_ENABLED 0x00000002 /* chip is enabled */ #define RL_IS_ENABLED(sc) ((sc)->sc_flags & RL_ENABLED) |