summaryrefslogtreecommitdiffstats
path: root/sys/dev/bge/if_bge.c
diff options
context:
space:
mode:
authoryongari <yongari@FreeBSD.org>2010-10-19 23:04:23 +0000
committeryongari <yongari@FreeBSD.org>2010-10-19 23:04:23 +0000
commita8bf3f7ffc311fa9824224b40c4df2011514c7f0 (patch)
treedc1412c80ac2ea6bbce23427a9727ef720fc9b46 /sys/dev/bge/if_bge.c
parent831bbfaf753dc145ab80d1807336d4fb9ef8dffe (diff)
downloadFreeBSD-src-a8bf3f7ffc311fa9824224b40c4df2011514c7f0.zip
FreeBSD-src-a8bf3f7ffc311fa9824224b40c4df2011514c7f0.tar.gz
Add workaround for BCM5906 controller silicon bug. If device
receive two back-to-back send BDs with less than or equal to 8 total bytes then the device may hang. The two back-to-back send BDs must be in the same frame for this failure to occur. Thanks to davidch for detailed errata information. Reviewed by: davidch
Diffstat (limited to 'sys/dev/bge/if_bge.c')
-rw-r--r--sys/dev/bge/if_bge.c43
1 files changed, 43 insertions, 0 deletions
diff --git a/sys/dev/bge/if_bge.c b/sys/dev/bge/if_bge.c
index 662713f..5380931 100644
--- a/sys/dev/bge/if_bge.c
+++ b/sys/dev/bge/if_bge.c
@@ -374,6 +374,7 @@ static void bge_tick(void *);
static void bge_stats_clear_regs(struct bge_softc *);
static void bge_stats_update(struct bge_softc *);
static void bge_stats_update_regs(struct bge_softc *);
+static struct mbuf *bge_check_short_dma(struct mbuf *);
static struct mbuf *bge_setup_tso(struct bge_softc *, struct mbuf *,
uint16_t *);
static int bge_encap(struct bge_softc *, struct mbuf **, uint32_t *);
@@ -2633,6 +2634,8 @@ bge_attach(device_t dev)
case BGE_ASICREV_BCM5752:
case BGE_ASICREV_BCM5906:
sc->bge_flags |= BGE_FLAG_575X_PLUS;
+ if (sc->bge_asicrev == BGE_ASICREV_BCM5906)
+ sc->bge_flags |= BGE_FLAG_SHORT_DMA_BUG;
/* FALLTHROUGH */
case BGE_ASICREV_BCM5705:
sc->bge_flags |= BGE_FLAG_5705_PLUS;
@@ -4060,6 +4063,39 @@ bge_cksum_pad(struct mbuf *m)
}
static struct mbuf *
+bge_check_short_dma(struct mbuf *m)
+{
+ struct mbuf *n;
+ int found;
+
+ /*
+ * If device receive two back-to-back send BDs with less than
+ * or equal to 8 total bytes then the device may hang. The two
+ * back-to-back send BDs must in the same frame for this failure
+ * to occur. Scan mbuf chains and see whether two back-to-back
+ * send BDs are there. If this is the case, allocate new mbuf
+ * and copy the frame to workaround the silicon bug.
+ */
+ for (n = m, found = 0; n != NULL; n = n->m_next) {
+ if (n->m_len < 8) {
+ found++;
+ if (found > 1)
+ break;
+ continue;
+ }
+ found = 0;
+ }
+
+ if (found > 1) {
+ n = m_defrag(m, M_DONTWAIT);
+ if (n == NULL)
+ m_freem(m);
+ } else
+ n = m;
+ return (n);
+}
+
+static struct mbuf *
bge_setup_tso(struct bge_softc *sc, struct mbuf *m, uint16_t *mss)
{
struct ip *ip;
@@ -4132,6 +4168,13 @@ bge_encap(struct bge_softc *sc, struct mbuf **m_head, uint32_t *txidx)
csum_flags = 0;
mss = 0;
vlan_tag = 0;
+ if ((sc->bge_flags & BGE_FLAG_SHORT_DMA_BUG) != 0 &&
+ m->m_next != NULL) {
+ *m_head = bge_check_short_dma(m);
+ if (*m_head == NULL)
+ return (ENOBUFS);
+ m = *m_head;
+ }
if ((m->m_pkthdr.csum_flags & CSUM_TSO) != 0) {
*m_head = m = bge_setup_tso(sc, m, &mss);
if (*m_head == NULL)
OpenPOWER on IntegriCloud