Transmit csum offload does not work at all on certain hardware revision
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Fri, 3 Oct 2008 14:07:02 +0000 (14:07 +0000)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Fri, 3 Oct 2008 14:07:02 +0000 (14:07 +0000)
once frame length exceeds certain threshold (different parts seems to
have different thresholds).  Borrow code from ip_output to do software
csum, if transmit csum offloading is enabled and frame length exceeds
hardware's threshold.

8169, 8169S, 8169SB and 8168B are tested, while 8169S and 8169SB does not
seem to have this bug.

sys/dev/netif/re/if_re.c
sys/dev/netif/re/if_revar.h

index f2a6302..8d89de9 100644 (file)
@@ -33,7 +33,7 @@
  * THE POSSIBILITY OF SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/dev/re/if_re.c,v 1.25 2004/06/09 14:34:01 naddy Exp $
- * $DragonFly: src/sys/dev/netif/re/if_re.c,v 1.58 2008/10/03 11:35:25 sephe Exp $
+ * $DragonFly: src/sys/dev/netif/re/if_re.c,v 1.59 2008/10/03 14:07:02 sephe Exp $
  */
 
 /*
  * driver is 7422 bytes.
  */
 
+#define _IP_VHL
+
 #include "opt_polling.h"
 
 #include <sys/param.h>
 #include <sys/bus.h>
 #include <sys/endian.h>
 #include <sys/kernel.h>
+#include <sys/in_cksum.h>
 #include <sys/interrupt.h>
 #include <sys/malloc.h>
 #include <sys/mbuf.h>
 #include <net/vlan/if_vlan_var.h>
 #include <net/vlan/if_vlan_ether.h>
 
+#include <netinet/ip.h>
+
 #include <dev/netif/mii_layer/mii.h>
 #include <dev/netif/mii_layer/miivar.h>
 
@@ -197,20 +202,22 @@ static const struct re_type re_devs[] = {
 };
 
 static const struct re_hwrev re_hwrevs[] = {
-       { RE_HWREV_8139CPLUS,   RE_8139CPLUS,   RE_F_HASMPC,    "C+" },
-       { RE_HWREV_8168_SPIN1,  RE_8169,        RE_F_PCIE,      "8168" },
-       { RE_HWREV_8168_SPIN2,  RE_8169,        RE_F_PCIE,      "8168" },
-       { RE_HWREV_8168_SPIN3,  RE_8169,        RE_F_PCIE,      "8168" },
-       { RE_HWREV_8168C,       RE_8169,        RE_F_PCIE,      "8168C" },
-       { RE_HWREV_8169,        RE_8169,        RE_F_HASMPC,    "8169" },
-       { RE_HWREV_8169S,       RE_8169,        RE_F_HASMPC,    "8169S" },
-       { RE_HWREV_8110S,       RE_8169,        RE_F_HASMPC,    "8110S" },
-       { RE_HWREV_8169_8110SB, RE_8169,        RE_F_HASMPC,    "8169SB" },
-       { RE_HWREV_8169_8110SC, RE_8169,        0,              "8169SC" },
-       { RE_HWREV_8100E,       RE_8169,        RE_F_HASMPC,    "8100E" },
-       { RE_HWREV_8101E,       RE_8169,        RE_F_PCIE,      "8101E" },
-       { RE_HWREV_8102EL,      RE_8169,        RE_F_PCIE,      "8102EL" },
-       { 0, 0, 0, NULL }
+       { RE_HWREV_8139CPLUS,   RE_8139CPLUS,   RE_F_HASMPC,    0, "C+" },
+       { RE_HWREV_8168_SPIN1,  RE_8169,        RE_F_PCIE,      0, "8168" },
+       { RE_HWREV_8168_SPIN2,  RE_8169,
+         RE_F_PCIE | RE_F_JUMBO_SWCSUM, RE_SWCSUM_LIM_8168B, "8168" },
+       { RE_HWREV_8168_SPIN3,  RE_8169,        RE_F_PCIE,      0, "8168" },
+       { RE_HWREV_8168C,       RE_8169,        RE_F_PCIE,      0, "8168C" },
+       { RE_HWREV_8169,        RE_8169,
+         RE_F_HASMPC | RE_F_JUMBO_SWCSUM, RE_SWCSUM_LIM_8169, "8169" },
+       { RE_HWREV_8169S,       RE_8169,        RE_F_HASMPC,    0, "8169S" },
+       { RE_HWREV_8110S,       RE_8169,        RE_F_HASMPC,    0, "8110S" },
+       { RE_HWREV_8169_8110SB, RE_8169,        RE_F_HASMPC,    0, "8169SB" },
+       { RE_HWREV_8169_8110SC, RE_8169,        0,              0, "8169SC" },
+       { RE_HWREV_8100E,       RE_8169,        RE_F_HASMPC,    0, "8100E" },
+       { RE_HWREV_8101E,       RE_8169,        RE_F_PCIE,      0, "8101E" },
+       { RE_HWREV_8102EL,      RE_8169,        RE_F_PCIE,      0, "8102EL" },
+       { 0, 0, 0, 0, NULL }
 };
 
 static int     re_probe(device_t);
