aboutsummaryrefslogtreecommitdiff
path: root/net/sctp/outqueue.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/sctp/outqueue.c')
-rw-r--r--net/sctp/outqueue.c822
1 files changed, 403 insertions, 419 deletions
diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c
index 1bb3c5c35d2..9c77947c059 100644
--- a/net/sctp/outqueue.c
+++ b/net/sctp/outqueue.c
@@ -22,16 +22,12 @@
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * along with GNU CC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
* Please send any bug reports or fixes you make to the
* email address(es):
- * lksctp developers <lksctp-developers@lists.sourceforge.net>
- *
- * Or submit a bug report through the following website:
- * http://www.sf.net/projects/lksctp
+ * lksctp developers <linux-sctp@vger.kernel.org>
*
* Written or modified by:
* La Monte H.P. Yarroll <piggy@acm.org>
@@ -41,15 +37,15 @@
* Hui Huang <hui.huang@nokia.com>
* Sridhar Samudrala <sri@us.ibm.com>
* Jon Grimm <jgrimm@us.ibm.com>
- *
- * Any bugs reported given to us we will try to fix... any fixes shared will
- * be incorporated into the next SCTP release.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/types.h>
#include <linux/list.h> /* For struct list_head */
#include <linux/socket.h>
#include <linux/ip.h>
+#include <linux/slab.h>
#include <net/sock.h> /* For skb_set_owner_w */
#include <net/sctp/sctp.h>
@@ -60,8 +56,9 @@ static int sctp_acked(struct sctp_sackhdr *sack, __u32 tsn);
static void sctp_check_transmitted(struct sctp_outq *q,
struct list_head *transmitted_queue,
struct sctp_transport *transport,
+ union sctp_addr *saddr,
struct sctp_sackhdr *sack,
- __u32 highest_new_tsn);
+ __u32 *highest_new_tsn);
static void sctp_mark_missing(struct sctp_outq *q,
struct list_head *transmitted_queue,
@@ -71,13 +68,14 @@ static void sctp_mark_missing(struct sctp_outq *q,
static void sctp_generate_fwdtsn(struct sctp_outq *q, __u32 sack_ctsn);
+static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout);
+
/* Add data to the front of the queue. */
static inline void sctp_outq_head_data(struct sctp_outq *q,
struct sctp_chunk *ch)
{
list_add(&ch->list, &q->out_chunk_list);
q->out_qlen += ch->skb->len;
- return;
}
/* Take data from the front of the queue. */
@@ -100,7 +98,6 @@ static inline void sctp_outq_tail_data(struct sctp_outq *q,
{
list_add_tail(&ch->list, &q->out_chunk_list);
q->out_qlen += ch->skb->len;
- return;
}
/*
@@ -113,7 +110,7 @@ static inline int sctp_cacc_skip_3_1_d(struct sctp_transport *primary,
struct sctp_transport *transport,
int count_of_newacks)
{
- if (count_of_newacks >=2 && transport != primary)
+ if (count_of_newacks >= 2 && transport != primary)
return 1;
return 0;
}
@@ -128,7 +125,8 @@ static inline int sctp_cacc_skip_3_1_d(struct sctp_transport *primary,
static inline int sctp_cacc_skip_3_1_f(struct sctp_transport *transport,
int count_of_newacks)
{
- if (count_of_newacks < 2 && !transport->cacc.cacc_saw_newack)
+ if (count_of_newacks < 2 &&
+ (transport && !transport->cacc.cacc_saw_newack))
return 1;
return 0;
}
@@ -174,13 +172,13 @@ static inline int sctp_cacc_skip_3_2(struct sctp_transport *primary, __u32 tsn)
* 3) If the missing report count for TSN t is to be
* incremented according to [RFC2960] and
* [SCTP_STEWART-2002], and CHANGEOVER_ACTIVE is set,
- * then the sender MUST futher execute steps 3.1 and
+ * then the sender MUST further execute steps 3.1 and
* 3.2 to determine if the missing report count for
* TSN t SHOULD NOT be incremented.
*
* 3.3) If 3.1 and 3.2 do not dictate that the missing
* report count for t should not be incremented, then
- * the sender SOULD increment missing report count for
+ * the sender SHOULD increment missing report count for
* t (according to [RFC2960] and [SCTP_STEWART_2002]).
*/
static inline int sctp_cacc_skip(struct sctp_transport *primary,
@@ -189,8 +187,8 @@ static inline int sctp_cacc_skip(struct sctp_transport *primary,
__u32 tsn)
{
if (primary->cacc.changeover_active &&
- (sctp_cacc_skip_3_1(primary, transport, count_of_newacks)
- || sctp_cacc_skip_3_2(primary, tsn)))
+ (sctp_cacc_skip_3_1(primary, transport, count_of_newacks) ||
+ sctp_cacc_skip_3_2(primary, tsn)))
return 1;
return 0;
}
@@ -201,32 +199,27 @@ static inline int sctp_cacc_skip(struct sctp_transport *primary,
*/
void sctp_outq_init(struct sctp_association *asoc, struct sctp_outq *q)
{
+ memset(q, 0, sizeof(struct sctp_outq));
+
q->asoc = asoc;
INIT_LIST_HEAD(&q->out_chunk_list);
INIT_LIST_HEAD(&q->control_chunk_list);
INIT_LIST_HEAD(&q->retransmit);
INIT_LIST_HEAD(&q->sacked);
INIT_LIST_HEAD(&q->abandoned);
-
- q->outstanding_bytes = 0;
- q->empty = 1;
- q->cork = 0;
-
- q->malloced = 0;
- q->out_qlen = 0;
}
/* Free the outqueue structure and any related pending chunks.
*/
-void sctp_outq_teardown(struct sctp_outq *q)
+static void __sctp_outq_teardown(struct sctp_outq *q)
{
struct sctp_transport *transport;
- struct list_head *lchunk, *pos, *temp;
+ struct list_head *lchunk, *temp;
struct sctp_chunk *chunk, *tmp;
/* Throw away unacknowledged chunks. */
- list_for_each(pos, &q->asoc->peer.transport_addr_list) {
- transport = list_entry(pos, struct sctp_transport, transports);
+ list_for_each_entry(transport, &q->asoc->peer.transport_addr_list,
+ transports) {
while ((lchunk = sctp_list_dequeue(&transport->transmitted)) != NULL) {
chunk = list_entry(lchunk, struct sctp_chunk,
transmitted_list);
@@ -271,8 +264,6 @@ void sctp_outq_teardown(struct sctp_outq *q)
sctp_chunk_free(chunk);
}
- q->error = 0;
-
/* Throw away any leftover control chunks. */
list_for_each_entry_safe(chunk, tmp, &q->control_chunk_list, list) {
list_del_init(&chunk->list);
@@ -280,31 +271,34 @@ void sctp_outq_teardown(struct sctp_outq *q)
}
}
+void sctp_outq_teardown(struct sctp_outq *q)
+{
+ __sctp_outq_teardown(q);
+ sctp_outq_init(q->asoc, q);
+}
+
/* Free the outqueue structure and any related pending chunks. */
void sctp_outq_free(struct sctp_outq *q)
{
/* Throw away leftover chunks. */
- sctp_outq_teardown(q);
-
- /* If we were kmalloc()'d, free the memory. */
- if (q->malloced)
- kfree(q);
+ __sctp_outq_teardown(q);
}
/* Put a new chunk in an sctp_outq. */
int sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk)
{
+ struct net *net = sock_net(q->asoc->base.sk);
int error = 0;
- SCTP_DEBUG_PRINTK("sctp_outq_tail(%p, %p[%s])\n",
- q, chunk, chunk && chunk->chunk_hdr ?
- sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type))
- : "Illegal Chunk");
+ pr_debug("%s: outq:%p, chunk:%p[%s]\n", __func__, q, chunk,
+ chunk && chunk->chunk_hdr ?
+ sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)) :
+ "illegal chunk");
/* If it is data, queue it up, otherwise, send it
* immediately.
*/
- if (SCTP_CID_DATA == chunk->chunk_hdr->type) {
+ if (sctp_chunk_is_data(chunk)) {
/* Is it OK to queue data chunks? */
/* From 9. Termination of Association
*
@@ -315,7 +309,6 @@ int sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk)
* chunk.
*/
switch (q->asoc->state) {
- case SCTP_STATE_EMPTY:
case SCTP_STATE_CLOSED:
case SCTP_STATE_SHUTDOWN_PENDING:
case SCTP_STATE_SHUTDOWN_SENT:
@@ -326,22 +319,21 @@ int sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk)
break;
default:
- SCTP_DEBUG_PRINTK("outqueueing (%p, %p[%s])\n",
- q, chunk, chunk && chunk->chunk_hdr ?
- sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type))
- : "Illegal Chunk");
+ pr_debug("%s: outqueueing: outq:%p, chunk:%p[%s])\n",
+ __func__, q, chunk, chunk && chunk->chunk_hdr ?
+ sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)) :
+ "illegal chunk");
sctp_outq_tail_data(q, chunk);
if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
- SCTP_INC_STATS(SCTP_MIB_OUTUNORDERCHUNKS);
+ SCTP_INC_STATS(net, SCTP_MIB_OUTUNORDERCHUNKS);
else
- SCTP_INC_STATS(SCTP_MIB_OUTORDERCHUNKS);
- q->empty = 0;
+ SCTP_INC_STATS(net, SCTP_MIB_OUTORDERCHUNKS);
break;
}
} else {
list_add_tail(&chunk->list, &q->control_chunk_list);
- SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS);
+ SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS);
}
if (error < 0)
@@ -403,11 +395,11 @@ void sctp_retransmit_mark(struct sctp_outq *q,
* not be retransmitted
*/
if (!chunk->tsn_gap_acked) {
- chunk->transport->flight_size -=
- sctp_data_size(chunk);
+ if (chunk->transport)
+ chunk->transport->flight_size -=
+ sctp_data_size(chunk);
q->outstanding_bytes -= sctp_data_size(chunk);
- q->asoc->peer.rwnd += (sctp_data_size(chunk) +
- sizeof(struct sk_buff));
+ q->asoc->peer.rwnd += sctp_data_size(chunk);
}
continue;
}
@@ -417,17 +409,8 @@ void sctp_retransmit_mark(struct sctp_outq *q,
* be added to the retransmit queue.
*/
if ((reason == SCTP_RTXR_FAST_RTX &&
- (chunk->fast_retransmit > 0)) ||
+ (chunk->fast_retransmit == SCTP_NEED_FRTX)) ||
(reason != SCTP_RTXR_FAST_RTX && !chunk->tsn_gap_acked)) {
- /* If this chunk was sent less then 1 rto ago, do not
- * retransmit this chunk, but give the peer time
- * to acknowlege it. Do this only when
- * retransmitting due to T3 timeout.
- */
- if (reason == SCTP_RTXR_T3_RTX &&
- (jiffies - chunk->sent_at) < transport->last_rto)
- continue;
-
/* RFC 2960 6.2.1 Processing a Received SACK
*
* C) Any time a DATA chunk is marked for
@@ -436,10 +419,10 @@ void sctp_retransmit_mark(struct sctp_outq *q,
* (Section 7.2.4)), add the data size of those
* chunks to the rwnd.
*/
- q->asoc->peer.rwnd += (sctp_data_size(chunk) +
- sizeof(struct sk_buff));
+ q->asoc->peer.rwnd += sctp_data_size(chunk);
q->outstanding_bytes -= sctp_data_size(chunk);
- transport->flight_size -= sctp_data_size(chunk);
+ if (chunk->transport)
+ transport->flight_size -= sctp_data_size(chunk);
/* sctpimpguide-05 Section 2.8.2
* M5) If a T3-rtx timer expires, the
@@ -459,6 +442,8 @@ void sctp_retransmit_mark(struct sctp_outq *q,
transport->rto_pending = 0;
}
+ chunk->resent = 1;
+
/* Move the chunk to the retransmit queue. The chunks
* on the retransmit queue are always kept in order.
*/
@@ -467,14 +452,10 @@ void sctp_retransmit_mark(struct sctp_outq *q,
}
}
- SCTP_DEBUG_PRINTK("%s: transport: %p, reason: %d, "
- "cwnd: %d, ssthresh: %d, flight_size: %d, "
- "pba: %d\n", __FUNCTION__,
- transport, reason,
- transport->cwnd, transport->ssthresh,
- transport->flight_size,
- transport->partial_bytes_acked);
-
+ pr_debug("%s: transport:%p, reason:%d, cwnd:%d, ssthresh:%d, "
+ "flight_size:%d, pba:%d\n", __func__, transport, reason,
+ transport->cwnd, transport->ssthresh, transport->flight_size,
+ transport->partial_bytes_acked);
}
/* Mark all the eligible packets on a transport for retransmission and force
@@ -483,27 +464,32 @@ void sctp_retransmit_mark(struct sctp_outq *q,
void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport,
sctp_retransmit_reason_t reason)
{
+ struct net *net = sock_net(q->asoc->base.sk);
int error = 0;
- switch(reason) {
+ switch (reason) {
case SCTP_RTXR_T3_RTX:
- SCTP_INC_STATS(SCTP_MIB_T3_RETRANSMITS);
+ SCTP_INC_STATS(net, SCTP_MIB_T3_RETRANSMITS);
sctp_transport_lower_cwnd(transport, SCTP_LOWER_CWND_T3_RTX);
/* Update the retran path if the T3-rtx timer has expired for
* the current retran path.
*/
if (transport == transport->asoc->peer.retran_path)
sctp_assoc_update_retran_path(transport->asoc);
+ transport->asoc->rtx_data_chunks +=
+ transport->asoc->unack_data;
break;
case SCTP_RTXR_FAST_RTX:
- SCTP_INC_STATS(SCTP_MIB_FAST_RETRANSMITS);
+ SCTP_INC_STATS(net, 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);
+ SCTP_INC_STATS(net, SCTP_MIB_PMTUD_RETRANSMITS);
break;
case SCTP_RTXR_T1_RTX:
- SCTP_INC_STATS(SCTP_MIB_T1_RETRANSMITS);
+ SCTP_INC_STATS(net, SCTP_MIB_T1_RETRANSMITS);
+ transport->asoc->init_retries++;
break;
default:
BUG();
@@ -515,9 +501,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;
@@ -535,17 +527,21 @@ 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, *lchunk1;
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
@@ -560,12 +556,19 @@ 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) {
+ /* If the chunk is abandoned, move it to abandoned list. */
+ if (sctp_chunk_abandoned(chunk)) {
+ list_del_init(&chunk->transmitted_list);
+ sctp_insert_list(&q->abandoned,
+ &chunk->transmitted_list);
+ continue;
+ }
/* Make sure that Gap Acked TSNs are not retransmitted. A
* simple approach is just to move such TSNs out of the
@@ -573,101 +576,123 @@ 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_move_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;
+
+redo:
/* Attempt to append this chunk to the packet. */
status = sctp_packet_append_chunk(pkt, chunk);
switch (status) {
case SCTP_XMIT_PMTU_FULL:
+ if (!pkt->has_data && !pkt->has_cookie_echo) {
+ /* If this packet did not contain DATA then
+ * retransmission did not happen, so do it
+ * again. We'll ignore the error here since
+ * control chunks are already freed so there
+ * is nothing we can do.
+ */
+ sctp_packet_transmit(pkt);
+ goto redo;
+ }
+
/* 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.
+ * Otherwise, try appending this chunk again.
*/
- if (rtx_timeout) {
- list_add(lchunk, lqueue);
- lchunk = NULL;
- }
+ if (rtx_timeout || fast_rtx)
+ done = 1;
+ else
+ goto redo;
- /* 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_move_tail(&chunk->transmitted_list,
+ &transport->transmitted);
/* Mark the chunk as ineligible for fast retransmit
* after it is retransmitted.
*/
- if (chunk->fast_retransmit > 0)
- chunk->fast_retransmit = -1;
-
- *start_timer = 1;
- q->empty = 0;
+ if (chunk->fast_retransmit == SCTP_NEED_FRTX)
+ chunk->fast_retransmit = SCTP_DONT_FRTX;
- /* Retrieve a new chunk to bundle. */
- lchunk = sctp_list_dequeue(lqueue);
+ q->asoc->stats.rtxchunks++;
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(lchunk1, lqueue) {
- chunk1 = list_entry(lchunk1, struct sctp_chunk,
- 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 == SCTP_NEED_FRTX)
+ chunk1->fast_retransmit = SCTP_DONT_FRTX;
}
}
+ *start_timer = timer;
+
+ /* Clear fast retransmit hint */
+ if (fast_rtx)
+ q->fast_rtx = 0;
+
return error;
}
/* Cork the outqueue so queued chunks are really queued. */
int sctp_outq_uncork(struct sctp_outq *q)
{
- int error = 0;
if (q->cork)
q->cork = 0;
- error = sctp_outq_flush(q, 0);
- return error;
+
+ return sctp_outq_flush(q, 0);
}
+
/*
* Try to flush an outqueue.
*
@@ -677,7 +702,7 @@ int sctp_outq_uncork(struct sctp_outq *q)
* locking concerns must be made. Today we use the sock lock to protect
* this function.
*/
-int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
+static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
{
struct sctp_packet *packet;
struct sctp_packet singleton;
@@ -691,6 +716,7 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
sctp_xmit_t status;
int error = 0;
int start_timer = 0;
+ int one_packet = 0;
/* These transports have chunks to send. */
struct list_head transport_list;
@@ -710,6 +736,16 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
*/
list_for_each_entry_safe(chunk, tmp, &q->control_chunk_list, list) {
+ /* RFC 5061, 5.3
+ * F1) This means that until such time as the ASCONF
+ * containing the add is acknowledged, the sender MUST
+ * NOT use the new IP address as a source for ANY SCTP
+ * packet except on carrying an ASCONF Chunk.
+ */
+ if (asoc->src_out_of_asoc_ok &&
+ chunk->chunk_hdr->type != SCTP_CID_ASCONF)
+ continue;
+
list_del_init(&chunk->list);
/* Pick the right transport to use. */
@@ -740,7 +776,8 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
if (!new_transport)
new_transport = asoc->peer.active_path;
} else if ((new_transport->state == SCTP_INACTIVE) ||
- (new_transport->state == SCTP_UNCONFIRMED)) {
+ (new_transport->state == SCTP_UNCONFIRMED) ||
+ (new_transport->state == SCTP_PF)) {
/* If the chunk is Heartbeat or Heartbeat Ack,
* send it to chunk->transport, even if it's
* inactive.
@@ -793,20 +830,44 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
break;
case SCTP_CID_ABORT:
- case SCTP_CID_SACK:
- case SCTP_CID_HEARTBEAT:
+ if (sctp_test_T_bit(chunk)) {
+ packet->vtag = asoc->c.my_vtag;
+ }
+ /* The following chunks are "response" chunks, i.e.
+ * they are generated in response to something we
+ * received. If we are sending these, then we can
+ * send only 1 packet containing these chunks.
+ */
case SCTP_CID_HEARTBEAT_ACK:
- case SCTP_CID_SHUTDOWN:
case SCTP_CID_SHUTDOWN_ACK:
- case SCTP_CID_ERROR:
- case SCTP_CID_COOKIE_ECHO:
case SCTP_CID_COOKIE_ACK:
- case SCTP_CID_ECN_ECNE:
+ case SCTP_CID_COOKIE_ECHO:
+ case SCTP_CID_ERROR:
case SCTP_CID_ECN_CWR:
- case SCTP_CID_ASCONF:
case SCTP_CID_ASCONF_ACK:
+ one_packet = 1;
+ /* Fall through */
+
+ case SCTP_CID_SACK:
+ case SCTP_CID_HEARTBEAT:
+ case SCTP_CID_SHUTDOWN:
+ case SCTP_CID_ECN_ECNE:
+ case SCTP_CID_ASCONF:
case SCTP_CID_FWD_TSN:
- sctp_packet_transmit_chunk(packet, chunk);
+ status = sctp_packet_transmit_chunk(packet, chunk,
+ one_packet);
+ if (status != SCTP_XMIT_OK) {
+ /* put the chunk back */
+ list_add(&chunk->list, &q->control_chunk_list);
+ } else {
+ asoc->stats.octrlchunks++;
+ /* PR-SCTP C5) If a FORWARD TSN is sent, the
+ * sender MUST assure that at least one T3-rtx
+ * timer is running.
+ */
+ if (chunk->chunk_hdr->type == SCTP_CID_FWD_TSN)
+ sctp_transport_reset_timers(transport);
+ }
break;
default:
@@ -815,6 +876,9 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
}
}
+ if (q->asoc->src_out_of_asoc_ok)
+ goto sctp_flush_out;
+
/* Is it OK to send data chunks? */
switch (asoc->state) {
case SCTP_STATE_COOKIE_ECHOED:
@@ -838,6 +902,8 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
* current cwnd).
*/
if (!list_empty(&q->retransmit)) {
+ if (asoc->peer.retran_path->state == SCTP_UNCONFIRMED)
+ goto sctp_flush_out;
if (transport == asoc->peer.retran_path)
goto retran;
@@ -873,8 +939,15 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
goto sctp_flush_out;
}
+ /* Apply Max.Burst limitation to the current transport in
+ * case it will be used for new data. We are going to
+ * rest it before we return, but we want to apply the limit
+ * to the currently queued data.
+ */
+ if (transport)
+ sctp_transport_burst_limited(transport);
+
/* Finally, transmit new packets. */
- start_timer = 0;
while ((chunk = sctp_outq_dequeue_data(q)) != NULL) {
/* RFC 2960 6.5 Every DATA chunk MUST carry a valid
* stream identifier.
@@ -901,8 +974,11 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
new_transport = chunk->transport;
if (!new_transport ||
((new_transport->state == SCTP_INACTIVE) ||
- (new_transport->state == SCTP_UNCONFIRMED)))
+ (new_transport->state == SCTP_UNCONFIRMED) ||
+ (new_transport->state == SCTP_PF)))
new_transport = asoc->peer.active_path;
+ if (new_transport->state == SCTP_UNCONFIRMED)
+ continue;
/* Change packets if necessary. */
if (new_transport != transport) {
@@ -919,24 +995,22 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
packet = &transport->packet;
sctp_packet_config(packet, vtag,
asoc->peer.ecn_capable);
+ /* We've switched transports, so apply the
+ * Burst limit to the new transport.
+ */
+ sctp_transport_burst_limited(transport);
}
- SCTP_DEBUG_PRINTK("sctp_outq_flush(%p, %p[%s]), ",
- q, chunk,
- chunk && chunk->chunk_hdr ?
- sctp_cname(SCTP_ST_CHUNK(
- chunk->chunk_hdr->type))
- : "Illegal Chunk");
-
- SCTP_DEBUG_PRINTK("TX TSN 0x%x skb->head "
- "%p skb->users %d.\n",
- ntohl(chunk->subh.data_hdr->tsn),
- chunk->skb ?chunk->skb->head : NULL,
- chunk->skb ?
- atomic_read(&chunk->skb->users) : -1);
+ pr_debug("%s: outq:%p, chunk:%p[%s], tx-tsn:0x%x skb->head:%p "
+ "skb->users:%d\n",
+ __func__, q, chunk, chunk && chunk->chunk_hdr ?
+ sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)) :
+ "illegal chunk", ntohl(chunk->subh.data_hdr->tsn),
+ chunk->skb ? chunk->skb->head : NULL, chunk->skb ?
+ atomic_read(&chunk->skb->users) : -1);
/* Add the chunk to the packet. */
- status = sctp_packet_transmit_chunk(packet, chunk);
+ status = sctp_packet_transmit_chunk(packet, chunk, 0);
switch (status) {
case SCTP_XMIT_PMTU_FULL:
@@ -945,15 +1019,26 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
/* We could not append this chunk, so put
* the chunk back on the output queue.
*/
- SCTP_DEBUG_PRINTK("sctp_outq_flush: could "
- "not transmit TSN: 0x%x, status: %d\n",
- ntohl(chunk->subh.data_hdr->tsn),
- status);
+ pr_debug("%s: could not transmit tsn:0x%x, status:%d\n",
+ __func__, ntohl(chunk->subh.data_hdr->tsn),
+ status);
+
sctp_outq_head_data(q, chunk);
goto sctp_flush_out;
break;
case SCTP_XMIT_OK:
+ /* The sender is in the SHUTDOWN-PENDING state,
+ * The sender MAY set the I-bit in the DATA
+ * chunk header.
+ */
+ if (asoc->state == SCTP_STATE_SHUTDOWN_PENDING)
+ chunk->chunk_hdr->flags |= SCTP_DATA_SACK_IMM;
+ if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
+ asoc->stats.ouodchunks++;
+ else
+ asoc->stats.oodchunks++;
+
break;
default:
@@ -975,8 +1060,6 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
sctp_transport_reset_timers(transport);
- q->empty = 0;
-
/* Only let one DATA chunk get bundled with a
* COOKIE-ECHO chunk.
*/
@@ -999,13 +1082,16 @@ sctp_flush_out:
*
* --xguo
*/
- while ((ltransport = sctp_list_dequeue(&transport_list)) != NULL ) {
+ while ((ltransport = sctp_list_dequeue(&transport_list)) != NULL) {
struct sctp_transport *t = list_entry(ltransport,
struct sctp_transport,
send_ready);
packet = &t->packet;
if (!sctp_packet_empty(packet))
error = sctp_packet_transmit(packet);
+
+ /* Clear the burst limited state, if any */
+ sctp_transport_burst_reset(t);
}
return error;
@@ -1030,60 +1116,34 @@ static void sctp_sack_update_unack_data(struct sctp_association *assoc,
assoc->unack_data = unack_data;
}
-/* Return the highest new tsn that is acknowledged by the given SACK chunk. */
-static __u32 sctp_highest_new_tsn(struct sctp_sackhdr *sack,
- struct sctp_association *asoc)
-{
- struct list_head *ltransport, *lchunk;
- struct sctp_transport *transport;
- struct sctp_chunk *chunk;
- __u32 highest_new_tsn, tsn;
- struct list_head *transport_list = &asoc->peer.transport_addr_list;
-
- highest_new_tsn = ntohl(sack->cum_tsn_ack);
-
- list_for_each(ltransport, transport_list) {
- transport = list_entry(ltransport, struct sctp_transport,
- transports);
- list_for_each(lchunk, &transport->transmitted) {
- chunk = list_entry(lchunk, struct sctp_chunk,
- transmitted_list);
- tsn = ntohl(chunk->subh.data_hdr->tsn);
-
- if (!chunk->tsn_gap_acked &&
- TSN_lt(highest_new_tsn, tsn) &&
- sctp_acked(sack, tsn))
- highest_new_tsn = tsn;
- }
- }
-
- return highest_new_tsn;
-}
-
/* This is where we REALLY process a SACK.
*
* Process the SACK against the outqueue. Mostly, this just frees
* things off the transmitted queue.
*/
-int sctp_outq_sack(struct sctp_outq *q, struct sctp_sackhdr *sack)
+int sctp_outq_sack(struct sctp_outq *q, struct sctp_chunk *chunk)
{
struct sctp_association *asoc = q->asoc;
+ struct sctp_sackhdr *sack = chunk->subh.sack_hdr;
struct sctp_transport *transport;
struct sctp_chunk *tchunk = NULL;
- struct list_head *lchunk, *transport_list, *pos, *temp;
+ struct list_head *lchunk, *transport_list, *temp;
sctp_sack_variable_t *frags = sack->variable;
__u32 sack_ctsn, ctsn, tsn;
__u32 highest_tsn, highest_new_tsn;
__u32 sack_a_rwnd;
- unsigned outstanding;
+ unsigned int outstanding;
struct sctp_transport *primary = asoc->peer.primary_path;
int count_of_newacks = 0;
+ int gap_ack_blocks;
+ u8 accum_moved = 0;
/* Grab the association's destination address list. */
transport_list = &asoc->peer.transport_addr_list;
sack_ctsn = ntohl(sack->cum_tsn_ack);
-
+ gap_ack_blocks = ntohs(sack->num_gap_ack_blocks);
+ asoc->stats.gapcnt += gap_ack_blocks;
/*
* SFR-CACC algorithm:
* On receipt of a SACK the sender SHOULD execute the
@@ -1093,81 +1153,82 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_sackhdr *sack)
* on the current primary, the CHANGEOVER_ACTIVE flag SHOULD be
* cleared. The CYCLING_CHANGEOVER flag SHOULD also be cleared for
* all destinations.
- */
- if (TSN_lte(primary->cacc.next_tsn_at_change, sack_ctsn)) {
- primary->cacc.changeover_active = 0;
- list_for_each(pos, transport_list) {
- transport = list_entry(pos, struct sctp_transport,
- transports);
- transport->cacc.cycling_changeover = 0;
- }
- }
-
- /*
- * SFR-CACC algorithm:
* 2) If the SACK contains gap acks and the flag CHANGEOVER_ACTIVE
* is set the receiver of the SACK MUST take the following actions:
*
* A) Initialize the cacc_saw_newack to 0 for all destination
* addresses.
+ *
+ * Only bother if changeover_active is set. Otherwise, this is
+ * totally suboptimal to do on every SACK.
*/
- if (sack->num_gap_ack_blocks &&
- primary->cacc.changeover_active) {
- list_for_each(pos, transport_list) {
- transport = list_entry(pos, struct sctp_transport,
- transports);
- transport->cacc.cacc_saw_newack = 0;
+ if (primary->cacc.changeover_active) {
+ u8 clear_cycling = 0;
+
+ if (TSN_lte(primary->cacc.next_tsn_at_change, sack_ctsn)) {
+ primary->cacc.changeover_active = 0;
+ clear_cycling = 1;
+ }
+
+ if (clear_cycling || gap_ack_blocks) {
+ list_for_each_entry(transport, transport_list,
+ transports) {
+ if (clear_cycling)
+ transport->cacc.cycling_changeover = 0;
+ if (gap_ack_blocks)
+ transport->cacc.cacc_saw_newack = 0;
+ }
}
}
/* Get the highest TSN in the sack. */
highest_tsn = sack_ctsn;
- if (sack->num_gap_ack_blocks)
- highest_tsn +=
- ntohs(frags[ntohs(sack->num_gap_ack_blocks) - 1].gab.end);
+ if (gap_ack_blocks)
+ highest_tsn += ntohs(frags[gap_ack_blocks - 1].gab.end);
- if (TSN_lt(asoc->highest_sacked, highest_tsn)) {
- highest_new_tsn = highest_tsn;
+ if (TSN_lt(asoc->highest_sacked, highest_tsn))
asoc->highest_sacked = highest_tsn;
- } else {
- highest_new_tsn = sctp_highest_new_tsn(sack, asoc);
- }
+
+ highest_new_tsn = sack_ctsn;
/* Run through the retransmit queue. Credit bytes received
* and free those chunks that we can.
*/
- sctp_check_transmitted(q, &q->retransmit, NULL, sack, highest_new_tsn);
- sctp_mark_missing(q, &q->retransmit, NULL, highest_new_tsn, 0);
+ sctp_check_transmitted(q, &q->retransmit, NULL, NULL, sack, &highest_new_tsn);
/* Run through the transmitted queue.
* Credit bytes received and free those chunks which we can.
*
* This is a MASSIVE candidate for optimization.
*/
- list_for_each(pos, transport_list) {
- transport = list_entry(pos, struct sctp_transport,
- transports);
+ list_for_each_entry(transport, transport_list, transports) {
sctp_check_transmitted(q, &transport->transmitted,
- transport, sack, highest_new_tsn);
+ transport, &chunk->source, sack,
+ &highest_new_tsn);
/*
* SFR-CACC algorithm:
* C) Let count_of_newacks be the number of
* destinations for which cacc_saw_newack is set.
*/
if (transport->cacc.cacc_saw_newack)
- count_of_newacks ++;
- }
-
- list_for_each(pos, transport_list) {
- transport = list_entry(pos, struct sctp_transport,
- transports);
- sctp_mark_missing(q, &transport->transmitted, transport,
- highest_new_tsn, count_of_newacks);
+ count_of_newacks++;
}
/* Move the Cumulative TSN Ack Point if appropriate. */
- if (TSN_lt(asoc->ctsn_ack_point, sack_ctsn))
+ if (TSN_lt(asoc->ctsn_ack_point, sack_ctsn)) {
asoc->ctsn_ack_point = sack_ctsn;
+ accum_moved = 1;
+ }
+
+ if (gap_ack_blocks) {
+
+ if (asoc->fast_recovery && accum_moved)
+ highest_new_tsn = highest_tsn;
+
+ list_for_each_entry(transport, transport_list, transports)
+ sctp_mark_missing(q, &transport->transmitted, transport,
+ highest_new_tsn, count_of_newacks);
+ }
/* Update unack_data field in the assoc. */
sctp_sack_update_unack_data(asoc, sack);
@@ -1202,38 +1263,22 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_sackhdr *sack)
sctp_generate_fwdtsn(q, sack_ctsn);
- SCTP_DEBUG_PRINTK("%s: sack Cumulative TSN Ack is 0x%x.\n",
- __FUNCTION__, sack_ctsn);
- SCTP_DEBUG_PRINTK("%s: Cumulative TSN Ack of association, "
- "%p is 0x%x. Adv peer ack point: 0x%x\n",
- __FUNCTION__, asoc, ctsn, asoc->adv_peer_ack_point);
+ pr_debug("%s: sack cumulative tsn ack:0x%x\n", __func__, sack_ctsn);
+ pr_debug("%s: cumulative tsn ack of assoc:%p is 0x%x, "
+ "advertised peer ack point:0x%x\n", __func__, asoc, ctsn,
+ asoc->adv_peer_ack_point);
- /* See if all chunks are acked.
- * Make sure the empty queue handler will get run later.
- */
- q->empty = (list_empty(&q->out_chunk_list) &&
- list_empty(&q->control_chunk_list) &&
- list_empty(&q->retransmit));
- if (!q->empty)
- goto finish;
-
- list_for_each(pos, transport_list) {
- transport = list_entry(pos, struct sctp_transport,
- transports);
- q->empty = q->empty && list_empty(&transport->transmitted);
- if (!q->empty)
- goto finish;
- }
-
- SCTP_DEBUG_PRINTK("sack queue is empty.\n");
-finish:
- return q->empty;
+ return sctp_outq_is_empty(q);
}
-/* Is the outqueue empty? */
+/* Is the outqueue empty?
+ * The queue is empty when we have not pending data, no in-flight data
+ * and nothing pending retransmissions.
+ */
int sctp_outq_is_empty(const struct sctp_outq *q)
{
- return q->empty;
+ return q->out_qlen == 0 && q->outstanding_bytes == 0 &&
+ list_empty(&q->retransmit);
}
/********************************************************************
@@ -1253,8 +1298,9 @@ int sctp_outq_is_empty(const struct sctp_outq *q)
static void sctp_check_transmitted(struct sctp_outq *q,
struct list_head *transmitted_queue,
struct sctp_transport *transport,
+ union sctp_addr *saddr,
struct sctp_sackhdr *sack,
- __u32 highest_new_tsn_in_sack)
+ __u32 *highest_new_tsn_in_sack)
{
struct list_head *lchunk;
struct sctp_chunk *tchunk;
@@ -1264,21 +1310,8 @@ static void sctp_check_transmitted(struct sctp_outq *q,
__u32 rtt;
__u8 restart_timer = 0;
int bytes_acked = 0;
-
- /* These state variables are for coherent debug output. --xguo */
-
-#if SCTP_DEBUG
- __u32 dbg_ack_tsn = 0; /* An ACKed TSN range starts here... */
- __u32 dbg_last_ack_tsn = 0; /* ...and finishes here. */
- __u32 dbg_kept_tsn = 0; /* An un-ACKed range starts here... */
- __u32 dbg_last_kept_tsn = 0; /* ...and finishes here. */
-
- /* 0 : The last TSN was ACKed.
- * 1 : The last TSN was NOT ACKed (i.e. KEPT).
- * -1: We need to initialize.
- */
- int dbg_prt_state = -1;
-#endif /* SCTP_DEBUG */
+ int migrate_bytes = 0;
+ bool forward_progress = false;
sack_ctsn = ntohl(sack->cum_tsn_ack);
@@ -1297,8 +1330,9 @@ static void sctp_check_transmitted(struct sctp_outq *q,
* considering it as 'outstanding'.
*/
if (!tchunk->tsn_gap_acked) {
- tchunk->transport->flight_size -=
- sctp_data_size(tchunk);
+ if (tchunk->transport)
+ tchunk->transport->flight_size -=
+ sctp_data_size(tchunk);
q->outstanding_bytes -= sctp_data_size(tchunk);
}
continue;
@@ -1332,6 +1366,23 @@ static void sctp_check_transmitted(struct sctp_outq *q,
rtt);
}
}
+
+ /* If the chunk hasn't been marked as ACKED,
+ * mark it and account bytes_acked if the
+ * chunk had a valid transport (it will not
+ * have a transport if ASCONF had deleted it
+ * while DATA was outstanding).
+ */
+ if (!tchunk->tsn_gap_acked) {
+ tchunk->tsn_gap_acked = 1;
+ if (TSN_lt(*highest_new_tsn_in_sack, tsn))
+ *highest_new_tsn_in_sack = tsn;
+ bytes_acked += sctp_data_size(tchunk);
+ if (!tchunk->transport)
+ migrate_bytes += sctp_data_size(tchunk);
+ forward_progress = true;
+ }
+
if (TSN_lte(tsn, sack_ctsn)) {
/* RFC 2960 6.3.2 Retransmission Timer Rules
*
@@ -1343,10 +1394,9 @@ static void sctp_check_transmitted(struct sctp_outq *q,
* current RTO.
*/
restart_timer = 1;
+ forward_progress = true;
if (!tchunk->tsn_gap_acked) {
- tchunk->tsn_gap_acked = 1;
- bytes_acked += sctp_data_size(tchunk);
/*
* SFR-CACC algorithm:
* 2) If the SACK contains gap acks
@@ -1386,66 +1436,17 @@ static void sctp_check_transmitted(struct sctp_outq *q,
* older than that newly acknowledged DATA
* chunk, are qualified as 'Stray DATA chunks'.
*/
- if (!tchunk->tsn_gap_acked) {
- tchunk->tsn_gap_acked = 1;
- bytes_acked += sctp_data_size(tchunk);
- }
list_add_tail(lchunk, &tlist);
}
-
-#if SCTP_DEBUG
- switch (dbg_prt_state) {
- case 0: /* last TSN was ACKed */
- if (dbg_last_ack_tsn + 1 == tsn) {
- /* This TSN belongs to the
- * current ACK range.
- */
- break;
- }
-
- if (dbg_last_ack_tsn != dbg_ack_tsn) {
- /* Display the end of the
- * current range.
- */
- SCTP_DEBUG_PRINTK("-%08x",
- dbg_last_ack_tsn);
- }
-
- /* Start a new range. */
- SCTP_DEBUG_PRINTK(",%08x", tsn);
- dbg_ack_tsn = tsn;
- break;
-
- case 1: /* The last TSN was NOT ACKed. */
- if (dbg_last_kept_tsn != dbg_kept_tsn) {
- /* Display the end of current range. */
- SCTP_DEBUG_PRINTK("-%08x",
- dbg_last_kept_tsn);
- }
-
- SCTP_DEBUG_PRINTK("\n");
-
- /* FALL THROUGH... */
- default:
- /* This is the first-ever TSN we examined. */
- /* Start a new range of ACK-ed TSNs. */
- SCTP_DEBUG_PRINTK("ACKed: %08x", tsn);
- dbg_prt_state = 0;
- dbg_ack_tsn = tsn;
- }
-
- dbg_last_ack_tsn = tsn;
-#endif /* SCTP_DEBUG */
-
} else {
if (tchunk->tsn_gap_acked) {
- SCTP_DEBUG_PRINTK("%s: Receiver reneged on "
- "data TSN: 0x%x\n",
- __FUNCTION__,
- tsn);
+ pr_debug("%s: receiver reneged on data TSN:0x%x\n",
+ __func__, tsn);
+
tchunk->tsn_gap_acked = 0;
- bytes_acked -= sctp_data_size(tchunk);
+ if (tchunk->transport)
+ bytes_acked -= sctp_data_size(tchunk);
/* RFC 2960 6.3.2 Retransmission Timer Rules
*
@@ -1460,61 +1461,21 @@ static void sctp_check_transmitted(struct sctp_outq *q,
}
list_add_tail(lchunk, &tlist);
-
-#if SCTP_DEBUG
- /* See the above comments on ACK-ed TSNs. */
- switch (dbg_prt_state) {
- case 1:
- if (dbg_last_kept_tsn + 1 == tsn)
- break;
-
- if (dbg_last_kept_tsn != dbg_kept_tsn)
- SCTP_DEBUG_PRINTK("-%08x",
- dbg_last_kept_tsn);
-
- SCTP_DEBUG_PRINTK(",%08x", tsn);
- dbg_kept_tsn = tsn;
- break;
-
- case 0:
- if (dbg_last_ack_tsn != dbg_ack_tsn)
- SCTP_DEBUG_PRINTK("-%08x",
- dbg_last_ack_tsn);
- SCTP_DEBUG_PRINTK("\n");
-
- /* FALL THROUGH... */
- default:
- SCTP_DEBUG_PRINTK("KEPT: %08x",tsn);
- dbg_prt_state = 1;
- dbg_kept_tsn = tsn;
- }
-
- dbg_last_kept_tsn = tsn;
-#endif /* SCTP_DEBUG */
}
}
-#if SCTP_DEBUG
- /* Finish off the last range, displaying its ending TSN. */
- switch (dbg_prt_state) {
- case 0:
- if (dbg_last_ack_tsn != dbg_ack_tsn) {
- SCTP_DEBUG_PRINTK("-%08x\n", dbg_last_ack_tsn);
- } else {
- SCTP_DEBUG_PRINTK("\n");
- }
- break;
-
- case 1:
- if (dbg_last_kept_tsn != dbg_kept_tsn) {
- SCTP_DEBUG_PRINTK("-%08x\n", dbg_last_kept_tsn);
- } else {
- SCTP_DEBUG_PRINTK("\n");
- }
- }
-#endif /* SCTP_DEBUG */
if (transport) {
if (bytes_acked) {
+ struct sctp_association *asoc = transport->asoc;
+
+ /* We may have counted DATA that was migrated
+ * to this transport due to DEL-IP operation.
+ * Subtract those bytes, since the were never
+ * send on this transport and shouldn't be
+ * credited to this transport.
+ */
+ bytes_acked -= migrate_bytes;
+
/* 8.2. When an outstanding TSN is acknowledged,
* the endpoint shall clear the error counter of
* the destination transport address to which the
@@ -1524,12 +1485,25 @@ static void sctp_check_transmitted(struct sctp_outq *q,
*/
transport->error_count = 0;
transport->asoc->overall_error_count = 0;
+ forward_progress = true;
+
+ /*
+ * While in SHUTDOWN PENDING, we may have started
+ * the T5 shutdown guard timer after reaching the
+ * retransmission limit. Stop that timer as soon
+ * as the receiver acknowledged any data.
+ */
+ if (asoc->state == SCTP_STATE_SHUTDOWN_PENDING &&
+ del_timer(&asoc->timers
+ [SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD]))
+ sctp_association_put(asoc);
/* Mark the destination transport address as
* active if it is not so marked.
*/
- if ((transport->state == SCTP_INACTIVE) ||
- (transport->state == SCTP_UNCONFIRMED)) {
+ if ((transport->state == SCTP_INACTIVE ||
+ transport->state == SCTP_UNCONFIRMED) &&
+ sctp_cmp_addr_exact(&transport->ipaddr, saddr)) {
sctp_assoc_control_transport(
transport->asoc,
transport,
@@ -1541,7 +1515,9 @@ static void sctp_check_transmitted(struct sctp_outq *q,
bytes_acked);
transport->flight_size -= bytes_acked;
- q->outstanding_bytes -= bytes_acked;
+ if (transport->flight_size == 0)
+ transport->partial_bytes_acked = 0;
+ q->outstanding_bytes -= bytes_acked + migrate_bytes;
} else {
/* RFC 2960 6.1, sctpimpguide-06 2.15.2
* When a sender is doing zero window probing, it
@@ -1552,13 +1528,18 @@ static void sctp_check_transmitted(struct sctp_outq *q,
* A sender is doing zero window probing when the
* receiver's advertised window is zero, and there is
* only one data chunk in flight to the receiver.
+ *
+ * Allow the association to timeout while in SHUTDOWN
+ * PENDING or SHUTDOWN RECEIVED in case the receiver
+ * stays in zero window mode forever.
*/
if (!q->asoc->peer.rwnd &&
!list_empty(&tlist) &&
- (sack_ctsn+2 == q->asoc->next_tsn)) {
- SCTP_DEBUG_PRINTK("%s: SACK received for zero "
- "window probe: %u\n",
- __FUNCTION__, sack_ctsn);
+ (sack_ctsn+2 == q->asoc->next_tsn) &&
+ q->asoc->state < SCTP_STATE_SHUTDOWN_PENDING) {
+ pr_debug("%s: sack received for zero window "
+ "probe:%u\n", __func__, sack_ctsn);
+
q->asoc->overall_error_count = 0;
transport->error_count = 0;
}
@@ -1571,15 +1552,18 @@ static void sctp_check_transmitted(struct sctp_outq *q,
* address.
*/
if (!transport->flight_size) {
- if (timer_pending(&transport->T3_rtx_timer) &&
- del_timer(&transport->T3_rtx_timer)) {
+ if (del_timer(&transport->T3_rtx_timer))
sctp_transport_put(transport);
- }
} else if (restart_timer) {
if (!mod_timer(&transport->T3_rtx_timer,
jiffies + transport->rto))
sctp_transport_hold(transport);
}
+
+ if (forward_progress) {
+ if (transport->dst)
+ dst_confirm(transport->dst);
+ }
}
list_splice(&tlist, transmitted_queue);
@@ -1593,14 +1577,13 @@ static void sctp_mark_missing(struct sctp_outq *q,
int count_of_newacks)
{
struct sctp_chunk *chunk;
- struct list_head *pos;
__u32 tsn;
char do_fast_retransmit = 0;
- struct sctp_transport *primary = q->asoc->peer.primary_path;
+ struct sctp_association *asoc = q->asoc;
+ struct sctp_transport *primary = asoc->peer.primary_path;
- list_for_each(pos, transmitted_queue) {
+ list_for_each_entry(chunk, transmitted_queue, transmitted_list) {
- chunk = list_entry(pos, struct sctp_chunk, transmitted_list);
tsn = ntohl(chunk->subh.data_hdr->tsn);
/* RFC 2960 7.2.4, sctpimpguide-05 2.8.2 M3) Examine all
@@ -1610,21 +1593,20 @@ static void sctp_mark_missing(struct sctp_outq *q,
* chunk if it has NOT been fast retransmitted or marked for
* fast retransmit already.
*/
- if (!chunk->fast_retransmit &&
+ if (chunk->fast_retransmit == SCTP_CAN_FRTX &&
!chunk->tsn_gap_acked &&
TSN_lt(tsn, highest_new_tsn_in_sack)) {
/* SFR-CACC may require us to skip marking
* this chunk as missing.
*/
- if (!transport || !sctp_cacc_skip(primary, transport,
- count_of_newacks, tsn)) {
+ if (!transport || !sctp_cacc_skip(primary,
+ chunk->transport,
+ count_of_newacks, tsn)) {
chunk->tsn_missing_report++;
- SCTP_DEBUG_PRINTK(
- "%s: TSN 0x%x missing counter: %d\n",
- __FUNCTION__, tsn,
- chunk->tsn_missing_report);
+ pr_debug("%s: tsn:0x%x missing counter:%d\n",
+ __func__, tsn, chunk->tsn_missing_report);
}
}
/*
@@ -1635,7 +1617,7 @@ static void sctp_mark_missing(struct sctp_outq *q,
*/
if (chunk->tsn_missing_report >= 3) {
- chunk->fast_retransmit = 1;
+ chunk->fast_retransmit = SCTP_NEED_FRTX;
do_fast_retransmit = 1;
}
}
@@ -1644,11 +1626,10 @@ static void sctp_mark_missing(struct sctp_outq *q,
if (do_fast_retransmit)
sctp_retransmit(q, transport, SCTP_RTXR_FAST_RTX);
- SCTP_DEBUG_PRINTK("%s: transport: %p, cwnd: %d, "
- "ssthresh: %d, flight_size: %d, pba: %d\n",
- __FUNCTION__, transport, transport->cwnd,
- transport->ssthresh, transport->flight_size,
- transport->partial_bytes_acked);
+ pr_debug("%s: transport:%p, cwnd:%d, ssthresh:%d, "
+ "flight_size:%d, pba:%d\n", __func__, transport,
+ transport->cwnd, transport->ssthresh,
+ transport->flight_size, transport->partial_bytes_acked);
}
}
@@ -1712,6 +1693,9 @@ static void sctp_generate_fwdtsn(struct sctp_outq *q, __u32 ctsn)
struct sctp_chunk *chunk;
struct list_head *lchunk, *temp;
+ if (!asoc->peer.prsctp_capable)
+ return;
+
/* PR-SCTP C1) Let SackCumAck be the Cumulative TSN ACK carried in the
* received SACK.
*
@@ -1799,6 +1783,6 @@ static void sctp_generate_fwdtsn(struct sctp_outq *q, __u32 ctsn)
if (ftsn_chunk) {
list_add_tail(&ftsn_chunk->list, &q->control_chunk_list);
- SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS);
+ SCTP_INC_STATS(sock_net(asoc->base.sk), SCTP_MIB_OUTCTRLCHUNKS);
}
}