tech-net archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: hme(4) hardware RX checksum
christos%zoulas.com@localhost wrote:
> | Now I notice gem.c has the similar code and hme.c needs to
> | have one more `else' for swcsum cases. Which is better?
>
> should be fixed too I guess.
Ah, gem.c has the else correctly.
> | --- hme.c 16 Dec 2008 22:35:31 -0000 1.68
> | +++ hme.c 6 Mar 2009 17:45:55 -0000
> | @@ -836,7 +836,7 @@
> | }
> |
> | m0->m_pkthdr.csum_flags |= M_CSUM_DATA | M_CSUM_NO_PSEUDOHDR;
> | - }
> | + } else
> | swcsum:
> | m0->m_pkthdr.csum_flags = 0;
> | #endif
>
> right the else needs to be there.
Committed.
> | BTW, does hardware checksum work even for VLAN packets?
>
> It must, because there is vlan stuff inside a hw capabilities if statement
> (I am just guessing).
It is not ETHERCAP_VLAN_HWTAGGING but ETHERCAP_VLAN_MTU
and hme(4) doesn't have HW capabilities for VLAN tags.
So once vlanif is configured on the hme(4) ETHERCAP_VLAN_MTU
is always set and all RX packets are treated as VLAN encapsulized.
In that case ETHERTYPE is not decoded correctly so all IP packets
are swcsum'ed.
> | At least it looks wrong to check sc_ethercom.ec_capenable
> | without checking eh->ether_type == ETHERTYPE_VLAN.
>
> You are suggesting:
>
> if (eh->ether_type == ETHERTYPE_VLAN &&
> sc->sc_ethercom.ec_capenable & ETHERCAP_VLAN_MTU) {
> ?
hme(4) seems to provide a dumb checksum for data after the IP header,
so I'm not sure if it can provide proper checksum even for
ETHERTYPE_VLAN packets. With a quick test, VLAN packets are
also swcsum'ed.
Currently hme(4) and gem(4) have the similar code which decodes
RX packet headers to adjust (deducting IP option sum etc.)
a dumb checksum data provided by hardware.
fxp(4) (i82559) also has the similar RX checksum hardware so
I wonder how we can share a common function to decode RX packets
for such dumb RX M_CSUM_DATA checksums.
---
:
} else if ((sc->sc_flags & FXPF_82559_RXCSUM) != 0) {
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
struct ether_header *eh;
struct ip *ip;
struct udphdr *uh;
u_int hlen, pktlen;
uint32_t optsum, optlen;
uint16_t *opts;
if (len < ETHER_HDR_LEN + sizeof(struct ip))
goto out;
pktlen = len - ETHER_HDR_LEN;
eh = mtod(m, struct ether_header *);
if (ntohs(eh->ether_type) != ETHERTYPE_IP)
goto out;
ip = (struct ip *)((uint8_t *)eh + ETHER_HDR_LEN);
if (ip->ip_v != IPVERSION)
goto out;
hlen = ip->ip_hl << 2;
if (hlen < sizeof(struct ip))
goto out;
/*
* 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)) != 0)
goto out;
switch (ip->ip_p) {
case IPPROTO_TCP:
if ((ifp->if_csum_flags_rx & M_CSUM_TCPv4) == 0 ||
pktlen < (hlen + sizeof(struct tcphdr)))
goto out;
csum_flags =
M_CSUM_TCPv4 | M_CSUM_DATA | M_CSUM_NO_PSEUDOHDR;
break;
case IPPROTO_UDP:
if ((ifp->if_csum_flags_rx & M_CSUM_UDPv4) == 0 ||
pktlen < (hlen + sizeof(struct udphdr)))
goto out;
uh = (struct udphdr *)((uint8_t *)ip + hlen);
if (uh->uh_sum == 0)
goto out; /* no checksum */
csum_flags =
M_CSUM_UDPv4 | M_CSUM_DATA | M_CSUM_NO_PSEUDOHDR;
break;
default:
goto out;
}
/* Extract computed checksum. */
csum_data = be16dec(mtod(m, uint8_t *) + len);
/* If the packet had IP options, we have to deduct them. */
optlen = hlen - sizeof(struct ip);
if (optlen > 0) {
optsum = 0;
opts = (uint16_t *)((uint8_t *)ip + sizeof(struct ip));
while (optlen > 1) {
optsum += ntohs(*opts++);
optlen -= sizeof(uint16_t);
}
while (optsum >> 16)
optsum = (optsum >> 16) + (optsum & 0xffff);
/* Deduct the IP opts sum from the hwsum (RFC 1624). */
csum_data = ~(~csum_data - ~optsum);
while (csum_data >> 16)
csum_data =
(csum_data >> 16) + (csum_data & 0xffff);
}
}
out:
m->m_pkthdr.csum_flags = csum_flags;
m->m_pkthdr.csum_data = csum_data;
---
Izumi Tsutsui
Home |
Main Index |
Thread Index |
Old Index