From e8dec714c152599d2c50ced87a4f8feac69f47be Mon Sep 17 00:00:00 2001 From: yongari Date: Fri, 28 Mar 2008 01:21:21 +0000 Subject: In revision 1.70, 1.71 and 1.84 re(4) tried to workaround checksum offload bugs by manual padding for short IP/UDP frames. Unfortunately it seems that these workaround does not work reliably on newer PCIe variants of RealTek chips. To workaround the hardware bug, always pad short frames if Tx IP checksum offload is requested. It seems that the hardware has a bug in IP checksum offload handling. NetBSD manually pads short frames only when the length of IP frame is less than 28 bytes but I chose 60 bytes to safety. Also unconditionally set IP checksum offload bit in Tx descriptor if any TCP or UDP checksum offload is requested. This is the same way as Linux does but it's not mentioned in data sheet. Obtained from: NetBSD Tested by: remko, danger --- sys/dev/re/if_re.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'sys/dev/re') diff --git a/sys/dev/re/if_re.c b/sys/dev/re/if_re.c index 85e6452..a4527a6 100644 --- a/sys/dev/re/if_re.c +++ b/sys/dev/re/if_re.c @@ -2167,16 +2167,12 @@ re_encap(sc, m_head) * need to send a really small IP fragment that's less than 60 * bytes in size, and IP header checksumming is enabled, the * resulting ethernet frame that appears on the wire will - * have garbled payload. To work around this, if TX checksum + * have garbled payload. To work around this, if TX IP checksum * offload is enabled, we always manually pad short frames out * to the minimum ethernet frame size. - * - * Note: this appears unnecessary for TCP, and doing it for TCP - * with PCIe adapters seems to result in bad checksums. */ - if ((*m_head)->m_pkthdr.csum_flags & (CSUM_IP | CSUM_UDP) && - ((*m_head)->m_pkthdr.csum_flags & CSUM_TCP) == 0 && - (*m_head)->m_pkthdr.len < RL_MIN_FRAMELEN) { + if ((*m_head)->m_pkthdr.len < RL_MIN_FRAMELEN && + ((*m_head)->m_pkthdr.csum_flags & CSUM_IP) != 0) { padlen = RL_MIN_FRAMELEN - (*m_head)->m_pkthdr.len; if (M_WRITABLE(*m_head) == 0) { /* Get a writable copy. */ @@ -2257,12 +2253,18 @@ re_encap(sc, m_head) ((uint32_t)(*m_head)->m_pkthdr.tso_segsz << RL_TDESC_CMD_MSSVAL_SHIFT); else { - if ((*m_head)->m_pkthdr.csum_flags & CSUM_IP) + /* + * Unconditionally enable IP checksum if TCP or UDP + * checksum is required. Otherwise, TCP/UDP checksum + * does't make effects. + */ + if (((*m_head)->m_pkthdr.csum_flags & RE_CSUM_FEATURES) != 0) { csum_flags |= RL_TDESC_CMD_IPCSUM; - if ((*m_head)->m_pkthdr.csum_flags & CSUM_TCP) - csum_flags |= RL_TDESC_CMD_TCPCSUM; - if ((*m_head)->m_pkthdr.csum_flags & CSUM_UDP) - csum_flags |= RL_TDESC_CMD_UDPCSUM; + if (((*m_head)->m_pkthdr.csum_flags & CSUM_TCP) != 0) + csum_flags |= RL_TDESC_CMD_TCPCSUM; + if (((*m_head)->m_pkthdr.csum_flags & CSUM_UDP) != 0) + csum_flags |= RL_TDESC_CMD_UDPCSUM; + } } /* -- cgit v1.1