kern/86306: [patch] if_em.c locks up while trying to send a highly
fragmented packet
Dmitrij Tejblum
tejblum at yandex-team.ru
Sun Sep 18 12:30:06 PDT 2005
>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:
More information about the freebsd-bugs
mailing list