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