Commit 62aeaff5 authored by Vlad Yasevich's avatar Vlad Yasevich Committed by David S. Miller

sctp: Start T3-RTX timer when fast retransmitting lowest TSN

When we are trying to fast retransmit the lowest outstanding TSN, we
need to restart the T3-RTX timer, so that subsequent timeouts will
correctly tag all the packets necessary for retransmissions.
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 a6465234
...@@ -1051,7 +1051,7 @@ void sctp_transport_route(struct sctp_transport *, union sctp_addr *, ...@@ -1051,7 +1051,7 @@ void sctp_transport_route(struct sctp_transport *, union sctp_addr *,
struct sctp_sock *); struct sctp_sock *);
void sctp_transport_pmtu(struct sctp_transport *); void sctp_transport_pmtu(struct sctp_transport *);
void sctp_transport_free(struct sctp_transport *); void sctp_transport_free(struct sctp_transport *);
void sctp_transport_reset_timers(struct sctp_transport *); void sctp_transport_reset_timers(struct sctp_transport *, int);
void sctp_transport_hold(struct sctp_transport *); void sctp_transport_hold(struct sctp_transport *);
void sctp_transport_put(struct sctp_transport *); void sctp_transport_put(struct sctp_transport *);
void sctp_transport_update_rto(struct sctp_transport *, __u32); void sctp_transport_update_rto(struct sctp_transport *, __u32);
...@@ -1141,6 +1141,9 @@ struct sctp_outq { ...@@ -1141,6 +1141,9 @@ struct sctp_outq {
/* How many unackd bytes do we have in-flight? */ /* How many unackd bytes do we have in-flight? */
__u32 outstanding_bytes; __u32 outstanding_bytes;
/* Are we doing fast-rtx on this queue */
char fast_rtx;
/* Corked? */ /* Corked? */
char cork; char cork;
......
...@@ -208,6 +208,7 @@ void sctp_outq_init(struct sctp_association *asoc, struct sctp_outq *q) ...@@ -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->sacked);
INIT_LIST_HEAD(&q->abandoned); INIT_LIST_HEAD(&q->abandoned);
q->fast_rtx = 0;
q->outstanding_bytes = 0; q->outstanding_bytes = 0;
q->empty = 1; q->empty = 1;
q->cork = 0; q->cork = 0;
...@@ -500,6 +501,7 @@ void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport, ...@@ -500,6 +501,7 @@ void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport,
case SCTP_RTXR_FAST_RTX: case SCTP_RTXR_FAST_RTX:
SCTP_INC_STATS(SCTP_MIB_FAST_RETRANSMITS); SCTP_INC_STATS(SCTP_MIB_FAST_RETRANSMITS);
sctp_transport_lower_cwnd(transport, SCTP_LOWER_CWND_FAST_RTX); sctp_transport_lower_cwnd(transport, SCTP_LOWER_CWND_FAST_RTX);
q->fast_rtx = 1;
break; break;
case SCTP_RTXR_PMTUD: case SCTP_RTXR_PMTUD:
SCTP_INC_STATS(SCTP_MIB_PMTUD_RETRANSMITS); SCTP_INC_STATS(SCTP_MIB_PMTUD_RETRANSMITS);
...@@ -543,10 +545,13 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt, ...@@ -543,10 +545,13 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
sctp_xmit_t status; sctp_xmit_t status;
struct sctp_chunk *chunk, *chunk1; struct sctp_chunk *chunk, *chunk1;
struct sctp_association *asoc; struct sctp_association *asoc;
int fast_rtx;
int error = 0; int error = 0;
int timer = 0;
asoc = q->asoc; asoc = q->asoc;
lqueue = &q->retransmit; lqueue = &q->retransmit;
fast_rtx = q->fast_rtx;
/* RFC 2960 6.3.3 Handle T3-rtx Expiration /* RFC 2960 6.3.3 Handle T3-rtx Expiration
* *
...@@ -587,13 +592,12 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt, ...@@ -587,13 +592,12 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
switch (status) { switch (status) {
case SCTP_XMIT_PMTU_FULL: case SCTP_XMIT_PMTU_FULL:
/* Send this packet. */ /* Send this packet. */
if ((error = sctp_packet_transmit(pkt)) == 0) error = sctp_packet_transmit(pkt);
*start_timer = 1;
/* 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) { if (rtx_timeout || fast_rtx) {
list_add(lchunk, lqueue); list_add(lchunk, lqueue);
lchunk = NULL; lchunk = NULL;
} }
...@@ -603,8 +607,7 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt, ...@@ -603,8 +607,7 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
case SCTP_XMIT_RWND_FULL: case SCTP_XMIT_RWND_FULL:
/* Send this packet. */ /* Send this packet. */
if ((error = sctp_packet_transmit(pkt)) == 0) error = sctp_packet_transmit(pkt);
*start_timer = 1;
/* Stop sending DATA as there is no more room /* Stop sending DATA as there is no more room
* at the receiver. * at the receiver.
...@@ -615,8 +618,7 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt, ...@@ -615,8 +618,7 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
case SCTP_XMIT_NAGLE_DELAY: case SCTP_XMIT_NAGLE_DELAY:
/* Send this packet. */ /* Send this packet. */
if ((error = sctp_packet_transmit(pkt)) == 0) error = sctp_packet_transmit(pkt);
*start_timer = 1;
/* Stop sending DATA because of nagle delay. */ /* Stop sending DATA because of nagle delay. */
list_add(lchunk, lqueue); list_add(lchunk, lqueue);
...@@ -635,7 +637,14 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt, ...@@ -635,7 +637,14 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
if (chunk->fast_retransmit > 0) if (chunk->fast_retransmit > 0)
chunk->fast_retransmit = -1; chunk->fast_retransmit = -1;
*start_timer = 1; /* 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;
q->empty = 0; q->empty = 0;
/* Retrieve a new chunk to bundle. */ /* Retrieve a new chunk to bundle. */
...@@ -643,12 +652,16 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt, ...@@ -643,12 +652,16 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
break; break;
} }
/* Set the timer if there were no errors */
if (!error && !timer)
timer = 1;
/* 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 && !lchunk) { 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;
...@@ -656,6 +669,12 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt, ...@@ -656,6 +669,12 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
} }
} }
*start_timer = timer;
/* Clear fast retransmit hint */
if (fast_rtx)
q->fast_rtx = 0;
return error; return error;
} }
...@@ -862,7 +881,8 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) ...@@ -862,7 +881,8 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
rtx_timeout, &start_timer); rtx_timeout, &start_timer);
if (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 /* This can happen on COOKIE-ECHO resend. Only
* one chunk can get bundled with a COOKIE-ECHO. * one chunk can get bundled with a COOKIE-ECHO.
...@@ -977,7 +997,7 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) ...@@ -977,7 +997,7 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
list_add_tail(&chunk->transmitted_list, list_add_tail(&chunk->transmitted_list,
&transport->transmitted); &transport->transmitted);
sctp_transport_reset_timers(transport); sctp_transport_reset_timers(transport, start_timer-1);
q->empty = 0; q->empty = 0;
......
...@@ -191,7 +191,7 @@ static void sctp_transport_destroy(struct sctp_transport *transport) ...@@ -191,7 +191,7 @@ static void sctp_transport_destroy(struct sctp_transport *transport)
/* Start T3_rtx timer if it is not already running and update the heartbeat /* Start T3_rtx timer if it is not already running and update the heartbeat
* timer. This routine is called every time a DATA chunk is sent. * timer. This routine is called every time a DATA chunk is sent.
*/ */
void sctp_transport_reset_timers(struct sctp_transport *transport) void sctp_transport_reset_timers(struct sctp_transport *transport, int force)
{ {
/* RFC 2960 6.3.2 Retransmission Timer Rules /* RFC 2960 6.3.2 Retransmission Timer Rules
* *
...@@ -201,7 +201,7 @@ void sctp_transport_reset_timers(struct sctp_transport *transport) ...@@ -201,7 +201,7 @@ void sctp_transport_reset_timers(struct sctp_transport *transport)
* address. * address.
*/ */
if (!timer_pending(&transport->T3_rtx_timer)) if (force || !timer_pending(&transport->T3_rtx_timer))
if (!mod_timer(&transport->T3_rtx_timer, if (!mod_timer(&transport->T3_rtx_timer,
jiffies + transport->rto)) jiffies + transport->rto))
sctp_transport_hold(transport); sctp_transport_hold(transport);
......
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