|
@@ -208,6 +208,7 @@ void sctp_outq_init(struct sctp_association *asoc, struct sctp_outq *q)
|
|
|
INIT_LIST_HEAD(&q->sacked);
|
|
|
INIT_LIST_HEAD(&q->abandoned);
|
|
|
|
|
|
+ q->fast_rtx = 0;
|
|
|
q->outstanding_bytes = 0;
|
|
|
q->empty = 1;
|
|
|
q->cork = 0;
|
|
@@ -500,6 +501,7 @@ void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport,
|
|
|
case SCTP_RTXR_FAST_RTX:
|
|
|
SCTP_INC_STATS(SCTP_MIB_FAST_RETRANSMITS);
|
|
|
sctp_transport_lower_cwnd(transport, SCTP_LOWER_CWND_FAST_RTX);
|
|
|
+ q->fast_rtx = 1;
|
|
|
break;
|
|
|
case SCTP_RTXR_PMTUD:
|
|
|
SCTP_INC_STATS(SCTP_MIB_PMTUD_RETRANSMITS);
|
|
@@ -518,9 +520,15 @@ void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport,
|
|
|
* the sender SHOULD try to advance the "Advanced.Peer.Ack.Point" by
|
|
|
* following the procedures outlined in C1 - C5.
|
|
|
*/
|
|
|
- sctp_generate_fwdtsn(q, q->asoc->ctsn_ack_point);
|
|
|
+ if (reason == SCTP_RTXR_T3_RTX)
|
|
|
+ sctp_generate_fwdtsn(q, q->asoc->ctsn_ack_point);
|
|
|
|
|
|
- error = sctp_outq_flush(q, /* rtx_timeout */ 1);
|
|
|
+ /* Flush the queues only on timeout, since fast_rtx is only
|
|
|
+ * triggered during sack processing and the queue
|
|
|
+ * will be flushed at the end.
|
|
|
+ */
|
|
|
+ if (reason != SCTP_RTXR_FAST_RTX)
|
|
|
+ error = sctp_outq_flush(q, /* rtx_timeout */ 1);
|
|
|
|
|
|
if (error)
|
|
|
q->asoc->base.sk->sk_err = -error;
|
|
@@ -538,17 +546,23 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
|
|
|
int rtx_timeout, int *start_timer)
|
|
|
{
|
|
|
struct list_head *lqueue;
|
|
|
- struct list_head *lchunk;
|
|
|
struct sctp_transport *transport = pkt->transport;
|
|
|
sctp_xmit_t status;
|
|
|
struct sctp_chunk *chunk, *chunk1;
|
|
|
struct sctp_association *asoc;
|
|
|
+ int fast_rtx;
|
|
|
int error = 0;
|
|
|
+ int timer = 0;
|
|
|
+ int done = 0;
|
|
|
|
|
|
asoc = q->asoc;
|
|
|
lqueue = &q->retransmit;
|
|
|
+ fast_rtx = q->fast_rtx;
|
|
|
|
|
|
- /* RFC 2960 6.3.3 Handle T3-rtx Expiration
|
|
|
+ /* This loop handles time-out retransmissions, fast retransmissions,
|
|
|
+ * and retransmissions due to opening of whindow.
|
|
|
+ *
|
|
|
+ * RFC 2960 6.3.3 Handle T3-rtx Expiration
|
|
|
*
|
|
|
* E3) Determine how many of the earliest (i.e., lowest TSN)
|
|
|
* outstanding DATA chunks for the address for which the
|
|
@@ -563,12 +577,12 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
|
|
|
* [Just to be painfully clear, if we are retransmitting
|
|
|
* because a timeout just happened, we should send only ONE
|
|
|
* packet of retransmitted data.]
|
|
|
+ *
|
|
|
+ * For fast retransmissions we also send only ONE packet. However,
|
|
|
+ * if we are just flushing the queue due to open window, we'll
|
|
|
+ * try to send as much as possible.
|
|
|
*/
|
|
|
- lchunk = sctp_list_dequeue(lqueue);
|
|
|
-
|
|
|
- while (lchunk) {
|
|
|
- chunk = list_entry(lchunk, struct sctp_chunk,
|
|
|
- transmitted_list);
|
|
|
+ list_for_each_entry_safe(chunk, chunk1, lqueue, transmitted_list) {
|
|
|
|
|
|
/* Make sure that Gap Acked TSNs are not retransmitted. A
|
|
|
* simple approach is just to move such TSNs out of the
|
|
@@ -576,58 +590,60 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
|
|
|
* next chunk.
|
|
|
*/
|
|
|
if (chunk->tsn_gap_acked) {
|
|
|
- list_add_tail(lchunk, &transport->transmitted);
|
|
|
- lchunk = sctp_list_dequeue(lqueue);
|
|
|
+ list_del(&chunk->transmitted_list);
|
|
|
+ list_add_tail(&chunk->transmitted_list,
|
|
|
+ &transport->transmitted);
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
+ /* If we are doing fast retransmit, ignore non-fast_rtransmit
|
|
|
+ * chunks
|
|
|
+ */
|
|
|
+ if (fast_rtx && !chunk->fast_retransmit)
|
|
|
+ continue;
|
|
|
+
|
|
|
/* Attempt to append this chunk to the packet. */
|
|
|
status = sctp_packet_append_chunk(pkt, chunk);
|
|
|
|
|
|
switch (status) {
|
|
|
case SCTP_XMIT_PMTU_FULL:
|
|
|
/* Send this packet. */
|
|
|
- if ((error = sctp_packet_transmit(pkt)) == 0)
|
|
|
- *start_timer = 1;
|
|
|
+ error = sctp_packet_transmit(pkt);
|
|
|
|
|
|
/* If we are retransmitting, we should only
|
|
|
* send a single packet.
|
|
|
*/
|
|
|
- if (rtx_timeout) {
|
|
|
- list_add(lchunk, lqueue);
|
|
|
- lchunk = NULL;
|
|
|
- }
|
|
|
+ if (rtx_timeout || fast_rtx)
|
|
|
+ done = 1;
|
|
|
|
|
|
- /* Bundle lchunk in the next round. */
|
|
|
+ /* Bundle next chunk in the next round. */
|
|
|
break;
|
|
|
|
|
|
case SCTP_XMIT_RWND_FULL:
|
|
|
/* Send this packet. */
|
|
|
- if ((error = sctp_packet_transmit(pkt)) == 0)
|
|
|
- *start_timer = 1;
|
|
|
+ error = sctp_packet_transmit(pkt);
|
|
|
|
|
|
/* Stop sending DATA as there is no more room
|
|
|
* at the receiver.
|
|
|
*/
|
|
|
- list_add(lchunk, lqueue);
|
|
|
- lchunk = NULL;
|
|
|
+ done = 1;
|
|
|
break;
|
|
|
|
|
|
case SCTP_XMIT_NAGLE_DELAY:
|
|
|
/* Send this packet. */
|
|
|
- if ((error = sctp_packet_transmit(pkt)) == 0)
|
|
|
- *start_timer = 1;
|
|
|
+ error = sctp_packet_transmit(pkt);
|
|
|
|
|
|
/* Stop sending DATA because of nagle delay. */
|
|
|
- list_add(lchunk, lqueue);
|
|
|
- lchunk = NULL;
|
|
|
+ done = 1;
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
/* The append was successful, so add this chunk to
|
|
|
* the transmitted list.
|
|
|
*/
|
|
|
- list_add_tail(lchunk, &transport->transmitted);
|
|
|
+ list_del(&chunk->transmitted_list);
|
|
|
+ list_add_tail(&chunk->transmitted_list,
|
|
|
+ &transport->transmitted);
|
|
|
|
|
|
/* Mark the chunk as ineligible for fast retransmit
|
|
|
* after it is retransmitted.
|
|
@@ -635,27 +651,44 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
|
|
|
if (chunk->fast_retransmit > 0)
|
|
|
chunk->fast_retransmit = -1;
|
|
|
|
|
|
- *start_timer = 1;
|
|
|
- q->empty = 0;
|
|
|
+ /* Force start T3-rtx timer when fast retransmitting
|
|
|
+ * the earliest outstanding TSN
|
|
|
+ */
|
|
|
+ if (!timer && fast_rtx &&
|
|
|
+ ntohl(chunk->subh.data_hdr->tsn) ==
|
|
|
+ asoc->ctsn_ack_point + 1)
|
|
|
+ timer = 2;
|
|
|
|
|
|
- /* Retrieve a new chunk to bundle. */
|
|
|
- lchunk = sctp_list_dequeue(lqueue);
|
|
|
+ q->empty = 0;
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- /* If we are here due to a retransmit timeout or a fast
|
|
|
- * retransmit and if there are any chunks left in the retransmit
|
|
|
- * queue that could not fit in the PMTU sized packet, they need
|
|
|
- * to be marked as ineligible for a subsequent fast retransmit.
|
|
|
- */
|
|
|
- if (rtx_timeout && !lchunk) {
|
|
|
- list_for_each_entry(chunk1, lqueue, transmitted_list) {
|
|
|
- if (chunk1->fast_retransmit > 0)
|
|
|
- chunk1->fast_retransmit = -1;
|
|
|
- }
|
|
|
+ /* Set the timer if there were no errors */
|
|
|
+ if (!error && !timer)
|
|
|
+ timer = 1;
|
|
|
+
|
|
|
+ if (done)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* If we are here due to a retransmit timeout or a fast
|
|
|
+ * retransmit and if there are any chunks left in the retransmit
|
|
|
+ * queue that could not fit in the PMTU sized packet, they need
|
|
|
+ * to be marked as ineligible for a subsequent fast retransmit.
|
|
|
+ */
|
|
|
+ if (rtx_timeout || fast_rtx) {
|
|
|
+ list_for_each_entry(chunk1, lqueue, transmitted_list) {
|
|
|
+ if (chunk1->fast_retransmit > 0)
|
|
|
+ chunk1->fast_retransmit = -1;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ *start_timer = timer;
|
|
|
+
|
|
|
+ /* Clear fast retransmit hint */
|
|
|
+ if (fast_rtx)
|
|
|
+ q->fast_rtx = 0;
|
|
|
+
|
|
|
return error;
|
|
|
}
|
|
|
|
|
@@ -862,7 +895,8 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
|
|
|
rtx_timeout, &start_timer);
|
|
|
|
|
|
if (start_timer)
|
|
|
- sctp_transport_reset_timers(transport);
|
|
|
+ sctp_transport_reset_timers(transport,
|
|
|
+ start_timer-1);
|
|
|
|
|
|
/* This can happen on COOKIE-ECHO resend. Only
|
|
|
* one chunk can get bundled with a COOKIE-ECHO.
|
|
@@ -977,7 +1011,7 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
|
|
|
list_add_tail(&chunk->transmitted_list,
|
|
|
&transport->transmitted);
|
|
|
|
|
|
- sctp_transport_reset_timers(transport);
|
|
|
+ sctp_transport_reset_timers(transport, start_timer-1);
|
|
|
|
|
|
q->empty = 0;
|
|
|
|