diff options
author | Jason Wright <jason@cvs.openbsd.org> | 2003-10-13 21:22:20 +0000 |
---|---|---|
committer | Jason Wright <jason@cvs.openbsd.org> | 2003-10-13 21:22:20 +0000 |
commit | 552ce22bff6d7387d25695b520b7725734c612af (patch) | |
tree | 02f32b674b6fc598acb204e083959f69a7afd22b | |
parent | ad8a74c2855b5cbe7c418d22a1a4f702e9626f00 (diff) |
rx checksum offload support (based on earlier code from aaron@, but with
fixes by me). Note: sk v1 chips can sometimes compute an incorrect cksum.
To deal with that, we never set the "bad" flags. Ie. if the card says it's
good, we assume it's good. If the card says it's bad, we fall back to
software. Ugly? You betcha.
-rw-r--r-- | sys/dev/pci/if_sk.c | 115 |
1 files changed, 107 insertions, 8 deletions
diff --git a/sys/dev/pci/if_sk.c b/sys/dev/pci/if_sk.c index 822964ffed3..c08f245deca 100644 --- a/sys/dev/pci/if_sk.c +++ b/sys/dev/pci/if_sk.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_sk.c,v 1.33 2003/08/12 05:23:06 nate Exp $ */ +/* $OpenBSD: if_sk.c,v 1.34 2003/10/13 21:22:19 jason Exp $ */ /* * Copyright (c) 1997, 1998, 1999, 2000 @@ -107,10 +107,13 @@ #include <netinet/in_systm.h> #include <netinet/in_var.h> #include <netinet/ip.h> +#include <netinet/udp.h> +#include <netinet/tcp.h> #include <netinet/if_ether.h> #endif #include <net/if_media.h> +#include <net/if_vlan_var.h> #if NBPFILTER > 0 #include <net/bpf.h> @@ -158,8 +161,7 @@ int sk_newbuf(struct sk_if_softc *, int, struct mbuf *, bus_dmamap_t); int sk_init_rx_ring(struct sk_if_softc *); int sk_init_tx_ring(struct sk_if_softc *); u_int8_t sk_vpd_readbyte(struct sk_softc *, int); -void sk_vpd_read_res(struct sk_softc *, - struct vpd_res *, int); +void sk_vpd_read_res(struct sk_softc *, struct vpd_res *, int); void sk_vpd_read(struct sk_softc *); int sk_xmac_miibus_readreg(struct device *, int, int); @@ -174,6 +176,7 @@ u_int32_t sk_calchash(caddr_t); void sk_setfilt(struct sk_if_softc *, caddr_t, int); void sk_setmulti(struct sk_if_softc *); void sk_tick(void *); +void sk_rxcsum(struct ifnet *, struct mbuf *, const u_int16_t, const u_int16_t); #ifdef SK_DEBUG #define DPRINTF(x) if (skdebug) printf x @@ -652,6 +655,9 @@ sk_init_rx_ring(struct sk_if_softc *sc_if) cd->sk_rx_chain[i].sk_next = &cd->sk_rx_chain[i + 1]; rd->sk_rx_ring[i].sk_next = SK_RX_RING_ADDR(sc_if,i+1); } + rd->sk_rx_ring[i].sk_csum1_start = ETHER_HDR_LEN; + rd->sk_rx_ring[i].sk_csum2_start = ETHER_HDR_LEN + + sizeof(struct ip); } for (i = 0; i < SK_RX_RING_CNT; i++) { @@ -1262,11 +1268,6 @@ skc_attach(struct device *parent, struct device *self, void *aux) * Map control/status registers. */ command = pci_conf_read(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); - command |= PCI_COMMAND_IO_ENABLE | - PCI_COMMAND_MEM_ENABLE | - PCI_COMMAND_MASTER_ENABLE; - pci_conf_write(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, command); - command = pci_conf_read(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); switch (PCI_PRODUCT(pa->pa_id)) { case PCI_PRODUCT_SCHNEIDERKOCH_GE: @@ -1589,6 +1590,7 @@ sk_rxeof(struct sk_if_softc *sc_if) int i, cur, total_len = 0; u_int32_t rxstat; bus_dmamap_t dmamap; + u_int16_t csum1, csum2; DPRINTFN(2, ("sk_rxeof\n")); @@ -1607,6 +1609,9 @@ sk_rxeof(struct sk_if_softc *sc_if) dmamap = sc_if->sk_cdata.sk_rx_map[cur]; sc_if->sk_cdata.sk_rx_map[cur] = 0; + csum1 = sc_if->sk_rdata->sk_rx_ring[i].sk_csum1; + csum2 = sc_if->sk_rdata->sk_rx_ring[i].sk_csum2; + SK_INC(i, SK_RX_RING_CNT); if (rxstat & XM_RXSTAT_ERRFRAME) { @@ -1643,10 +1648,13 @@ sk_rxeof(struct sk_if_softc *sc_if) ifp->if_ipackets++; + sk_rxcsum(ifp, m, csum1, csum2); + #if NBPFILTER > 0 if (ifp->if_bpf) bpf_mtap(ifp->if_bpf, m); #endif + /* pass it on. */ ether_input_mbuf(ifp, m); } @@ -1655,6 +1663,97 @@ sk_rxeof(struct sk_if_softc *sc_if) } void +sk_rxcsum(struct ifnet *ifp, struct mbuf *m, const u_int16_t csum1, const u_int16_t csum2) +{ + struct ether_header *eh; + struct ip *ip; + u_int8_t *pp; + int hlen, len, plen; + u_int16_t iph_csum, ipo_csum, ipd_csum, csum; + + pp = mtod(m, u_int8_t *); + plen = m->m_pkthdr.len; + if (plen < sizeof(*eh)) + return; + eh = (struct ether_header *)pp; + iph_csum = in_cksum_addword(csum1, (~csum2 & 0xffff)); + + if (eh->ether_type == htons(ETHERTYPE_VLAN)) { + u_int16_t *xp = (u_int16_t *)pp; + + xp = (u_int16_t *)pp; + if (xp[1] != htons(ETHERTYPE_IP)) + return; + iph_csum = in_cksum_addword(iph_csum, (~xp[0] & 0xffff)); + iph_csum = in_cksum_addword(iph_csum, (~xp[1] & 0xffff)); + xp = (u_int16_t *)(pp + sizeof(struct ip)); + iph_csum = in_cksum_addword(iph_csum, xp[0]); + iph_csum = in_cksum_addword(iph_csum, xp[1]); + pp += EVL_ENCAPLEN; + } else if (eh->ether_type != htons(ETHERTYPE_IP)) + return; + + pp += sizeof(*eh); + plen -= sizeof(*eh); + + ip = (struct ip *)pp; + + if (ip->ip_v != IPVERSION) + return; + + hlen = ip->ip_hl << 2; + if (hlen < sizeof(struct ip)) + return; + if (hlen > ntohs(ip->ip_len)) + return; + + /* Don't deal with truncated or padded packets. */ + if (plen != ntohs(ip->ip_len)) + return; + + len = hlen - sizeof(struct ip); + if (len > 0) { + u_int16_t *p; + + p = (u_int16_t *)(ip + 1); + ipo_csum = 0; + for (ipo_csum = 0; len > 0; len -= sizeof(*p), p++) + ipo_csum = in_cksum_addword(ipo_csum, *p); + iph_csum = in_cksum_addword(iph_csum, ipo_csum); + ipd_csum = in_cksum_addword(csum2, (~ipo_csum & 0xffff)); + } else + ipd_csum = csum2; + + if (iph_csum != 0xffff) { + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m); + return; + } + m->m_pkthdr.csum |= M_IPV4_CSUM_IN_OK; + + if (ip->ip_off & htons(IP_MF | IP_OFFMASK)) + return; /* ip frag, we're done for now */ + + pp += hlen; + + /* Only know checksum protocol for udp/tcp */ + if (ip->ip_p == IPPROTO_UDP) { + struct udphdr *uh = (struct udphdr *)pp; + + if (uh->uh_sum == 0) /* udp with no checksum */ + return; + } else if (ip->ip_p != IPPROTO_TCP) + return; + + csum = in_cksum_phdr(ip->ip_src.s_addr, ip->ip_dst.s_addr, + htonl(ntohs(ip->ip_len) - hlen + ip->ip_p) + ipd_csum); + if (csum == 0xffff) { + m->m_pkthdr.csum |= (ip->ip_p == IPPROTO_TCP) ? + M_TCP_CSUM_IN_OK : M_UDP_CSUM_IN_OK; + } +} + +void sk_txeof(struct sk_if_softc *sc_if) { struct sk_softc *sc = sc_if->sk_softc; |