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: Vlad Yasevich <vladislav.yasevich@hp.com> Tested-by: Wei Yongjun <yjwei@cn.fujitsu.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
committed by
David S. Miller
parent
62aeaff5cc
commit
8b750ce54b
@@ -520,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
|
* 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.
|
||||||
*/
|
*/
|
||||||
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)
|
if (error)
|
||||||
q->asoc->base.sk->sk_err = -error;
|
q->asoc->base.sk->sk_err = -error;
|
||||||
@@ -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,
|
|||||||
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,
|
|||||||
* [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,
|
|||||||
* 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,
|
|||||||
/* 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,
|
|||||||
/* 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,
|
|||||||
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,
|
|||||||
timer = 2;
|
timer = 2;
|
||||||
|
|
||||||
q->empty = 0;
|
q->empty = 0;
|
||||||
|
|
||||||
/* Retrieve a new chunk to bundle. */
|
|
||||||
lchunk = sctp_list_dequeue(lqueue);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -656,16 +667,19 @@ 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 we are here due to a retransmit timeout or a fast
|
if (done)
|
||||||
* retransmit and if there are any chunks left in the retransmit
|
break;
|
||||||
* queue that could not fit in the PMTU sized packet, they need
|
}
|
||||||
* to be marked as ineligible for a subsequent fast retransmit.
|
|
||||||
*/
|
/* If we are here due to a retransmit timeout or a fast
|
||||||
if (rtx_timeout && fast_rtx) {
|
* retransmit and if there are any chunks left in the retransmit
|
||||||
list_for_each_entry(chunk1, lqueue, transmitted_list) {
|
* queue that could not fit in the PMTU sized packet, they need
|
||||||
if (chunk1->fast_retransmit > 0)
|
* to be marked as ineligible for a subsequent fast retransmit.
|
||||||
chunk1->fast_retransmit = -1;
|
*/
|
||||||
}
|
if (rtx_timeout || fast_rtx) {
|
||||||
|
list_for_each_entry(chunk1, lqueue, transmitted_list) {
|
||||||
|
if (chunk1->fast_retransmit > 0)
|
||||||
|
chunk1->fast_retransmit = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user