From cda97c506ed6286860ccd090c5bcb5b25a684715 Mon Sep 17 00:00:00 2001 From: Mike Silbersack Date: Sat, 29 Mar 2003 21:27:07 +0000 Subject: [PATCH] Update if_dc to use m_defrag, removing the semi-duplicate dc_coal function. Also, use m_defrag where appropriate to defrag long mbuf chains in the same fashion as was done in if_sis.c. Before this change, if_dc would blow up and take down the interface if fed a really long mbuf chain. MFC after: 2 weeks --- sys/dev/dc/if_dc.c | 65 +++++++++++++++++++++------------------------- sys/pci/if_dc.c | 65 +++++++++++++++++++++------------------------- 2 files changed, 60 insertions(+), 70 deletions(-) diff --git a/sys/dev/dc/if_dc.c b/sys/dev/dc/if_dc.c index 1a73dc99ff32..ca726821047a 100644 --- a/sys/dev/dc/if_dc.c +++ b/sys/dev/dc/if_dc.c @@ -208,7 +208,6 @@ static void dc_acpi (device_t); static struct dc_type *dc_devtype (device_t); static int dc_newbuf (struct dc_softc *, int, struct mbuf *); static int dc_encap (struct dc_softc *, struct mbuf *, u_int32_t *); -static int dc_coal (struct dc_softc *, struct mbuf **); static void dc_pnic_rx_bug_war (struct dc_softc *, int); static int dc_rx_resync (struct dc_softc *); static void dc_rxeof (struct dc_softc *); @@ -3126,8 +3125,32 @@ dc_encap(sc, m_head, txidx) { struct dc_desc *f = NULL; struct mbuf *m; - int frag, cur, cnt = 0; + int frag, cur, cnt = 0, chainlen = 0; + /* + * If there's no way we can send any packets, return now. + */ + if (DC_TX_LIST_CNT - sc->dc_cdata.dc_tx_cnt < 6) + return (ENOBUFS); + + /* + * Count the number of frags in this chain to see if + * we need to m_defrag. Since the descriptor list is shared + * by all packets, we'll m_defrag long chains so that they + * do not use up the entire list, even if they would fit. + */ + + for (m = m_head; m != NULL; m = m->m_next) + chainlen++; + + if ((chainlen > DC_TX_LIST_CNT / 4) || + ((DC_TX_LIST_CNT - (chainlen + sc->dc_cdata.dc_tx_cnt)) < 6)) { + m = m_defrag(m_head, M_DONTWAIT); + if (m == NULL) + return (ENOBUFS); + m_head = m; + } + /* * Start packing the mbufs in this chain into * the fragment pointers. Stop when we run out @@ -3179,37 +3202,6 @@ 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) - return(ENOBUFS); - if (m->m_pkthdr.len > MHLEN) { - MCLGET(m_new, M_DONTWAIT); - if (!(m_new->m_flags & M_EXT)) { - m_freem(m_new); - 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 @@ -3222,7 +3214,7 @@ dc_start(ifp) struct ifnet *ifp; { struct dc_softc *sc; - struct mbuf *m_head = NULL; + struct mbuf *m_head = NULL, *m; int idx; sc = ifp->if_softc; @@ -3249,10 +3241,13 @@ dc_start(ifp) if (sc->dc_flags & DC_TX_COALESCE && (m_head->m_next != NULL || sc->dc_flags & DC_TX_ALIGN)) { - if (dc_coal(sc, &m_head)) { + m = m_defrag(m_head, M_DONTWAIT); + if (m == NULL) { IF_PREPEND(&ifp->if_snd, m_head); ifp->if_flags |= IFF_OACTIVE; break; + } else { + m_head = m; } } diff --git a/sys/pci/if_dc.c b/sys/pci/if_dc.c index 1a73dc99ff32..ca726821047a 100644 --- a/sys/pci/if_dc.c +++ b/sys/pci/if_dc.c @@ -208,7 +208,6 @@ static void dc_acpi (device_t); static struct dc_type *dc_devtype (device_t); static int dc_newbuf (struct dc_softc *, int, struct mbuf *); static int dc_encap (struct dc_softc *, struct mbuf *, u_int32_t *); -static int dc_coal (struct dc_softc *, struct mbuf **); static void dc_pnic_rx_bug_war (struct dc_softc *, int); static int dc_rx_resync (struct dc_softc *); static void dc_rxeof (struct dc_softc *); @@ -3126,8 +3125,32 @@ dc_encap(sc, m_head, txidx) { struct dc_desc *f = NULL; struct mbuf *m; - int frag, cur, cnt = 0; + int frag, cur, cnt = 0, chainlen = 0; + /* + * If there's no way we can send any packets, return now. + */ + if (DC_TX_LIST_CNT - sc->dc_cdata.dc_tx_cnt < 6) + return (ENOBUFS); + + /* + * Count the number of frags in this chain to see if + * we need to m_defrag. Since the descriptor list is shared + * by all packets, we'll m_defrag long chains so that they + * do not use up the entire list, even if they would fit. + */ + + for (m = m_head; m != NULL; m = m->m_next) + chainlen++; + + if ((chainlen > DC_TX_LIST_CNT / 4) || + ((DC_TX_LIST_CNT - (chainlen + sc->dc_cdata.dc_tx_cnt)) < 6)) { + m = m_defrag(m_head, M_DONTWAIT); + if (m == NULL) + return (ENOBUFS); + m_head = m; + } + /* * Start packing the mbufs in this chain into * the fragment pointers. Stop when we run out @@ -3179,37 +3202,6 @@ 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) - return(ENOBUFS); - if (m->m_pkthdr.len > MHLEN) { - MCLGET(m_new, M_DONTWAIT); - if (!(m_new->m_flags & M_EXT)) { - m_freem(m_new); - 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 @@ -3222,7 +3214,7 @@ dc_start(ifp) struct ifnet *ifp; { struct dc_softc *sc; - struct mbuf *m_head = NULL; + struct mbuf *m_head = NULL, *m; int idx; sc = ifp->if_softc; @@ -3249,10 +3241,13 @@ dc_start(ifp) if (sc->dc_flags & DC_TX_COALESCE && (m_head->m_next != NULL || sc->dc_flags & DC_TX_ALIGN)) { - if (dc_coal(sc, &m_head)) { + m = m_defrag(m_head, M_DONTWAIT); + if (m == NULL) { IF_PREPEND(&ifp->if_snd, m_head); ifp->if_flags |= IFF_OACTIVE; break; + } else { + m_head = m; } }