diff options
author | yongari <yongari@FreeBSD.org> | 2010-10-19 23:04:23 +0000 |
---|---|---|
committer | yongari <yongari@FreeBSD.org> | 2010-10-19 23:04:23 +0000 |
commit | a8bf3f7ffc311fa9824224b40c4df2011514c7f0 (patch) | |
tree | dc1412c80ac2ea6bbce23427a9727ef720fc9b46 /sys/dev/bge/if_bge.c | |
parent | 831bbfaf753dc145ab80d1807336d4fb9ef8dffe (diff) | |
download | FreeBSD-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.c | 43 |
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) |