Commit 8b750ce5 authored by Vlad Yasevich's avatar Vlad Yasevich Committed by David S. Miller

sctp: Flush the queue only once during fast retransmit.

When fast retransmit is triggered by a sack, we should flush the queue
only once so that only 1 retransmit happens.  Also, since we could
potentially have non-fast-rtx chunks on the retransmit queue, we need
make sure any chunks eligable for fast retransmit are sent first
during fast retransmission.
Signed-off-by: default avatarVlad Yasevich <vladislav.yasevich@hp.com>
Tested-by: default avatarWei Yongjun <yjwei@cn.fujitsu.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 62aeaff5
...@@ -520,8 +520,14 @@ void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport, ...@@ -520,8 +520,14 @@ void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport,
* the sender SHOULD try to advance the "Advanced.Peer.Ack.Point" by * the sender SHOULD try to advance the "Advanced.Peer.Ack.Point" by
* following the procedures outlined in C1 - C5. * following the procedures outlined in C1 - C5.
*/ */
if (reason == SCTP_RTXR_T3_RTX)
sctp_generate_fwdtsn(q, q->asoc->ctsn_ack_point); sctp_generate_fwdtsn(q, q->asoc->ctsn_ack_point);
/* 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); error = sctp_outq_flush(q, /* rtx_timeout */ 1);
if (error) if (error)
...@@ -540,7 +546,6 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt, ...@@ -540,7 +546,6 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
int rtx_timeout, int *start_timer) int rtx_timeout, int *start_timer)
{ {
struct list_head *lqueue; struct list_head *lqueue;
struct list_head *lchunk;
struct sctp_transport *transport = pkt->transport; struct sctp_transport *transport = pkt->transport;
sctp_xmit_t status; sctp_xmit_t status;
struct sctp_chunk *chunk, *chunk1; struct sctp_chunk *chunk, *chunk1;
...@@ -548,12 +553,16 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt, ...@@ -548,12 +553,16 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
int fast_rtx; int fast_rtx;
int error = 0; int error = 0;
int timer = 0; int timer = 0;
int done = 0;
asoc = q->asoc; asoc = q->asoc;
lqueue = &q->retransmit; lqueue = &q->retransmit;
fast_rtx = q->fast_rtx; 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) * E3) Determine how many of the earliest (i.e., lowest TSN)
* outstanding DATA chunks for the address for which the * outstanding DATA chunks for the address for which the
...@@ -568,12 +577,12 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt, ...@@ -568,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 * [Just to be painfully clear, if we are retransmitting
* because a timeout just happened, we should send only ONE * because a timeout just happened, we should send only ONE
* packet of retransmitted data.] * 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); list_for_each_entry_safe(chunk, chunk1, lqueue, transmitted_list) {
while (lchunk) {
chunk = list_entry(lchunk, struct sctp_chunk,
transmitted_list);
/* Make sure that Gap Acked TSNs are not retransmitted. A /* Make sure that Gap Acked TSNs are not retransmitted. A
* simple approach is just to move such TSNs out of the * simple approach is just to move such TSNs out of the
...@@ -581,11 +590,18 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt, ...@@ -581,11 +590,18 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
* next chunk. * next chunk.
*/ */
if (chunk->tsn_gap_acked) { if (chunk->tsn_gap_acked) {
list_add_tail(lchunk, &transport->transmitted); list_del(&chunk->transmitted_list);
lchunk = sctp_list_dequeue(lqueue); list_add_tail(&chunk->transmitted_list,
&transport->transmitted);
continue; 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. */ /* Attempt to append this chunk to the packet. */
status = sctp_packet_append_chunk(pkt, chunk); status = sctp_packet_append_chunk(pkt, chunk);
...@@ -597,12 +613,10 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt, ...@@ -597,12 +613,10 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
/* If we are retransmitting, we should only /* If we are retransmitting, we should only
* send a single packet. * send a single packet.
*/ */
if (rtx_timeout || fast_rtx) { if (rtx_timeout || fast_rtx)
list_add(lchunk, lqueue); done = 1;
lchunk = NULL;
}
/* Bundle lchunk in the next round. */ /* Bundle next chunk in the next round. */
break; break;
case SCTP_XMIT_RWND_FULL: case SCTP_XMIT_RWND_FULL:
...@@ -612,8 +626,7 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt, ...@@ -612,8 +626,7 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
/* Stop sending DATA as there is no more room /* Stop sending DATA as there is no more room
* at the receiver. * at the receiver.
*/ */
list_add(lchunk, lqueue); done = 1;
lchunk = NULL;
break; break;
case SCTP_XMIT_NAGLE_DELAY: case SCTP_XMIT_NAGLE_DELAY:
...@@ -621,15 +634,16 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt, ...@@ -621,15 +634,16 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
error = sctp_packet_transmit(pkt); error = sctp_packet_transmit(pkt);
/* Stop sending DATA because of nagle delay. */ /* Stop sending DATA because of nagle delay. */
list_add(lchunk, lqueue); done = 1;
lchunk = NULL;
break; break;
default: default:
/* The append was successful, so add this chunk to /* The append was successful, so add this chunk to
* the transmitted list. * 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 /* Mark the chunk as ineligible for fast retransmit
* after it is retransmitted. * after it is retransmitted.
...@@ -646,9 +660,6 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt, ...@@ -646,9 +660,6 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
timer = 2; timer = 2;
q->empty = 0; q->empty = 0;
/* Retrieve a new chunk to bundle. */
lchunk = sctp_list_dequeue(lqueue);
break; break;
} }
...@@ -656,18 +667,21 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt, ...@@ -656,18 +667,21 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
if (!error && !timer) if (!error && !timer)
timer = 1; timer = 1;
if (done)
break;
}
/* If we are here due to a retransmit timeout or a fast /* If we are here due to a retransmit timeout or a fast
* retransmit and if there are any chunks left in the retransmit * retransmit and if there are any chunks left in the retransmit
* queue that could not fit in the PMTU sized packet, they need * queue that could not fit in the PMTU sized packet, they need
* to be marked as ineligible for a subsequent fast retransmit. * to be marked as ineligible for a subsequent fast retransmit.
*/ */
if (rtx_timeout && fast_rtx) { if (rtx_timeout || fast_rtx) {
list_for_each_entry(chunk1, lqueue, transmitted_list) { list_for_each_entry(chunk1, lqueue, transmitted_list) {
if (chunk1->fast_retransmit > 0) if (chunk1->fast_retransmit > 0)
chunk1->fast_retransmit = -1; chunk1->fast_retransmit = -1;
} }
} }
}
*start_timer = timer; *start_timer = timer;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment