Date: Sun, 18 Sep 2005 23:25:35 +0400 (MSD) From: Dmitrij Tejblum <tejblum@yandex-team.ru> To: FreeBSD-gnats-submit@FreeBSD.org Subject: kern/86306: [patch] if_em.c locks up while trying to send a highly fragmented packet Message-ID: <200509181925.j8IJPZPe046693@walrus-t.yandex.ru> Resent-Message-ID: <200509181930.j8IJU4bI098618@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>Number: 86306 >Category: kern >Synopsis: [patch] if_em.c locks up while trying to send a highly fragmented packet >Confidential: no >Severity: serious >Priority: medium >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Sun Sep 18 19:30:04 GMT 2005 >Closed-Date: >Last-Modified: >Originator: Dmitrij Tejblum >Release: FreeBSD 5.4-STABLE i386 >Organization: OOO Yandex >Environment: >Description: When em_encap() tries to send a very long mbuf chain (i.e. more than EM_MAX_SCATTER == 64 mbufs), bus_dmamap_load_mbuf_sg() may fail with EFBIG. Then em_encap() fail, the packet is not sent and left in the output queue, and thus no futher transmission is possible. Some other driver handle similar condition with m_defrag(9) function (which is intended for this purpose). >How-To-Repeat: This is more likely with jumbo frames enabled. >Fix: Index: if_em.c =================================================================== RCS file: /home/ncvs/src/sys/dev/em/if_em.c,v retrieving revision 1.44.2.9 diff -u -r1.44.2.9 if_em.c --- if_em.c 19 May 2005 08:23:06 -0000 1.44.2.9 +++ if_em.c 18 Sep 2005 18:43:15 -0000 @@ -631,13 +631,6 @@ break; } - /* Send a copy of the frame to the BPF listener */ -#if __FreeBSD_version < 500000 - if (ifp->if_bpf) - bpf_mtap(ifp, m_head); -#else - BPF_MTAP(ifp, m_head); -#endif /* Set timeout in case hardware has problems transmitting */ ifp->if_timer = EM_TX_TIMEOUT; @@ -1221,10 +1214,37 @@ } error = bus_dmamap_load_mbuf_sg(adapter->txtag, map, m_head, segs, &nsegs, BUS_DMA_NOWAIT); - if (error != 0) { + if (error != 0 && error != EFBIG) { + printf("em%d: can't map mbuf (error %d)\n", + adapter->unit, error); adapter->no_tx_dma_setup++; bus_dmamap_destroy(adapter->txtag, map); - return (error); + m_freem(m_head); + *m_headp = NULL; + return (0); + } else if (error == EFBIG) { + struct mbuf *mn; + mn = m_defrag(m_head, M_DONTWAIT); + if (mn == NULL) { + printf("em%d: can't defrag mbuf\n", adapter->unit); + bus_dmamap_destroy(adapter->txtag, map); + m_freem(m_head); + *m_headp = NULL; + adapter->no_tx_dma_setup++; + return (0); + } + m_head = mn; + error = bus_dmamap_load_mbuf_sg(adapter->txtag, map, m_head, + segs, &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { + printf("em%d: can't map mbuf2 (error %d)\n", + adapter->unit, error); + bus_dmamap_destroy(adapter->txtag, map); + m_freem(m_head); + *m_headp = NULL; + adapter->no_tx_dma_setup++; + return (0); + } } KASSERT(nsegs != 0, ("em_encap: empty packet")); @@ -1390,6 +1410,13 @@ } } + /* Send a copy of the frame to the BPF listener */ +#if __FreeBSD_version < 500000 + if (ifp->if_bpf) + bpf_mtap(ifp, m_head); +#else + BPF_MTAP(ifp, m_head); +#endif return(0); } @@ -3258,7 +3285,8 @@ adapter->stats.mpc + adapter->stats.cexterr; /* Tx Errors */ - ifp->if_oerrors = adapter->stats.ecol + adapter->stats.latecol; + ifp->if_oerrors = adapter->stats.ecol + adapter->stats.latecol + + adapter->no_tx_dma_setup + adapter->no_tx_map_avail; } (When em_encap() return an error, futher transmission stops and the interface is marked with OACTIVE. But OACTIVE mean that the *hardware* output queue is full, and the code that clear OACTIVE assume so. Therefore I think that in case of mbuf or dmamap failure it is better to return 0 from em_encap()). >Release-Note: >Audit-Trail: >Unformatted:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200509181925.j8IJPZPe046693>