Module Name: src Committed By: thorpej Date: Mon Aug 22 16:14:31 UTC 2022
Modified Files: src/sys/dev/pci: if_age.c Log Message: age_encap(): Use m_defrag(), not m_pullup(), to compact an mbuf chain if it has too many DMA segments. Let the caller handle dispose of the packet on fatal errors. To generate a diff of this commit: cvs rdiff -u -r1.70 -r1.71 src/sys/dev/pci/if_age.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/pci/if_age.c diff -u src/sys/dev/pci/if_age.c:1.70 src/sys/dev/pci/if_age.c:1.71 --- src/sys/dev/pci/if_age.c:1.70 Tue May 31 08:43:15 2022 +++ src/sys/dev/pci/if_age.c Mon Aug 22 16:14:31 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: if_age.c,v 1.70 2022/05/31 08:43:15 andvar Exp $ */ +/* $NetBSD: if_age.c,v 1.71 2022/08/22 16:14:31 thorpej Exp $ */ /* $OpenBSD: if_age.c,v 1.1 2009/01/16 05:00:34 kevlo Exp $ */ /*- @@ -31,7 +31,7 @@ /* Driver for Attansic Technology Corp. L1 Gigabit Ethernet. */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if_age.c,v 1.70 2022/05/31 08:43:15 andvar Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_age.c,v 1.71 2022/08/22 16:14:31 thorpej Exp $"); #include "vlan.h" @@ -98,7 +98,7 @@ static void age_dma_free(struct age_soft static void age_get_macaddr(struct age_softc *, uint8_t[]); static void age_phy_reset(struct age_softc *); -static int age_encap(struct age_softc *, struct mbuf **); +static int age_encap(struct age_softc *, struct mbuf *); static void age_init_tx_ring(struct age_softc *); static int age_init_rx_ring(struct age_softc *); static void age_init_rr_ring(struct age_softc *); @@ -1056,7 +1056,7 @@ age_start(struct ifnet *ifp) { struct age_softc *sc = ifp->if_softc; struct mbuf *m_head; - int enq; + int enq, error; if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) return; @@ -1076,9 +1076,13 @@ age_start(struct ifnet *ifp) * don't have room, set the OACTIVE flag and wait * for the NIC to drain the ring. */ - if (age_encap(sc, &m_head)) { - if (m_head == NULL) - break; + if ((error = age_encap(sc, m_head)) != 0) { + if (error == EFBIG) { + /* This is fatal for the packet. */ + m_freem(m_head); + if_statinc(ifp, if_oerrors); + continue; + } IF_PREPEND(&ifp->if_snd, m_head); ifp->if_flags |= IFF_OACTIVE; break; @@ -1213,16 +1217,14 @@ age_resume(device_t dv, const pmf_qual_t } static int -age_encap(struct age_softc *sc, struct mbuf **m_head) +age_encap(struct age_softc *sc, struct mbuf * const m) { struct age_txdesc *txd, *txd_last; struct tx_desc *desc; - struct mbuf *m; bus_dmamap_t map; uint32_t cflags, poff, vtag; int error, i, nsegs, prod; - m = *m_head; cflags = vtag = 0; poff = 0; @@ -1231,40 +1233,31 @@ age_encap(struct age_softc *sc, struct m txd_last = txd; map = txd->tx_dmamap; - error = bus_dmamap_load_mbuf(sc->sc_dmat, map, *m_head, BUS_DMA_NOWAIT); - + error = bus_dmamap_load_mbuf(sc->sc_dmat, map, m, BUS_DMA_NOWAIT); if (error == EFBIG) { - error = 0; - - *m_head = m_pullup(*m_head, MHLEN); - if (*m_head == NULL) { - printf("%s: can't defrag TX mbuf\n", - device_xname(sc->sc_dev)); - return ENOBUFS; - } - - error = bus_dmamap_load_mbuf(sc->sc_dmat, map, *m_head, + struct mbuf *mnew = m_defrag(m, M_NOWAIT); + if (mnew != NULL) { + KASSERT(m == mnew); + error = bus_dmamap_load_mbuf(sc->sc_dmat, map, mnew, BUS_DMA_NOWAIT); - - if (error != 0) { - printf("%s: could not load defragged TX mbuf\n", - device_xname(sc->sc_dev)); - m_freem(*m_head); - *m_head = NULL; + } else { + /* Just drop if we can't defrag. */ + error = EFBIG; + } + if (error) { + if (error == EFBIG) { + printf("%s: Tx packet consumes too many " + "DMA segments, dropping...\n", + device_xname(sc->sc_dev)); + } return error; } } else if (error) { - printf("%s: could not load TX mbuf\n", device_xname(sc->sc_dev)); return error; } nsegs = map->dm_nsegs; - - if (nsegs == 0) { - m_freem(*m_head); - *m_head = NULL; - return EIO; - } + KASSERT(nsegs != 0); /* Check descriptor overrun. */ if (sc->age_cdata.age_tx_cnt + nsegs >= AGE_TX_RING_CNT - 2) { @@ -1274,7 +1267,6 @@ age_encap(struct age_softc *sc, struct m bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize, BUS_DMASYNC_PREWRITE); - m = *m_head; /* Configure Tx IP/TCP/UDP checksum offload. */ if ((m->m_pkthdr.csum_flags & AGE_CSUM_FEATURES) != 0) { cflags |= AGE_TD_CSUM;