diff options
author | Brad Smith <brad@cvs.openbsd.org> | 2005-02-22 20:44:27 +0000 |
---|---|---|
committer | Brad Smith <brad@cvs.openbsd.org> | 2005-02-22 20:44:27 +0000 |
commit | 0f8082a54ffcb01901d89e1a73a0aef934881777 (patch) | |
tree | 86fd3e1416e893b9421a547c54b398f3472e7884 /sys/arch | |
parent | b04cb25e5cf0717707dfa291827a44a7f88a0dcb (diff) |
add hme(4) RX TCP/UDP checksum offload
Based on a diff posted to sparc@ by Pyun YongHyeon
Diffstat (limited to 'sys/arch')
-rw-r--r-- | sys/arch/sparc/dev/hme.c | 124 | ||||
-rw-r--r-- | sys/arch/sparc/dev/hmereg.h | 3 |
2 files changed, 119 insertions, 8 deletions
diff --git a/sys/arch/sparc/dev/hme.c b/sys/arch/sparc/dev/hme.c index c54dd3c0cdb..fe6fde31be0 100644 --- a/sys/arch/sparc/dev/hme.c +++ b/sys/arch/sparc/dev/hme.c @@ -1,4 +1,4 @@ -/* $OpenBSD: hme.c,v 1.46 2005/01/15 05:24:10 brad Exp $ */ +/* $OpenBSD: hme.c,v 1.47 2005/02/22 20:44:26 brad Exp $ */ /* * Copyright (c) 1998 Jason L. Wright (jason@thought.net) @@ -62,6 +62,8 @@ #include <netinet/in_var.h> #include <netinet/ip.h> #include <netinet/if_ether.h> +#include <netinet/tcp.h> +#include <netinet/udp.h> #endif #if NBPFILTER > 0 @@ -100,11 +102,14 @@ int hme_tint(struct hme_softc *); int hme_mint(struct hme_softc *, u_int32_t); int hme_eint(struct hme_softc *, u_int32_t); +/* TCP/UDP checksum offloading support */ +void hme_rxcksum(struct mbuf *, u_int32_t); + void hme_reset_rx(struct hme_softc *); void hme_reset_tx(struct hme_softc *); -void hme_read(struct hme_softc *, int, int); -int hme_put(struct hme_softc *, int, struct mbuf *); +void hme_read(struct hme_softc *, int, int, u_int32_t); +int hme_put(struct hme_softc *, int, struct mbuf *); /* * ifmedia glue @@ -531,7 +536,7 @@ void hmeinit(sc) struct hme_softc *sc; { - u_int32_t c; + u_int32_t c, n; struct ifnet *ifp = &sc->sc_arpcom.ac_if; struct hme_tcvr *tcvr = sc->sc_tcvr; struct hme_cr *cr = sc->sc_cr; @@ -589,7 +594,11 @@ hmeinit(sc) txr->tx_rsize = (HME_TX_RING_SIZE >> TXR_RSIZE_SHIFT) - 1; txr->cfg |= TXR_CFG_DMAENABLE; - c = RXR_CFG_DMAENABLE | (HME_RX_OFFSET << 3) | (HME_RX_CSUMLOC << 16); + c = RXR_CFG_DMAENABLE | (HME_RX_OFFSET << 3); + /* RX TCP/UDP cksum offset */ + n = (ETHER_HDR_LEN + sizeof(struct ip)) / 2; + n = (n << RXR_CFG_CSUM_SHIFT) & RXR_CFG_CSUMSTART; + c |= n; #if HME_RX_RING_SIZE == 32 c |= RXR_CFG_RINGSIZE32; #elif HME_RX_RING_SIZE == 64 @@ -742,6 +751,105 @@ hme_tint(sc) return (1); } +/* + * XXX layering violation + * + * If we can have additional csum data member in 'struct pkthdr' for + * these incomplete checksum offload capable hardware, things would be + * much simpler. That member variable will carry partial checksum + * data and it may be evaluated in TCP/UDP input handler after + * computing pseudo header checksumming. + */ +void +hme_rxcksum(struct mbuf *m, u_int32_t flags) +{ + struct ether_header *eh; + struct ip *ip; + struct udphdr *uh; + int32_t hlen, len, pktlen; + u_int16_t cksum, flag_bad, flag_ok, *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; + flag_ok = M_TCP_CSUM_IN_OK; + flag_bad = M_TCP_CSUM_IN_BAD; + 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 */ + flag_ok = M_UDP_CSUM_IN_OK; + flag_bad = M_UDP_CSUM_IN_BAD; + break; + default: + return; + } + + cksum = ~(flags & HME_RXD_CSUM); + /* 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; + } + } + /* cksum fixup for pseudo-header, replace with in_cksum_phdr()? */ + 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 |= flag_bad; + else + m->m_pkthdr.csum |= flag_ok; +} + int hme_rint(sc) struct hme_softc *sc; @@ -762,7 +870,7 @@ hme_rint(sc) if (rxd.rx_flags & HME_RXD_OVERFLOW) ifp->if_ierrors++; else - hme_read(sc, bix, len); + hme_read(sc, bix, len, rxd.rx_flags); rxd.rx_flags = HME_RXD_OWN | ((HME_RX_PKT_BUF_SZ - HME_RX_OFFSET) << 16); @@ -857,9 +965,10 @@ hme_put(sc, idx, m) } void -hme_read(sc, idx, len) +hme_read(sc, idx, len, flags) struct hme_softc *sc; int idx, len; + u_int32_t flags; { struct ifnet *ifp = &sc->sc_arpcom.ac_if; struct mbuf *m; @@ -882,6 +991,7 @@ hme_read(sc, idx, len) m_adj(m, HME_RX_OFFSET); ifp->if_ipackets++; + hme_rxcksum(m, flags); #if NBPFILTER > 0 /* diff --git a/sys/arch/sparc/dev/hmereg.h b/sys/arch/sparc/dev/hmereg.h index e7d340624f2..11bfabe320b 100644 --- a/sys/arch/sparc/dev/hmereg.h +++ b/sys/arch/sparc/dev/hmereg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: hmereg.h,v 1.11 2004/09/28 00:21:23 brad Exp $ */ +/* $OpenBSD: hmereg.h,v 1.12 2005/02/22 20:44:26 brad Exp $ */ /* * Copyright (c) 1998 Jason L. Wright (jason@thought.net) @@ -195,6 +195,7 @@ struct hme_rxr { #define RXR_CFG_RINGSIZE256 0x00000600 /* rx descptr ring size: 128 */ #define RXR_CFG_reserved3 0x0000f800 /* reserved bits */ #define RXR_CFG_CSUMSTART 0x007f0000 /* rx offset of checksum */ +#define RXR_CFG_CSUM_SHIFT 16 /* * configuration registers |