Hardware Checksum support on NetBSD-current
ハードウェアで補助されたチェックサム計算
FreeBSDのmanを読んだらこんなん乗ってたので、NetBSDではどうよ?と思ったら、3.0のアナウンスに
gem(4) and hme(4) now have hardware checksum support.
とか書いてあった。
manのどこに書いてあるのかは良く分からんけど。
で、gem(4)のコードを読んでみたら、
gem_attach()に
/* * The GEM hardware supports basic TCP checksum offloading only. * Several (all?) revisions (Sun rev. 01 and Apple rev. 00 and 80) * have bugs in the receive checksum, so don't enable it for now. if ((GEM_IS_SUN(sc) && sc->sc_chiprev != 1) || (GEM_IS_APPLE(sc) && (sc->sc_chiprev != 0 && sc->sc_chiprev != 0x80))) ifp->if_capabilities |= IFCAP_CSUM_TCPv4_Rx; */ ifp->if_capabilities |= IFCAP_CSUM_TCPv4_Tx;
gem_start()に
#ifdef INET /* h/w checksum */ if (ifp->if_csum_flags_tx & M_CSUM_TCPv4 && m0->m_pkthdr.csum_flags & M_CSUM_TCPv4) { struct ether_header *eh; uint16_t offset, start; eh = mtod(m0, struct ether_header *); switch (ntohs(eh->ether_type)) { case ETHERTYPE_IP: start = ETHER_HDR_LEN; break; case ETHERTYPE_VLAN: start = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; break; default: /* unsupported, drop it */ m_free(m0); continue; } start += M_CSUM_DATA_IPv4_IPHL(m0->m_pkthdr.csum_data); offset = M_CSUM_DATA_IPv4_OFFSET(m0->m_pkthdr.csum_data) + start; flags |= (start << GEM_TD_CXSUM_STARTSHFT) | (offset << GEM_TD_CXSUM_STUFFSHFT) | GEM_TD_CXSUM_ENABLE; } #endif
gem_rint()に
#ifdef INET /* hardware checksum */ if (ifp->if_csum_flags_rx & M_CSUM_TCPv4) { struct ether_header *eh; struct ip *ip; int32_t hlen, pktlen; if (sc->sc_ethercom.ec_capenable & ETHERCAP_VLAN_MTU) { pktlen = m->m_pkthdr.len - ETHER_HDR_LEN - ETHER_VLAN_ENCAP_LEN; eh = (struct ether_header *) (mtod(m, char *) + ETHER_VLAN_ENCAP_LEN); } else { pktlen = m->m_pkthdr.len - ETHER_HDR_LEN; eh = mtod(m, struct ether_header *); } if (ntohs(eh->ether_type) != ETHERTYPE_IP) goto swcsum; ip = (struct ip *) ((char *)eh + ETHER_HDR_LEN); /* IPv4 only */ if (ip->ip_v != IPVERSION) goto swcsum; hlen = ip->ip_hl << 2; if (hlen < sizeof(struct ip)) goto swcsum; /* * bail if too short, has random trailing garbage, * truncated, fragment, or has ethernet pad. */ if ((ntohs(ip->ip_len) < hlen) || (ntohs(ip->ip_len) != pktlen) || (ntohs(ip->ip_off) & (IP_MF | IP_OFFMASK))) goto swcsum; switch (ip->ip_p) { case IPPROTO_TCP: if (! (ifp->if_csum_flags_rx & M_CSUM_TCPv4)) goto swcsum; if (pktlen < (hlen + sizeof(struct tcphdr))) goto swcsum; m->m_pkthdr.csum_flags = M_CSUM_TCPv4; break; case IPPROTO_UDP: /* FALLTHROUGH */ default: goto swcsum; } /* the uncomplemented sum is expected */ m->m_pkthdr.csum_data = (~rxstat) & GEM_RD_CHECKSUM; /* if the pkt had ip options, we have to deduct them */ if (hlen > sizeof(struct ip)) { uint16_t *opts; uint32_t optsum, temp; optsum = 0; temp = hlen - sizeof(struct ip); opts = (uint16_t *) ((char *) ip + sizeof(struct ip)); while (temp > 1) { optsum += ntohs(*opts++); temp -= 2; } while (optsum >> 16) optsum = (optsum >> 16) + (optsum & 0xffff); /* Deduct ip opts sum from hwsum (rfc 1624). */ m->m_pkthdr.csum_data = ~((~m->m_pkthdr.csum_data) - ~optsum); while (m->m_pkthdr.csum_data >> 16) m->m_pkthdr.csum_data = (m->m_pkthdr.csum_data >> 16) + (m->m_pkthdr.csum_data & 0xffff); } m->m_pkthdr.csum_flags |= M_CSUM_DATA | M_CSUM_NO_PSEUDOHDR; } else swcsum: m->m_pkthdr.csum_flags = 0; #endif
とかいうよーなコードが入ってた。
多分これを真似すれば他のデバイスでも同じ事ができるであらう。
re(4) and wm(4) now support TCPv4 hardware transmit segment offload.
というのもどうなっているのかちょっと気になる。
#というあたりはどこを見れば書いてあるですか???
manでifnetとかやったら何もないし、ぐぐってもみあたんないし・・・