@@ -1231,6 +1238,7 @@ re_attach(device_t dev)
                if (hw_rev->re_rev == hwrev) {
                        sc->re_type = hw_rev->re_type;
                        sc->re_flags = hw_rev->re_flags;
+                       sc->re_swcsum_lim = hw_rev->re_swcsum_lim;
                        break;
                }
        }
@@ -1930,6 +1938,58 @@ re_encap(struct re_softc *sc, struct mbuf **m_head, int *idx0)
        if (m->m_pkthdr.csum_flags & CSUM_UDP)
                csum_flags |= RE_TDESC_CMD_UDPCSUM;
 
+       if (m->m_pkthdr.len > sc->re_swcsum_lim &&
+           (m->m_pkthdr.csum_flags & (CSUM_DELAY_IP | CSUM_DELAY_DATA)) &&
+           (sc->re_flags & RE_F_JUMBO_SWCSUM)) {
+               struct ether_header *eh;
+               struct ip *ip;
+               u_short offset;
+
+               m = m_pullup(m, sizeof(struct ether_header *));
+               if (m == NULL) {
+                       *m_head = NULL;
+                       return ENOBUFS;
+               }
+               eh = mtod(m, struct ether_header *);
+
+               /* XXX */
+               if (eh->ether_type == ETHERTYPE_VLAN)
+                       offset = sizeof(struct ether_vlan_header);
+               else
+                       offset = sizeof(struct ether_header);
+
+               m = m_pullup(m, offset + sizeof(struct ip *));
+               if (m == NULL) {
+                       *m_head = NULL;
+                       return ENOBUFS;
+               }
+               ip = (struct ip *)(mtod(m, uint8_t *) + offset);
+
+               if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
+                       u_short csum;
+
+                       offset += IP_VHL_HL(ip->ip_vhl) << 2;
+                       csum = in_cksum_skip(m, ntohs(ip->ip_len), offset);
+                       if (m->m_pkthdr.csum_flags & CSUM_UDP && csum == 0)
+                               csum = 0xffff;
+                       offset += m->m_pkthdr.csum_data;        /* checksum offset */
+                       *(u_short *)(m->m_data + offset) = csum;
+
+                       m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
+               }
+               if (m->m_pkthdr.csum_flags & CSUM_DELAY_IP) {
+                       ip->ip_sum = 0;
+                       if (ip->ip_vhl == IP_VHL_BORING) {
+                               ip->ip_sum = in_cksum_hdr(ip);
+                       } else {
+                               ip->ip_sum =
+                               in_cksum(m, IP_VHL_HL(ip->ip_vhl) << 2);
+                       }
+                       m->m_pkthdr.csum_flags &= ~CSUM_DELAY_IP;
+               }
+               *m_head = m; /* 'm' may be changed by above two m_pullup() */
+       }
+
        /*
         * With some of the RealTek chips, using the checksum offload
         * support in conjunction with the autopadding feature results
index 24fb07c..d5dbe61 100644 (file)
@@ -33,7 +33,7 @@
  * THE POSSIBILITY OF SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/pci/if_rlreg.h,v 1.42 2004/05/24 19:39:23 jhb Exp $
- * $DragonFly: src/sys/dev/netif/re/if_revar.h,v 1.6 2008/10/03 10:12:35 sephe Exp $
+ * $DragonFly: src/sys/dev/netif/re/if_revar.h,v 1.7 2008/10/03 14:07:02 sephe Exp $
  */
 
 struct re_chain_data {
@@ -69,6 +69,7 @@ struct re_hwrev {
        uint32_t                re_rev;
        int                     re_type;        /* RE_{8139CPLUS,8169} */
        uint32_t                re_flags;       /* see RE_F_ */
+       int                     re_swcsum_lim;
        const char              *re_desc;
 };
 
@@ -140,6 +141,7 @@ struct re_softc {
        int                     suspended;      /* 0 = normal  1 = suspended */
        int                     re_link;
        int                     re_eewidth;
+       int                     re_swcsum_lim;
 #ifdef DEVICE_POLLING
        int                     rxcycles;
 #endif
@@ -160,6 +162,10 @@ struct re_softc {
 
 #define RE_F_HASMPC            0x1
 #define RE_F_PCIE              0x2
+#define RE_F_JUMBO_SWCSUM      0x4
+
+#define RE_SWCSUM_LIM_8169     2038
+#define RE_SWCSUM_LIM_8168B    2082
 
 #define RE_TX_MODERATION_IS_ENABLED(sc)                        \
        ((sc)->re_tx_ack == RE_ISR_TIMEOUT_EXPIRED)