Reintroduce the dc_coal() workaround routine for coalescing outbound
packets into a single buffer, and set the DC_TX_COALESCE flag for the Davicom DM9102 chip. I thought I had escaped this problem, but... This chip appears to silently corrupt or discard transmitted frames when using scatter/gather DMA (i.e. DMAing each packet fragment in place with a separate descriptor). The only way to insure reliable transmission is to coalesce transmitted packets into a single cluster buffer. (There may also be an alignment constraint here, but mbuf cluster buffers are naturally aligned on 2K boundaries, which seems to be good enough.) The DM9102 driver for Linux written by Davicom also uses this workaround. Unfortunately, the Davicom datasheet has no errata section describing this or any other apparently known defect. Problem noted by: allan_chou@davicom.com.tw
This commit is contained in:
parent
1e3f91cfff
commit
63989277ed
@ -197,6 +197,7 @@ static struct dc_type *dc_devtype __P((device_t));
|
||||
static int dc_newbuf __P((struct dc_softc *, int, struct mbuf *));
|
||||
static int dc_encap __P((struct dc_softc *, struct mbuf *,
|
||||
u_int32_t *));
|
||||
static int dc_coal __P((struct dc_softc *, struct mbuf **));
|
||||
static void dc_pnic_rx_bug_war __P((struct dc_softc *, int));
|
||||
static int dc_rx_resync __P((struct dc_softc *));
|
||||
static void dc_rxeof __P((struct dc_softc *));
|
||||
@ -1454,7 +1455,7 @@ static int dc_attach(dev)
|
||||
case DC_DEVICEID_DM9100:
|
||||
case DC_DEVICEID_DM9102:
|
||||
sc->dc_type = DC_TYPE_DM9102;
|
||||
sc->dc_flags |= DC_TX_USE_TX_INTR;
|
||||
sc->dc_flags |= DC_TX_COALESCE|DC_TX_USE_TX_INTR;
|
||||
sc->dc_flags |= DC_REDUCED_MII_POLL;
|
||||
sc->dc_pmode = DC_PMODE_MII;
|
||||
break;
|
||||
@ -2374,6 +2375,39 @@ static int dc_encap(sc, m_head, txidx)
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Coalesce an mbuf chain into a single mbuf cluster buffer.
|
||||
* Needed for some really badly behaved chips that just can't
|
||||
* do scatter/gather correctly.
|
||||
*/
|
||||
static int dc_coal(sc, m_head)
|
||||
struct dc_softc *sc;
|
||||
struct mbuf **m_head;
|
||||
{
|
||||
struct mbuf *m_new, *m;
|
||||
|
||||
m = *m_head;
|
||||
MGETHDR(m_new, M_DONTWAIT, MT_DATA);
|
||||
if (m_new == NULL) {
|
||||
printf("dc%d: no memory for tx list", sc->dc_unit);
|
||||
return(ENOBUFS);
|
||||
}
|
||||
if (m->m_pkthdr.len > MHLEN) {
|
||||
MCLGET(m_new, M_DONTWAIT);
|
||||
if (!(m_new->m_flags & M_EXT)) {
|
||||
m_freem(m_new);
|
||||
printf("dc%d: no memory for tx list", sc->dc_unit);
|
||||
return(ENOBUFS);
|
||||
}
|
||||
}
|
||||
m_copydata(m, 0, m->m_pkthdr.len, mtod(m_new, caddr_t));
|
||||
m_new->m_pkthdr.len = m_new->m_len = m->m_pkthdr.len;
|
||||
m_freem(m);
|
||||
*m_head = m_new;
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Main transmit routine. To avoid having to do mbuf copies, we put pointers
|
||||
* to the mbuf data regions directly in the transmit lists. We also save a
|
||||
@ -2403,6 +2437,14 @@ static void dc_start(ifp)
|
||||
if (m_head == NULL)
|
||||
break;
|
||||
|
||||
if (sc->dc_flags & DC_TX_COALESCE) {
|
||||
if (dc_coal(sc, &m_head)) {
|
||||
IF_PREPEND(&ifp->if_snd, m_head);
|
||||
ifp->if_flags |= IFF_OACTIVE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (dc_encap(sc, m_head, &idx)) {
|
||||
IF_PREPEND(&ifp->if_snd, m_head);
|
||||
ifp->if_flags |= IFF_OACTIVE;
|
||||
|
@ -197,6 +197,7 @@ static struct dc_type *dc_devtype __P((device_t));
|
||||
static int dc_newbuf __P((struct dc_softc *, int, struct mbuf *));
|
||||
static int dc_encap __P((struct dc_softc *, struct mbuf *,
|
||||
u_int32_t *));
|
||||
static int dc_coal __P((struct dc_softc *, struct mbuf **));
|
||||
static void dc_pnic_rx_bug_war __P((struct dc_softc *, int));
|
||||
static int dc_rx_resync __P((struct dc_softc *));
|
||||
static void dc_rxeof __P((struct dc_softc *));
|
||||
@ -1454,7 +1455,7 @@ static int dc_attach(dev)
|
||||
case DC_DEVICEID_DM9100:
|
||||
case DC_DEVICEID_DM9102:
|
||||
sc->dc_type = DC_TYPE_DM9102;
|
||||
sc->dc_flags |= DC_TX_USE_TX_INTR;
|
||||
sc->dc_flags |= DC_TX_COALESCE|DC_TX_USE_TX_INTR;
|
||||
sc->dc_flags |= DC_REDUCED_MII_POLL;
|
||||
sc->dc_pmode = DC_PMODE_MII;
|
||||
break;
|
||||
@ -2374,6 +2375,39 @@ static int dc_encap(sc, m_head, txidx)
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Coalesce an mbuf chain into a single mbuf cluster buffer.
|
||||
* Needed for some really badly behaved chips that just can't
|
||||
* do scatter/gather correctly.
|
||||
*/
|
||||
static int dc_coal(sc, m_head)
|
||||
struct dc_softc *sc;
|
||||
struct mbuf **m_head;
|
||||
{
|
||||
struct mbuf *m_new, *m;
|
||||
|
||||
m = *m_head;
|
||||
MGETHDR(m_new, M_DONTWAIT, MT_DATA);
|
||||
if (m_new == NULL) {
|
||||
printf("dc%d: no memory for tx list", sc->dc_unit);
|
||||
return(ENOBUFS);
|
||||
}
|
||||
if (m->m_pkthdr.len > MHLEN) {
|
||||
MCLGET(m_new, M_DONTWAIT);
|
||||
if (!(m_new->m_flags & M_EXT)) {
|
||||
m_freem(m_new);
|
||||
printf("dc%d: no memory for tx list", sc->dc_unit);
|
||||
return(ENOBUFS);
|
||||
}
|
||||
}
|
||||
m_copydata(m, 0, m->m_pkthdr.len, mtod(m_new, caddr_t));
|
||||
m_new->m_pkthdr.len = m_new->m_len = m->m_pkthdr.len;
|
||||
m_freem(m);
|
||||
*m_head = m_new;
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Main transmit routine. To avoid having to do mbuf copies, we put pointers
|
||||
* to the mbuf data regions directly in the transmit lists. We also save a
|
||||
@ -2403,6 +2437,14 @@ static void dc_start(ifp)
|
||||
if (m_head == NULL)
|
||||
break;
|
||||
|
||||
if (sc->dc_flags & DC_TX_COALESCE) {
|
||||
if (dc_coal(sc, &m_head)) {
|
||||
IF_PREPEND(&ifp->if_snd, m_head);
|
||||
ifp->if_flags |= IFF_OACTIVE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (dc_encap(sc, m_head, &idx)) {
|
||||
IF_PREPEND(&ifp->if_snd, m_head);
|
||||
ifp->if_flags |= IFF_OACTIVE;
|
||||
|
Loading…
Reference in New Issue
Block a user