David Young
2012-09-19 23:34:15 UTC
wm(4) sets up its Tx DMA maps like this,
if ((error = bus_dmamap_create(sc->sc_dmat, WM_MAXTXDMA,
WM_NTXSEGS, WTX_MAX_LEN, 0, 0,
&sc->sc_txsoft[i].txs_dmamap)) != 0) {
WM_MAXTXDMA is round_page(IP_MAXPACKET) == round_page(65535) ==
65536. Thus wm(4) will fail to map for Tx any mbuf whose m_pkthdr.len
produce a longer buffer because first it clamps the length to
IP_MAXPACKET,
if (use_tso) {
/*
* Truncate TSO transfers to IP_MAXPACKET, and make
* sure that we send equal size transfers down the
* stack (rather than big-small-big-small-...).
*/
#ifdef INET6
CTASSERT(IPV6_MAXPACKET == IP_MAXPACKET);
#endif
len = (min(len, IP_MAXPACKET) / txsegsize) * txsegsize;
...
and then it adds in the combined length of the IP and TCP headers:
m->m_pkthdr.len = hdrlen + len;
In this way, wm(4) can see m->m_pkthdr.len greater than 65536 and fail
to map m. It will send no feedback to TCP to stop trying to send such
long un-segmented buffers. Also, it looks to me like it will retry
forever to map the same mbuf for DMA---that matches the misbehavior that
we're seeing at $DAYJOB, where the wm(4) ceases to transmit anything.
It seems to me that drivers should advertise the maximum unsegmented
buffer length that they support (at least some wm instances support
1MB), and tcp_output() should be very careful not to send a buffer any
longer than what is supported. What do you think?
Dave
if ((error = bus_dmamap_create(sc->sc_dmat, WM_MAXTXDMA,
WM_NTXSEGS, WTX_MAX_LEN, 0, 0,
&sc->sc_txsoft[i].txs_dmamap)) != 0) {
WM_MAXTXDMA is round_page(IP_MAXPACKET) == round_page(65535) ==
65536. Thus wm(4) will fail to map for Tx any mbuf whose m_pkthdr.len
65536. That's ok if tcp_output() produces a buffer no longer
than 65536 bytes for the NIC to segment, but in practice it willproduce a longer buffer because first it clamps the length to
IP_MAXPACKET,
if (use_tso) {
/*
* Truncate TSO transfers to IP_MAXPACKET, and make
* sure that we send equal size transfers down the
* stack (rather than big-small-big-small-...).
*/
#ifdef INET6
CTASSERT(IPV6_MAXPACKET == IP_MAXPACKET);
#endif
len = (min(len, IP_MAXPACKET) / txsegsize) * txsegsize;
...
and then it adds in the combined length of the IP and TCP headers:
m->m_pkthdr.len = hdrlen + len;
In this way, wm(4) can see m->m_pkthdr.len greater than 65536 and fail
to map m. It will send no feedback to TCP to stop trying to send such
long un-segmented buffers. Also, it looks to me like it will retry
forever to map the same mbuf for DMA---that matches the misbehavior that
we're seeing at $DAYJOB, where the wm(4) ceases to transmit anything.
It seems to me that drivers should advertise the maximum unsegmented
buffer length that they support (at least some wm instances support
1MB), and tcp_output() should be very careful not to send a buffer any
longer than what is supported. What do you think?
Dave
--
David Young
***@pobox.com Urbana, IL (217) 721-9981
--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
David Young
***@pobox.com Urbana, IL (217) 721-9981
--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de