Subject: Re: RTL8169 hw IP4CSUM_Tx workaround
To: None <tech-kern@NetBSD.org>
From: Izumi Tsutsui <tsutsui@ceres.dti.ne.jp>
List: tech-kern
Date: 10/26/2006 01:52:55
In article <061021151532.M0112888@mirage.ceres.dti.ne.jp>
I wrote:
> The attached patch seems to work on my RTL8169 and macppc,
> but is there anyway to confirm if IFCAP_CSUM_IPv4_Tx
Oops, there is a fatal bug in the previous patch.
(uninitialized m_len which would cause random memory corruption)
Here is a revised one:
---
Index: rtl8169.c
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/rtl8169.c,v
retrieving revision 1.48
diff -u -r1.48 rtl8169.c
--- rtl8169.c 24 Oct 2006 11:17:49 -0000 1.48
+++ rtl8169.c 25 Oct 2006 15:45:27 -0000
@@ -152,8 +152,11 @@
#include <dev/ic/rtl8169var.h>
+#define ETHER_PAD_LEN (ETHER_MIN_LEN - ETHER_CRC_LEN)
+
static int re_encap(struct rtk_softc *, struct mbuf *, int *);
+static inline int re_cksum_pad(struct mbuf *);
static int re_newbuf(struct rtk_softc *, int, struct mbuf *);
static int re_rx_list_init(struct rtk_softc *);
@@ -758,7 +761,7 @@
*/
ifp->if_capabilities |=
- /* IFCAP_CSUM_IPv4_Tx | */ IFCAP_CSUM_IPv4_Rx |
+ IFCAP_CSUM_IPv4_Tx | IFCAP_CSUM_IPv4_Rx |
IFCAP_CSUM_TCPv4_Tx | IFCAP_CSUM_TCPv4_Rx |
IFCAP_CSUM_UDPv4_Tx | IFCAP_CSUM_UDPv4_Rx |
IFCAP_TSOv4;
@@ -1501,6 +1504,53 @@
return handled;
}
+/*
+ * This function is just taken and modified from bge(4) driver.
+ *
+ * Although re(4) chips support auto-padding small packets,
+ * but it seems to cause a bug on IPv4 hardware checksum offload.
+ * To avoid the bug, pad packets manually if ip4csum is enabled.
+ */
+static inline int
+re_cksum_pad(struct mbuf *pkt)
+{
+ struct mbuf *m_last, *m_pad;
+ int padlen;
+
+ padlen = ETHER_PAD_LEN - pkt->m_pkthdr.len;
+
+ /*
+ * Walk packet chain to find last mbuf. We will either
+ * pad there, or append a new mbuf and pad it.
+ */
+ for (m_last = pkt; m_last->m_next != NULL; m_last = m_last->m_next)
+ continue;
+
+ /* `m_last' now points to last in chain. */
+ if (M_TRAILINGSPACE(m_last) < padlen) {
+ /*
+ * Allocate new empty mbuf chain pad it. Compact later.
+ *
+ * XXX
+ * Is it better to allocate a new mbuf by MCLGET(9)
+ * and copy whole data to avoid one more fragment
+ * since the packet size is small enough in this case.
+ */
+ MGET(m_pad, M_DONTWAIT, MT_DATA);
+ if (m_pad == NULL)
+ return ENOMEM;
+ m_pad->m_len = 0;
+ m_last->m_next = m_pad;
+ m_last = m_pad;
+ }
+
+ memset(mtod(m_last, char *) + m_last->m_len, 0, padlen);
+ m_last->m_len += padlen;
+ pkt->m_pkthdr.len += padlen;
+
+ return 0;
+}
+
static int
re_encap(struct rtk_softc *sc, struct mbuf *m, int *idx)
{
@@ -1541,6 +1591,15 @@
if ((m->m_pkthdr.csum_flags &
(M_CSUM_IPv4 | M_CSUM_TCPv4 | M_CSUM_UDPv4)) != 0) {
rtk_flags |= RTK_TDESC_CMD_IPCSUM;
+ /*
+ * Pad small packets explicitly if ip4csum is enabled
+ * to avoid a hardware bug around IPv4 outboard cksum.
+ */
+ if (m->m_pkthdr.len < ETHER_PAD_LEN) {
+ error = re_cksum_pad(m);
+ if (error != 0)
+ return error;
+ }
if (m->m_pkthdr.csum_flags & M_CSUM_TCPv4) {
rtk_flags |= RTK_TDESC_CMD_TCPCSUM;
} else if (m->m_pkthdr.csum_flags & M_CSUM_UDPv4) {
---
Izumi Tsutsui