From b5898b804f2173d49d6f1a827ad8b0477e5d0229 Mon Sep 17 00:00:00 2001 From: Pyun YongHyeon Date: Wed, 11 Apr 2007 00:47:29 +0000 Subject: [PATCH] Add work around for hardware Tx checksum offload bug in Yukon II. Yukon II generated corrupted TCP checksum for short TCP packets that's less than 60 bytes in size(e.g. window probe packet, pure ACK packet etc). Padding the frame with zeros to make the frame minimum ethernet frame size didn't work at all. Instead of dropping Tx checksum offload support we calculate TCP checksum with S/W method when we encounter short TCP frames. Fortunately it seems that short UDP datagrams appear to be handled correctly by Yukon II. While I'm here simplify ethernet/VLAN header size calculation logic. PR: 111384 --- sys/dev/msk/if_msk.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/sys/dev/msk/if_msk.c b/sys/dev/msk/if_msk.c index a2bb2c72447d..57038e1d187e 100644 --- a/sys/dev/msk/if_msk.c +++ b/sys/dev/msk/if_msk.c @@ -131,6 +131,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include @@ -2647,7 +2648,6 @@ msk_encap(struct msk_if_softc *sc_if, struct mbuf **m_head) * hardware. However, TSO performance of Yukon II is very * good such that it's worth to implement it. */ - struct ether_vlan_header *evh; struct ether_header *eh; struct ip *ip; struct tcphdr *tcp; @@ -2669,17 +2669,37 @@ msk_encap(struct msk_if_softc *sc_if, struct mbuf **m_head) *m_head = NULL; return (ENOBUFS); } - evh = mtod(m, struct ether_vlan_header *); - ip = (struct ip *)(evh + 1); - } else - ip = (struct ip *)(eh + 1); + } m = m_pullup(m, offset + sizeof(struct ip)); if (m == NULL) { *m_head = NULL; return (ENOBUFS); } + ip = (struct ip *)(mtod(m, char *) + offset); offset += (ip->ip_hl << 2); tcp_offset = offset; + /* + * It seems that Yukon II has Tx checksum offload bug for + * small TCP packets that's less than 60 bytes in size + * (e.g. TCP window probe packet, pure ACK packet). + * Common work around like padding with zeros to make the + * frame minimum ethernet frame size didn't work at all. + * Instead of disabling checksum offload completely we + * resort to S/W checksum routine when we encounter short + * TCP frames. + * Short UDP packets appear to be handled correctly by + * Yukon II. + */ + if (m->m_pkthdr.len < MSK_MIN_FRAMELEN && + (m->m_pkthdr.csum_flags & CSUM_TCP) != 0) { + uint16_t csum; + + csum = in_cksum_skip(m, ntohs(ip->ip_len) + offset - + (ip->ip_hl << 2), offset); + *(uint16_t *)(m->m_data + offset + + m->m_pkthdr.csum_data) = csum; + m->m_pkthdr.csum_flags &= ~CSUM_TCP; + } if ((m->m_pkthdr.csum_flags & CSUM_TSO) != 0) { m = m_pullup(m, offset + sizeof(struct tcphdr)); if (m == NULL) {