this follows the more standard order for fitting a packet onto a tx
ring. it also uses the more modern m_defrag pattern for heavily
fragmented packets.
Works For Me(tm).
ok?
Index: if_pcn.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_pcn.c,v
retrieving revision 1.44
diff -u -p -r1.44 if_pcn.c
--- if_pcn.c 10 Jul 2020 13:26:38 -0000 1.44
+++ if_pcn.c 18 Mar 2021 01:34:01 -0000
@@ -811,10 +811,10 @@ void
pcn_start(struct ifnet *ifp)
{
struct pcn_softc *sc = ifp->if_softc;
- struct mbuf *m0, *m;
+ struct mbuf *m0;
struct pcn_txsoft *txs;
bus_dmamap_t dmamap;
- int error, nexttx, lasttx = -1, ofree, seg;
+ int nexttx, lasttx = -1, ofree, seg;
if (!(ifp->if_flags & IFF_RUNNING) || ifq_is_oactive(&ifp->if_snd))
return;
@@ -831,80 +831,34 @@ pcn_start(struct ifnet *ifp)
* descriptors.
*/
for (;;) {
- /* Grab a packet off the queue. */
- m0 = ifq_deq_begin(&ifp->if_snd);
- if (m0 == NULL)
+ if (sc->sc_txsfree == 0 ||
+ sc->sc_txfree < (PCN_NTXSEGS + 1)) {
+ ifq_set_oactive(&ifp->if_snd);
break;
- m = NULL;
+ }
- /* Get a work queue entry. */
- if (sc->sc_txsfree == 0) {
- ifq_deq_rollback(&ifp->if_snd, m0);
+ /* Grab a packet off the queue. */
+ m0 = ifq_dequeue(&ifp->if_snd);
+ if (m0 == NULL)
break;
- }
txs = &sc->sc_txsoft[sc->sc_txsnext];
dmamap = txs->txs_dmamap;
- /*
- * Load the DMA map. If this fails, the packet either
- * didn't fit in the alloted number of segments, or we
- * were short on resources. In this case, we'll copy
- * and try again.
- */
- if (bus_dmamap_load_mbuf(sc->sc_dmat, dmamap, m0,
- BUS_DMA_WRITE|BUS_DMA_NOWAIT) != 0) {
- MGETHDR(m, M_DONTWAIT, MT_DATA);
- if (m == NULL) {
- ifq_deq_rollback(&ifp->if_snd, m0);
- break;
- }
- if (m0->m_pkthdr.len > MHLEN) {
- MCLGET(m, M_DONTWAIT);
- if ((m->m_flags & M_EXT) == 0) {
- ifq_deq_rollback(&ifp->if_snd, m0);
- m_freem(m);
- break;
- }
- }
- m_copydata(m0, 0, m0->m_pkthdr.len, mtod(m, caddr_t));
- m->m_pkthdr.len = m->m_len = m0->m_pkthdr.len;
- error = bus_dmamap_load_mbuf(sc->sc_dmat, dmamap,
- m, BUS_DMA_WRITE|BUS_DMA_NOWAIT);
- if (error) {
- ifq_deq_rollback(&ifp->if_snd, m0);
- break;
- }
- }
-
- /*
- * Ensure we have enough descriptors free to describe
- * the packet. Note, we always reserve one descriptor
- * at the end of the ring as a termination point, to
- * prevent wrap-around.
- */
- if (dmamap->dm_nsegs > (sc->sc_txfree - 1)) {
- /*
- * Not enough free descriptors to transmit this
- * packet. We haven't committed anything yet,
- * so just unload the DMA map, put the packet
- * back on the queue, and punt. Notify the upper
- * layer that there are not more slots left.
- *
- * XXX We could allocate an mbuf and copy, but
- * XXX is it worth it?
- */
- ifq_set_oactive(&ifp->if_snd);
- bus_dmamap_unload(sc->sc_dmat, dmamap);
- m_freem(m);
- ifq_deq_rollback(&ifp->if_snd, m0);
+ switch (bus_dmamap_load_mbuf(sc->sc_dmat, dmamap, m0,
+ BUS_DMA_NOWAIT)) {
+ case 0:
break;
- }
+ case EFBIG:
+ if (m_defrag(m0, M_DONTWAIT) == 0 &&
+ bus_dmamap_load_mbuf(sc->sc_dmat, dmamap, m0,
+ BUS_DMA_NOWAIT) == 0)
+ break;
- ifq_deq_commit(&ifp->if_snd, m0);
- if (m != NULL) {
+ /* FALLTHROUGH */
+ default:
m_freem(m0);
- m0 = m;
+ continue;
}
/*
@@ -1000,11 +954,6 @@ pcn_start(struct ifnet *ifp)
#endif /* NBPFILTER > 0 */
}
- if (sc->sc_txsfree == 0 || sc->sc_txfree == 0) {
- /* No more slots left; notify upper layer. */
- ifq_set_oactive(&ifp->if_snd);
- }
-
if (sc->sc_txfree != ofree) {
/* Set a watchdog timer in case the chip flakes out. */
ifp->if_timer = 5;
@@ -1198,8 +1147,6 @@ pcn_txintr(struct pcn_softc *sc)
uint32_t tmd1, tmd2, tmd;
int i, j;
- ifq_clr_oactive(&ifp->if_snd);
-
/*
* Go through our Tx list and free mbufs for those
* frames which have been transmitted.
@@ -1286,6 +1233,9 @@ pcn_txintr(struct pcn_softc *sc)
*/
if (sc->sc_txsfree == PCN_TXQUEUELEN)
ifp->if_timer = 0;
+
+ if (ifq_is_oactive(&ifp->if_snd))
+ ifq_restart(&ifp->if_snd);
}
/*