diff options
author | Brad Smith <brad@cvs.openbsd.org> | 2005-02-04 05:02:39 +0000 |
---|---|---|
committer | Brad Smith <brad@cvs.openbsd.org> | 2005-02-04 05:02:39 +0000 |
commit | 355734ac497c277d6e8a1fe6c1f3e5293efcb20a (patch) | |
tree | bc9f8d4eb319737ad050177b4b05b288db9b7bc2 /sys/dev/ic | |
parent | 21b1631149f2a33c5d9d7e87fc8a4b628cd5c367 (diff) |
add hme(4) RX TCP/UDP checksum offload
From a diff posted to sparc@ by Pyun YongHyeon
ok dlg@, tested by mickey@ "i get some badass improvement here." and
a few other users.
Diffstat (limited to 'sys/dev/ic')
-rw-r--r-- | sys/dev/ic/hme.c | 115 | ||||
-rw-r--r-- | sys/dev/ic/hmereg.h | 3 |
2 files changed, 114 insertions, 4 deletions
diff --git a/sys/dev/ic/hme.c b/sys/dev/ic/hme.c index 2f5e41f1602..5c082d70c02 100644 --- a/sys/dev/ic/hme.c +++ b/sys/dev/ic/hme.c @@ -1,4 +1,4 @@ -/* $OpenBSD: hme.c,v 1.34 2005/01/15 05:24:11 brad Exp $ */ +/* $OpenBSD: hme.c,v 1.35 2005/02/04 05:02:38 brad Exp $ */ /* $NetBSD: hme.c,v 1.21 2001/07/07 15:59:37 thorpej Exp $ */ /*- @@ -67,6 +67,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 @@ -112,6 +114,8 @@ void hme_mediastatus(struct ifnet *, struct ifmediareq *); int hme_eint(struct hme_softc *, u_int); int hme_rint(struct hme_softc *); int hme_tint(struct hme_softc *); +/* TCP/UDP checksum offloading support */ +void hme_rxcksum(struct mbuf *, u_int32_t); void hme_config(sc) @@ -481,7 +485,7 @@ hme_init(sc) bus_space_handle_t erx = sc->sc_erx; bus_space_handle_t mac = sc->sc_mac; u_int8_t *ea; - u_int32_t v; + u_int32_t v, n; /* * Initialization sequence. The numbered steps below correspond @@ -590,6 +594,10 @@ hme_init(sc) #endif /* Enable DMA */ v |= HME_ERX_CFG_DMAENABLE | (HME_RX_OFFSET << 3); + /* RX TCP/UDP cksum offset */ + n = (ETHER_HDR_LEN + sizeof(struct ip)) / 2; + n = (n << HME_ERX_CFG_CSUM_SHIFT) & HME_ERX_CFG_CSUMSTART; + v |= n; bus_space_write_4(t, erx, HME_ERXI_CFG, v); /* step 11. XIF Configuration */ @@ -727,6 +735,105 @@ hme_tint(sc) } /* + * 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_XD_RXCKSUM); + /* 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; +} + +/* * Receive interrupt. */ int @@ -769,6 +876,9 @@ hme_rint(sc) goto again; } + ifp->if_ipackets++; + hme_rxcksum(m, flags); + #if NBPFILTER > 0 if (ifp->if_bpf) { m->m_pkthdr.len = m->m_len = len; @@ -776,7 +886,6 @@ hme_rint(sc) } #endif - ifp->if_ipackets++; ether_input_mbuf(ifp, m); again: diff --git a/sys/dev/ic/hmereg.h b/sys/dev/ic/hmereg.h index c37e138adf2..22f270d6bb9 100644 --- a/sys/dev/ic/hmereg.h +++ b/sys/dev/ic/hmereg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: hmereg.h,v 1.7 2004/09/28 00:21:23 brad Exp $ */ +/* $OpenBSD: hmereg.h,v 1.8 2005/02/04 05:02:38 brad Exp $ */ /* $NetBSD: hmereg.h,v 1.8 2001/04/30 12:22:42 bouyer Exp $ */ /*- @@ -164,6 +164,7 @@ #define HME_ERX_CFG_RINGSIZE128 0x00000400 /* Descriptor ring size: 128 */ #define HME_ERX_CFG_RINGSIZE256 0x00000600 /* Descriptor ring size: 256 */ #define HME_ERX_CFG_CSUMSTART 0x007f0000 /* cksum offset */ +#define HME_ERX_CFG_CSUM_SHIFT 16 /* * HME MAC-core register offsets |