diff options
author | yongari <yongari@FreeBSD.org> | 2006-08-14 02:21:26 +0000 |
---|---|---|
committer | yongari <yongari@FreeBSD.org> | 2006-08-14 02:21:26 +0000 |
commit | 297f53342b73885fd27db30f6852e67f9a6c293a (patch) | |
tree | aec9cb413c3563c1678700a91843eb8be6957a92 /sys/dev | |
parent | 494fd63ce267ca352d6bf5af3a85075eecd61d76 (diff) | |
download | FreeBSD-src-297f53342b73885fd27db30f6852e67f9a6c293a.zip FreeBSD-src-297f53342b73885fd27db30f6852e67f9a6c293a.tar.gz |
Make em(4) handle too many fragmented frame with m_defrag(9).
Previously em(4) requeued the failed mbuf chains from
bus_dmamap_load_mbuf_sg(9) failure to resend it later. However,
bus_dmamap_load_mbuf_sg(9) may never complete its request as the
fragmented frames can have more than EM_MAX_SCATTER segments.
To handle the above EFBIG case, defragment the frame with m_defrag(9)
and free the mbuf chain if it can't deframent the chain due to
resource shortage.
Reviewed by glebius (with improvements)
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/em/if_em.c | 45 |
1 files changed, 33 insertions, 12 deletions
diff --git a/sys/dev/em/if_em.c b/sys/dev/em/if_em.c index 3f8792a..1a41390 100644 --- a/sys/dev/em/if_em.c +++ b/sys/dev/em/if_em.c @@ -1486,20 +1486,45 @@ em_encap(struct adapter *adapter, struct mbuf **m_headp) tx_buffer = &adapter->tx_buffer_area[adapter->next_avail_tx_desc]; tx_buffer_last = tx_buffer; map = tx_buffer->map; - error = bus_dmamap_load_mbuf_sg(adapter->txtag, map, m_head, segs, &nsegs, - BUS_DMA_NOWAIT); - if (error != 0) { + error = bus_dmamap_load_mbuf_sg(adapter->txtag, map, *m_headp, segs, + &nsegs, BUS_DMA_NOWAIT); + if (error == EFBIG) { + struct mbuf *m; + + m = m_defrag(*m_headp, M_DONTWAIT); + if (m == NULL) { + /* Assume m_defrag(9) used only m_get(9). */ + adapter->mbuf_alloc_failed++; + m_freem(*m_headp); + *m_headp = NULL; + return (ENOBUFS); + } + *m_headp = m; + error = bus_dmamap_load_mbuf_sg(adapter->txtag, map, *m_headp, + segs, &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { + adapter->no_tx_dma_setup++; + m_freem(*m_headp); + *m_headp = NULL; + return (error); + } + } else if (error != 0) { adapter->no_tx_dma_setup++; return (error); } - KASSERT(nsegs != 0, ("em_encap: empty packet")); + if (nsegs == 0) { + m_freem(*m_headp); + *m_headp = NULL; + return (EIO); + } if (nsegs > adapter->num_tx_desc_avail) { adapter->no_tx_desc_avail2++; - error = ENOBUFS; - goto encap_fail; + bus_dmamap_unload(adapter->txtag, map); + return (ENOBUFS); } + m_head = *m_headp; if (ifp->if_hwassist > 0) em_transmit_checksum_setup(adapter, m_head, &txd_upper, &txd_lower); else @@ -1526,8 +1551,8 @@ em_encap(struct adapter *adapter, struct mbuf **m_headp) if (txd_used == adapter->num_tx_desc_avail) { adapter->next_avail_tx_desc = txd_saved; adapter->no_tx_desc_avail2++; - error = ENOBUFS; - goto encap_fail; + bus_dmamap_unload(adapter->txtag, map); + return (ENOBUFS); } tx_buffer = &adapter->tx_buffer_area[i]; current_tx_desc = &adapter->tx_desc_base[i]; @@ -1599,10 +1624,6 @@ em_encap(struct adapter *adapter, struct mbuf **m_headp) } return (0); - -encap_fail: - bus_dmamap_unload(adapter->txtag, map); - return (error); } /********************************************************************* |