Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/pci Check in hooks to fix checksum offload on bge de...



details:   https://anonhg.NetBSD.org/src/rev/ac8fdff7ac8e
branches:  trunk
changeset: 550810:ac8fdff7ac8e
user:      jonathan <jonathan%NetBSD.org@localhost>
date:      Fri Aug 22 03:32:35 2003 +0000

description:
Check in hooks to fix checksum offload on bge devices. Empirical
observation is that some 570x devices can get themselves into a state
where they miscompute off-loaded TCP or UDP checksums on packets so
small that Ethernet padding is required.  Further obsevation suggests
that the bge checksum-offload hardware is adding those padding bytes
into its TCP checksum computation. (Once a 5700 gets in this state,
even a warm boot won't fix it: it needs a hard powerdown.)

Work around the problem by padding such runts with zeros: even if the
checksum-offload adds in extra zeros, the resulting sum will be correct.

Also, dont trust the checksum-offload on received packets smaller than
the minimum ethernet frame, in case the Rx-side has a similar bug.

Finally, on packets where we do trust the outboard Rx-side TCP or UDP
checksum, the bge did not include the pseudo-header. Set the
M_CSUM_NO_PSEUDOHDR bit as well as M_CSUM_DATA, and rely on
udp_input() or tcp_input() adding in the sum via in_cksum_phdr().

diffstat:

 sys/dev/pci/if_bge.c |  107 ++++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 93 insertions(+), 14 deletions(-)

diffs (156 lines):

diff -r dc4951095ad2 -r ac8fdff7ac8e sys/dev/pci/if_bge.c
--- a/sys/dev/pci/if_bge.c      Fri Aug 22 03:06:32 2003 +0000
+++ b/sys/dev/pci/if_bge.c      Fri Aug 22 03:32:35 2003 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: if_bge.c,v 1.45 2003/08/22 03:03:20 jonathan Exp $     */
+/*     $NetBSD: if_bge.c,v 1.46 2003/08/22 03:32:35 jonathan Exp $     */
 
 /*
  * Copyright (c) 2001 Wind River Systems
@@ -79,7 +79,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_bge.c,v 1.45 2003/08/22 03:03:20 jonathan Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_bge.c,v 1.46 2003/08/22 03:32:35 jonathan Exp $");
 
 #include "bpfilter.h"
 #include "vlan.h"
@@ -123,6 +123,8 @@
 
 #include <uvm/uvm_extern.h>
 
+#define ETHER_MIN_NOPAD (ETHER_MIN_LEN - ETHER_CRC_LEN) /* i.e., 60 */
+
 int bge_probe(struct device *, struct cfdata *, void *);
 void bge_attach(struct device *, struct device *, void *);
 void bge_release_resources(struct bge_softc *);
@@ -132,6 +134,7 @@
 void bge_tick(void *);
 void bge_stats_update(struct bge_softc *);
 int bge_encap(struct bge_softc *, struct mbuf *, u_int32_t *);
+static __inline int bge_cksum_pad(struct mbuf *pkt);
 static __inline int bge_compact_dma_runt(struct mbuf *pkt);
 
 int bge_intr(void *);
@@ -2472,18 +2475,23 @@
                        bpf_mtap(ifp->if_bpf, m);
 #endif
 
-               if ((sc->bge_quirks & BGE_QUIRK_CSUM_BROKEN) == 0) {
-                       m->m_pkthdr.csum_flags |= M_CSUM_IPv4;
-                       if ((cur_rx->bge_ip_csum ^ 0xffff) != 0)
-                               m->m_pkthdr.csum_flags |= M_CSUM_IPv4_BAD;
-#if 0  /* XXX appears to be broken */
-                       if (cur_rx->bge_flags & BGE_RXBDFLAG_TCP_UDP_CSUM) {
-                               m->m_pkthdr.csum_data =
-                                   cur_rx->bge_tcp_udp_csum;
-                               m->m_pkthdr.csum_flags |=
-                                   (M_CSUM_TCPv4|M_CSUM_UDPv4|M_CSUM_DATA);
-                       }
-#endif
+               m->m_pkthdr.csum_flags |= M_CSUM_IPv4;
+
+               if ((cur_rx->bge_ip_csum ^ 0xffff) != 0)
+                       m->m_pkthdr.csum_flags |= M_CSUM_IPv4_BAD;
+               /*
+                * Rx transport checksum-offload may also
+                * have bugs with packets which, when transmitted,
+                * were `runts' requiring padding.
+                */
+               if (cur_rx->bge_flags & BGE_RXBDFLAG_TCP_UDP_CSUM &&
+                   (/* (sc->_bge_quirks & BGE_QUIRK_SHORT_CKSUM_BUG) == 0 ||*/
+                    m->m_pkthdr.len >= ETHER_MIN_NOPAD)) {
+                       m->m_pkthdr.csum_data =
+                           cur_rx->bge_tcp_udp_csum;
+                       m->m_pkthdr.csum_flags |=
+                           (M_CSUM_TCPv4|M_CSUM_UDPv4|
+                            M_CSUM_DATA|M_CSUM_NO_PSEUDOHDR);
                }
 
                /*
@@ -2751,6 +2759,59 @@
 #endif
 }
 
+/*
+ * Pad outbound frame to ETHER_MIN_NOPAD for an unusual reason.
+ * The bge hardware will pad out Tx runts to ETHER_MIN_NOPAD,
+ * but when such padded frames employ the  bge IP/TCP checksum offload,
+ * the hardware checksum assist gives incorrect results (possibly
+ * from incorporating its own padding into the UDP/TCP checksum; who knows).
+ * If we pad such runts with zeros, the onboard checksum comes out correct.
+ */
+static __inline int
+bge_cksum_pad(struct mbuf *pkt)
+{
+       struct mbuf *last = NULL;
+       int padlen;
+
+       padlen = ETHER_MIN_NOPAD - pkt->m_pkthdr.len;
+
+       /* if there's only the packet-header and we can pad there, use it. */
+       if (pkt->m_pkthdr.len == pkt->m_len &&
+           !M_READONLY(pkt) && M_TRAILINGSPACE(pkt) >= padlen) {
+               last = pkt;
+       } else {
+               /*
+                * Walk packet chain to find last mbuf. We will either
+                * pad there, or append a new mbuf and pad it 
+                * (thus perhaps avoiding the bcm5700 dma-min bug).
+                */
+               for (last = pkt; last->m_next != NULL; last = last->m_next) {
+                      (void) 0; /* do nothing*/
+               }
+
+               /* `last' now points to last in chain. */
+               if (!M_READONLY(last) && M_TRAILINGSPACE(last) >= padlen) {
+                       (void) 0; /* we can pad here, in-place. */
+               } else {
+                       /* Allocate new empty mbuf, pad it. Compact later. */
+                       struct mbuf *n;
+                       MGET(n, M_DONTWAIT, MT_DATA);
+                       n->m_len = 0;
+                       last->m_next = n;
+                       last = n;
+               }
+       }
+
+#ifdef DEBUG
+         KASSERT(M_WRITABLE(last), ("to-pad mbuf not writeable\n"));
+         KASSERT(M_TRAILINGSPACE(last) >= padlen, ("insufficient space to pad\n"));
+#endif
+       /* Now zero the pad area, to avoid the bge cksum-assist bug */
+       memset(mtod(last, caddr_t) + last->m_len, 0, padlen);
+       last->m_len += padlen;
+       pkt->m_pkthdr.len += padlen;
+       return 0;
+}
 
 /*
  * Compact outbound packets to avoid bug with DMA segments less than 8 bytes.
@@ -2896,6 +2957,24 @@
                        csum_flags |= BGE_TXBDFLAG_TCP_UDP_CSUM;
        }
 
+       /* 
+        * If we were asked to do an outboard checksum, and the NIC
+        * has the bug where it sometimes adds in the Ethernet padding,
+        * explicitly pad with zeros so the cksum will be correct either way.
+        * (For now, do this for all chip versions, until newer
+        * are confirmed to not require the workaround.)
+        */
+       if ((csum_flags & BGE_TXBDFLAG_TCP_UDP_CSUM) == 0 ||
+#ifdef notyet
+           (sc->bge_quirks & BGE_QUIRK_SHORT_CKSUM_BUG) == 0 ||
+#endif     
+           m_head->m_pkthdr.len >= ETHER_MIN_NOPAD)
+               goto check_dma_bug;
+
+       if (bge_cksum_pad(m_head) != 0)
+           return ENOBUFS;
+
+check_dma_bug:
        if (!(sc->bge_quirks & BGE_QUIRK_5700_SMALLDMA))
                goto doit;
        /*



Home | Main Index | Thread Index | Old Index