From 0383a360d1897e0c4940ac8a5264aa27a4c3a45b Mon Sep 17 00:00:00 2001 From: Mark Kettenis Date: Thu, 8 Aug 2013 16:01:35 +0000 Subject: Turns out neither the flow control diff nor the RX checksum offload diff is responsible for the hangs seen by Henning. So put them back. --- sys/dev/ic/gem.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 121 insertions(+), 8 deletions(-) (limited to 'sys/dev') diff --git a/sys/dev/ic/gem.c b/sys/dev/ic/gem.c index 2318840356c..514c6fb2476 100644 --- a/sys/dev/ic/gem.c +++ b/sys/dev/ic/gem.c @@ -1,4 +1,4 @@ -/* $OpenBSD: gem.c,v 1.100 2013/07/25 19:50:56 kettenis Exp $ */ +/* $OpenBSD: gem.c,v 1.101 2013/08/08 16:01:34 kettenis Exp $ */ /* $NetBSD: gem.c,v 1.1 2001/09/16 00:11:43 eeh Exp $ */ /* @@ -56,7 +56,11 @@ #ifdef INET #include +#include +#include #include +#include +#include #endif #if NBPFILTER > 0 @@ -116,6 +120,7 @@ int gem_eint(struct gem_softc *, u_int); int gem_rint(struct gem_softc *); int gem_tint(struct gem_softc *, u_int32_t); int gem_pint(struct gem_softc *); +void gem_rxcksum(struct mbuf *, u_int64_t); #ifdef GEM_DEBUG #define DPRINTF(sc, x) if ((sc)->sc_arpcom.ac_if.if_flags & IFF_DEBUG) \ @@ -133,7 +138,7 @@ gem_config(struct gem_softc *sc) struct ifnet *ifp = &sc->sc_arpcom.ac_if; struct mii_data *mii = &sc->sc_mii; struct mii_softc *child; - int i, error, phyad; + int i, error, mii_flags, phyad; struct ifmedia_entry *ifm; /* Make sure the chip is stopped. */ @@ -247,6 +252,8 @@ gem_config(struct gem_softc *sc) gem_mifinit(sc); + mii_flags = MIIF_DOPAUSE; + /* * Look for an external PHY. */ @@ -265,7 +272,7 @@ gem_config(struct gem_softc *sc) } mii_attach(&sc->sc_dev, mii, 0xffffffff, phyad, - MII_OFFSET_ANY, 0); + MII_OFFSET_ANY, mii_flags); } /* @@ -294,7 +301,7 @@ gem_config(struct gem_softc *sc) } mii_attach(&sc->sc_dev, mii, 0xffffffff, phyad, - MII_OFFSET_ANY, 0); + MII_OFFSET_ANY, mii_flags); } /* @@ -316,8 +323,10 @@ gem_config(struct gem_softc *sc) mii->mii_readreg = gem_pcs_readreg; mii->mii_writereg = gem_pcs_writereg; + mii_flags |= MIIF_NOISOLATE; + mii_attach(&sc->sc_dev, mii, 0xffffffff, MII_PHY_ANY, - MII_OFFSET_ANY, MIIF_NOISOLATE); + MII_OFFSET_ANY, mii_flags); } child = LIST_FIRST(&mii->mii_phys); @@ -802,7 +811,9 @@ gem_init(struct ifnet *ifp) /* Encode Receive Descriptor ring size: four possible values */ v = gem_ringsize(GEM_NRXDESC /*XXX*/); - + /* RX TCP/UDP checksum offset */ + v |= ((ETHER_HDR_LEN + sizeof(struct ip)) << + GEM_RX_CONFIG_CXM_START_SHFT); /* Enable DMA */ bus_space_write_4(t, h, GEM_RX_CONFIG, v|(GEM_THRSH_1024<sc_arpcom.ac_enaddr[0]<<8) | sc->sc_arpcom.ac_enaddr[1]); } +/* + * RX TCP/UDP checksum + */ +void +gem_rxcksum(struct mbuf *m, u_int64_t rxstat) +{ + struct ether_header *eh; + struct ip *ip; + struct udphdr *uh; + int32_t hlen, len, pktlen; + u_int16_t cksum, *opts; + u_int32_t temp32; + union pseudoh { + struct hdr { + u_int16_t len; + u_int8_t ttl; + u_int8_t proto; + u_int32_t src; + u_int32_t dst; + } h; + u_int16_t w[6]; + } ph; + + pktlen = m->m_pkthdr.len; + if (pktlen < sizeof(struct ether_header)) + return; + eh = mtod(m, struct ether_header *); + if (eh->ether_type != htons(ETHERTYPE_IP)) + return; + ip = (struct ip *)(eh + 1); + if (ip->ip_v != IPVERSION) + return; + + hlen = ip->ip_hl << 2; + pktlen -= sizeof(struct ether_header); + if (hlen < sizeof(struct ip)) + return; + if (ntohs(ip->ip_len) < hlen) + return; + if (ntohs(ip->ip_len) != pktlen) + return; + if (ip->ip_off & htons(IP_MF | IP_OFFMASK)) + return; /* can't handle fragmented packet */ + + switch (ip->ip_p) { + case IPPROTO_TCP: + if (pktlen < (hlen + sizeof(struct tcphdr))) + return; + break; + case IPPROTO_UDP: + if (pktlen < (hlen + sizeof(struct udphdr))) + return; + uh = (struct udphdr *)((caddr_t)ip + hlen); + if (uh->uh_sum == 0) + return; /* no checksum */ + break; + default: + return; + } + + cksum = htons(~(rxstat & GEM_RD_CHECKSUM)); + /* cksum fixup for IP options */ + len = hlen - sizeof(struct ip); + if (len > 0) { + opts = (u_int16_t *)(ip + 1); + for (; len > 0; len -= sizeof(u_int16_t), opts++) { + temp32 = cksum - *opts; + temp32 = (temp32 >> 16) + (temp32 & 65535); + cksum = temp32 & 65535; + } + } + + ph.h.len = htons(ntohs(ip->ip_len) - hlen); + ph.h.ttl = 0; + ph.h.proto = ip->ip_p; + ph.h.src = ip->ip_src.s_addr; + ph.h.dst = ip->ip_dst.s_addr; + temp32 = cksum; + opts = &ph.w[0]; + temp32 += opts[0] + opts[1] + opts[2] + opts[3] + opts[4] + opts[5]; + temp32 = (temp32 >> 16) + (temp32 & 65535); + temp32 += (temp32 >> 16); + cksum = ~temp32; + if (cksum == 0) { + m->m_pkthdr.csum_flags |= + M_TCP_CSUM_IN_OK | M_UDP_CSUM_IN_OK; + } +} + /* * Receive interrupt. */ @@ -1001,6 +1101,8 @@ gem_rint(struct gem_softc *sc) m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = len; + gem_rxcksum(m, rxstat); + #if NBPFILTER > 0 if (ifp->if_bpf) bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN); @@ -1361,6 +1463,17 @@ gem_mii_statchg(struct device *dev) v &= ~GEM_MAC_XIF_GMII_MODE; } bus_space_write_4(t, mac, GEM_MAC_XIF_CONFIG, v); + + /* + * 802.3x flow control + */ + v = bus_space_read_4(t, mac, GEM_MAC_CONTROL_CONFIG); + v &= ~(GEM_MAC_CC_RX_PAUSE | GEM_MAC_CC_TX_PAUSE); + if ((IFM_OPTIONS(sc->sc_mii.mii_media_active) & IFM_ETH_RXPAUSE) != 0) + v |= GEM_MAC_CC_RX_PAUSE; + if ((IFM_OPTIONS(sc->sc_mii.mii_media_active) & IFM_ETH_TXPAUSE) != 0) + v |= GEM_MAC_CC_TX_PAUSE; + bus_space_write_4(t, mac, GEM_MAC_CONTROL_CONFIG, v); } int -- cgit v1.2.3