summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2014-06-22 15:27:01 -0600
committerGregory Nutt <gnutt@nuttx.org>2014-06-22 15:27:01 -0600
commit811612f2294330eb76af73ab676c3a561a7b71c2 (patch)
tree63630c37ec72dde3499fd42603d89af873a70520
parent1c8253772e4a162a3c5faa755745dc3b7fccf6db (diff)
downloadnuttx-811612f2294330eb76af73ab676c3a561a7b71c2.tar.gz
nuttx-811612f2294330eb76af73ab676c3a561a7b71c2.tar.bz2
nuttx-811612f2294330eb76af73ab676c3a561a7b71c2.zip
TCP write buffering: Correct handling of retry counter
-rw-r--r--nuttx/net/iob/iob_trimhead.c4
-rw-r--r--nuttx/net/iob/iob_trimtail.c4
-rw-r--r--nuttx/net/net_send_buffered.c233
3 files changed, 145 insertions, 96 deletions
diff --git a/nuttx/net/iob/iob_trimhead.c b/nuttx/net/iob/iob_trimhead.c
index 0fe349023..23d8adf8a 100644
--- a/nuttx/net/iob/iob_trimhead.c
+++ b/nuttx/net/iob/iob_trimhead.c
@@ -40,6 +40,7 @@
#include <nuttx/config.h>
#include <assert.h>
+#include <debug.h>
#include <nuttx/net/iob.h>
@@ -83,6 +84,8 @@ FAR struct iob_s *iob_trimhead(FAR struct iob_s *iob, unsigned int trimlen)
uint16_t pktlen;
unsigned int len;
+ nllvdbg("iob=%p pktlen=%d trimlen=%d\n", iob, iob->io_pktlen, trimlen);
+
if (iob && trimlen > 0)
{
/* Trim from the head of the I/IO buffer chain */
@@ -94,6 +97,7 @@ FAR struct iob_s *iob_trimhead(FAR struct iob_s *iob, unsigned int trimlen)
{
/* Do we trim this entire I/O buffer away? */
+ nllvdbg("iob=%p len=%d vs %d\n", iob, iob->io_len, len);
if (iob->io_len <= len)
{
FAR struct iob_s *next;
diff --git a/nuttx/net/iob/iob_trimtail.c b/nuttx/net/iob/iob_trimtail.c
index 872560a49..61f536644 100644
--- a/nuttx/net/iob/iob_trimtail.c
+++ b/nuttx/net/iob/iob_trimtail.c
@@ -40,6 +40,7 @@
#include <nuttx/config.h>
#include <string.h>
+#include <debug.h>
#include <nuttx/net/iob.h>
@@ -81,6 +82,8 @@ FAR struct iob_s *iob_trimtail(FAR struct iob_s *iob, unsigned int trimlen)
unsigned int iosize;
int len;
+ nlldbg("iob=%p pktlen=%d trimlen=%d\n", iob, iob->io_pktlen, trimlen);
+
if (iob && trimlen > 0)
{
len = trimlen;
@@ -113,6 +116,7 @@ FAR struct iob_s *iob_trimtail(FAR struct iob_s *iob, unsigned int trimlen)
* I/O buffer away?
*/
+ nllvdbg("iob=%p len=%d vs %d\n", last, last->io_len, len);
if (last->io_len <= len)
{
/* Yes.. Consume the entire buffer */
diff --git a/nuttx/net/net_send_buffered.c b/nuttx/net/net_send_buffered.c
index 1584be800..a9346574e 100644
--- a/nuttx/net/net_send_buffered.c
+++ b/nuttx/net/net_send_buffered.c
@@ -208,7 +208,7 @@ static uint16_t send_interrupt(FAR struct uip_driver_s *dev, FAR void *pvconn,
FAR struct uip_conn *conn = (FAR struct uip_conn *)pvconn;
FAR struct socket *psock = (FAR struct socket *)pvpriv;
- //nllvdbg("flags: %04x\n", flags);
+ nllvdbg("flags: %04x\n", flags);
/* If this packet contains an acknowledgement, then update the count of
* acknowledged bytes.
@@ -248,8 +248,8 @@ static uint16_t send_interrupt(FAR struct uip_driver_s *dev, FAR void *pvconn,
/* Get the sequence number at the end of the data */
lastseq = WRB_SEQNO(wrb) + WRB_PKTLEN(wrb);
- nllvdbg("ACK: seqno=%d lastseq=%d pktlen=%d ackno=%d\n",
- WRB_SEQNO(wrb), lastseq, WRB_PKTLEN(wrb), ackno);
+ nllvdbg("ACK: wrb=%p seqno=%d lastseq=%d pktlen=%d ackno=%d\n",
+ wrb, WRB_SEQNO(wrb), lastseq, WRB_PKTLEN(wrb), ackno);
/* Has the entire buffer been ACKed? */
@@ -265,17 +265,23 @@ static uint16_t send_interrupt(FAR struct uip_driver_s *dev, FAR void *pvconn,
}
else
{
+ unsigned int trimlen;
+
/* No, then just trim the ACKed bytes from the beginning
* of the write buffer. This will free up some I/O buffers
* that can be reused while are still sending the last
* buffers in the chain.
*/
- WRB_TRIM(wrb, ackno - WRB_SEQNO(wrb));
- WRB_SEQNO(wrb) = ackno;
+ trimlen = ackno - WRB_SEQNO(wrb);
+ nllvdbg("ACK: wrb=%p trim %d bytes\n", wrb, trimlen);
+ WRB_TRIM(wrb, trimlen);
- nllvdbg("ACK: seqno=%d pktlen=%d\n",
- WRB_SEQNO(wrb), WRB_PKTLEN(wrb));
+ /* Set the new sequence number for what remains */
+
+ WRB_SEQNO(wrb) = ackno;
+ nllvdbg("ACK: wrb=%p seqno=%d pktlen=%d\n",
+ wrb, WRB_SEQNO(wrb), WRB_PKTLEN(wrb));
}
}
}
@@ -300,8 +306,8 @@ static uint16_t send_interrupt(FAR struct uip_driver_s *dev, FAR void *pvconn,
nacked = WRB_SENT(wrb);
}
- nllvdbg("ACK: seqno=%d nacked=%d sent=%d ackno=%d\n",
- WRB_SEQNO(wrb), nacked, WRB_SENT(wrb), ackno);
+ nllvdbg("ACK: wrb=%p seqno=%d nacked=%d sent=%d ackno=%d\n",
+ wrb, WRB_SEQNO(wrb), nacked, WRB_SENT(wrb), ackno);
/* Trim the ACKed bytes from the beginning of the write buffer. */
@@ -309,8 +315,8 @@ static uint16_t send_interrupt(FAR struct uip_driver_s *dev, FAR void *pvconn,
WRB_SEQNO(wrb) = ackno;
WRB_SENT(wrb) -= nacked;
- nllvdbg("ACK: seqno=%d pktlen=%d sent=%d\n",
- WRB_SEQNO(wrb), WRB_PKTLEN(wrb), WRB_SENT(wrb));
+ nllvdbg("ACK: wrb=%p seqno=%d pktlen=%d sent=%d\n",
+ wrb, WRB_SEQNO(wrb), WRB_PKTLEN(wrb), WRB_SENT(wrb));
}
}
@@ -340,13 +346,46 @@ static uint16_t send_interrupt(FAR struct uip_driver_s *dev, FAR void *pvconn,
nllvdbg("REXMIT: %04x\n", flags);
/* If there is a partially sent write buffer at the head of the
- * write_q, reset the number of bytes sent.
+ * write_q? Has anything been sent from that write buffer?
*/
- wrb = (FAR struct tcp_wrbuffer_s*)sq_peek(&conn->write_q);
- if (wrb)
+ wrb = (FAR struct tcp_wrbuffer_s *)sq_peek(&conn->write_q);
+ if (wrb && WRB_SENT(wrb) > 0)
{
+ FAR struct tcp_wrbuffer_s *tmp;
+
+ /* Yes.. Reset the number of bytes sent sent from the write buffer */
+
WRB_SENT(wrb) = 0;
+
+ /* Increment the retransmit count on this write buffer. */
+
+ if (++WRB_NRTX(wrb) >= UIP_MAXRTX)
+ {
+ nlldbg("Expiring wrb=%p nrtx=%d\n", wrb, WRB_NRTX(wrb));
+
+ /* The maximum retry count as been exhausted. Remove the write
+ * buffer at the head of the queue.
+ */
+
+ tmp = (FAR struct tcp_wrbuffer_s *)sq_remfirst(&conn->write_q);
+ DEBUGASSERT(tmp == wrb);
+ UNUSED(wrb);
+
+ /* And return the write buffer to the free list */
+
+ tcp_wrbuffer_release(wrb);
+
+ /* NOTE expired is different from un-ACKed, it is designed to
+ * represent the number of segments that have been sent,
+ * retransmitted, and un-ACKed, if expired is not zero, the
+ * connection will be closed.
+ *
+ * field expired can only be updated at UIP_ESTABLISHED state
+ */
+
+ conn->expired++;
+ }
}
/* Move all segments that have been sent but not ACKed to the write
@@ -360,8 +399,10 @@ static uint16_t send_interrupt(FAR struct uip_driver_s *dev, FAR void *pvconn,
/* Free any write buffers that have exceed the retry count */
- if (WRB_NRTX(wrb) >= UIP_MAXRTX)
+ if (++WRB_NRTX(wrb) >= UIP_MAXRTX)
{
+ nlldbg("Expiring wrb=%p nrtx=%d\n", wrb, WRB_NRTX(wrb));
+
/* Return the write buffer to the free list */
tcp_wrbuffer_release(wrb);
@@ -433,114 +474,112 @@ static uint16_t send_interrupt(FAR struct uip_driver_s *dev, FAR void *pvconn,
size_t sndlen;
/* Peek at the head of the write queue (but don't remove anything
- * from the write queue yet.
+ * from the write queue yet). We know from the above test that
+ * the write_q is not empty.
*/
wrb = (FAR struct tcp_wrbuffer_s *)sq_peek(&conn->write_q);
- if (wrb)
+ DEBUGASSERT(wrb);
+
+ /* Get the amount of data that we can send in the next packet.
+ * We will send either the remaining data in the buffer I/O
+ * buffer chain, or as much as will fit given the MSS and current
+ * window size.
+ */
+
+ sndlen = WRB_PKTLEN(wrb) - WRB_SENT(wrb);
+ if (sndlen > uip_mss(conn))
{
- /* Get the amount of data that we can send in the next packet.
- * We will send either the remaining data in the buffer I/O
- * buffer chain, or as much as will fit given the MSS and current
- * window size.
- */
+ sndlen = uip_mss(conn);
+ }
- sndlen = WRB_PKTLEN(wrb) - WRB_SENT(wrb);
- if (sndlen > uip_mss(conn))
- {
- sndlen = uip_mss(conn);
- }
+ if (sndlen > conn->winsize)
+ {
+ sndlen = conn->winsize;
+ }
- if (sndlen > conn->winsize)
- {
- sndlen = conn->winsize;
- }
+ nllvdbg("SEND: wrb=%p pktlen=%d sent=%d sndlen=%d\n",
+ wrb, WRB_PKTLEN(wrb), WRB_SENT(wrb), sndlen);
- nllvdbg("pktlen=%d sent=%d sndlen=%d\n",
- WRB_PKTLEN(wrb), WRB_SENT(wrb), sndlen);
+ /* Is this the first we have tried to send from this
+ * write buffer?
+ */
- /* Is this the first we have tried to send from this
- * write buffer?
+ if (WRB_SENT(wrb) == 0)
+ {
+ /* Yes.. Set the sequence number for this segment. If
+ * we are retransmitting, then the sequence number will
+ * already be set for this write buffer.
*/
-
- if (WRB_SENT(wrb) == 0)
+
+ if (WRB_SEQNO(wrb) == (unsigned)-1)
{
- /* Yes..Set the sequence number for this segment.
- * NOTE: the TCP stack updates sndseq on receipt of ACK
- * *before* this function is called. In that case sndseq
- * will point to the next unacknowledged byte (which might
- * have already been sent). We will overwrite the value of
- * sndseq here before the packet is sent.
- */
-
- if (WRB_NRTX(wrb) == 0 && WRB_SEQNO(wrb) == (unsigned)-1)
- {
- WRB_SEQNO(wrb) = conn->isn + conn->sent;
- }
-
- uip_tcpsetsequence(conn->sndseq, WRB_SEQNO(wrb));
+ WRB_SEQNO(wrb) = conn->isn + conn->sent;
}
- /* Then set-up to send that amount of data with the offset
- * corresponding to the amount of data already sent. (this
- * won't* actually happen until the polling cycle completes).
+ /* The TCP stack updates sndseq on receipt of ACK *before*
+ * this function is called. In that case sndseq will point
+ * to the next unacknowledged byte (which might have already
+ * been sent). We will overwrite the value of sndseq here
+ * before the packet is sent.
*/
- uip_iobsend(dev, WRB_IOB(wrb), sndlen, WRB_SENT(wrb));
+ uip_tcpsetsequence(conn->sndseq, WRB_SEQNO(wrb));
+ }
- /* Remember how much data we send out now so that we know
- * when everything has been acknowledged. Just increment
- * the amount of data sent. This will be needed in
- * sequence* number calculations and we know that this is
- * not a re-transmission. Re-transmissions do not go through
- * this path.
- */
+ /* Then set-up to send that amount of data with the offset
+ * corresponding to the amount of data already sent. (this
+ * won't actually happen until the polling cycle completes).
+ */
- if (WRB_NRTX(wrb) == 0)
- {
- conn->unacked += sndlen;
- conn->sent += sndlen;
- }
+ uip_iobsend(dev, WRB_IOB(wrb), sndlen, WRB_SENT(wrb));
- /* Increment the retransmission counter before expiration.
- * NOTE we will not calculate the retransmission timer
- * (RTT) to save cpu cycles, each send_insert_seqment
- * segment will be retransmitted UIP_MAXRTX times in halt-
- * second interval before expiration.
- */
+ /* Remember how much data we send out now so that we know
+ * when everything has been acknowledged. Just increment
+ * the amount of data sent. This will be needed in
+ * sequence number calculations and we know that this is
+ * not a re-transmission. Re-transmissions do not go through
+ * this path.
+ */
- WRB_NRTX(wrb)++;
- nllvdbg("nrtx=%d unacked=%d sent=%d\n",
- WRB_NRTX(wrb), conn->unacked, conn->sent);
+ if (WRB_NRTX(wrb) == 0)
+ {
+ conn->unacked += sndlen;
+ conn->sent += sndlen;
+ }
- /* Remove the write buffer from the write queue if the
- * last of the data has been sent from the buffer.
- */
+ nllvdbg("SEND: nrtx=%d unacked=%d sent=%d\n",
+ WRB_NRTX(wrb), conn->unacked, conn->sent);
- WRB_SENT(wrb) += sndlen;
- DEBUGASSERT(WRB_SENT(wrb) <= WRB_PKTLEN(wrb));
+ /* Increment the count of bytes sent from this write buffer */
- if (WRB_SENT(wrb) >= WRB_PKTLEN(wrb))
- {
- FAR struct tcp_wrbuffer_s *tmp;
+ WRB_SENT(wrb) += sndlen;
+ DEBUGASSERT(WRB_SENT(wrb) <= WRB_PKTLEN(wrb));
- tmp = (FAR struct tcp_wrbuffer_s *)sq_remfirst(&conn->write_q);
- DEBUGASSERT(tmp == wrb);
- UNUSED(tmp);
+ /* Remove the write buffer from the write queue if the
+ * last of the data has been sent from the buffer.
+ */
- /* Put the I/O buffer chin in the unacked queue; the
- * segment is waiting for ACK again
- */
+ if (WRB_SENT(wrb) >= WRB_PKTLEN(wrb))
+ {
+ FAR struct tcp_wrbuffer_s *tmp;
- send_insert_seqment(wrb, &conn->unacked_q);
- }
+ tmp = (FAR struct tcp_wrbuffer_s *)sq_remfirst(&conn->write_q);
+ DEBUGASSERT(tmp == wrb);
+ UNUSED(tmp);
- /* Only one data can be sent by low level driver at once,
- * tell the caller stop polling the other connection.
+ /* Put the I/O buffer chain in the un-acked queue; the
+ * segment is waiting for ACK again
*/
- flags &= ~UIP_POLL;
+ send_insert_seqment(wrb, &conn->unacked_q);
}
+
+ /* Only one data can be sent by low level driver at once,
+ * tell the caller stop polling the other connection.
+ */
+
+ flags &= ~UIP_POLL;
}
}
@@ -690,7 +729,9 @@ ssize_t psock_send(FAR struct socket *psock, FAR const void *buf, size_t len,
*/
sq_addlast(&wrb->wb_node, &conn->write_q);
- nvdbg("Queued WRB=%p pktlen=%d\n", wrb, WRB_PKTLEN(wrb));
+ nvdbg("Queued WRB=%p pktlen=%d write_q(%p,%p)\n",
+ wrb, WRB_PKTLEN(wrb),
+ conn->write_q.head, conn->write_q.tail);
/* Notify the device driver of the availability of TX data */