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