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.c437
1 files changed, 175 insertions, 262 deletions
diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c
index 8c6d379b4bb..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,9 +37,6 @@
* 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
@@ -63,6 +56,7 @@ 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);
@@ -116,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;
}
@@ -131,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;
}
@@ -177,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,
@@ -204,25 +199,19 @@ 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->fast_rtx = 0;
- 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, *temp;
@@ -275,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);
@@ -284,26 +271,29 @@ 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.
@@ -319,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:
@@ -330,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)
@@ -411,8 +399,7 @@ void sctp_retransmit_mark(struct sctp_outq *q,
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;
}
@@ -432,8 +419,7 @@ 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);
if (chunk->transport)
transport->flight_size -= sctp_data_size(chunk);
@@ -456,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.
*/
@@ -464,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", __func__,
- 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
@@ -480,11 +464,12 @@ 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.
@@ -495,15 +480,15 @@ void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport,
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:
@@ -545,13 +530,11 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
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;
@@ -579,6 +562,13 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
* try to send as much as possible.
*/
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
@@ -586,9 +576,8 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
* next chunk.
*/
if (chunk->tsn_gap_acked) {
- list_del(&chunk->transmitted_list);
- list_add_tail(&chunk->transmitted_list,
- &transport->transmitted);
+ list_move_tail(&chunk->transmitted_list,
+ &transport->transmitted);
continue;
}
@@ -620,9 +609,12 @@ redo:
/* If we are retransmitting, we should only
* send a single packet.
+ * Otherwise, try appending this chunk again.
*/
if (rtx_timeout || fast_rtx)
done = 1;
+ else
+ goto redo;
/* Bundle next chunk in the next round. */
break;
@@ -649,9 +641,8 @@ redo:
/* The append was successful, so add this chunk to
* the transmitted list.
*/
- list_del(&chunk->transmitted_list);
- list_add_tail(&chunk->transmitted_list,
- &transport->transmitted);
+ list_move_tail(&chunk->transmitted_list,
+ &transport->transmitted);
/* Mark the chunk as ineligible for fast retransmit
* after it is retransmitted.
@@ -659,7 +650,7 @@ redo:
if (chunk->fast_retransmit == SCTP_NEED_FRTX)
chunk->fast_retransmit = SCTP_DONT_FRTX;
- q->empty = 0;
+ q->asoc->stats.rtxchunks++;
break;
}
@@ -695,11 +686,10 @@ redo:
/* 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);
}
@@ -746,6 +736,16 @@ static 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. */
@@ -776,7 +776,8 @@ static 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.
@@ -845,7 +846,7 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
case SCTP_CID_ECN_CWR:
case SCTP_CID_ASCONF_ACK:
one_packet = 1;
- /* Fall throught */
+ /* Fall through */
case SCTP_CID_SACK:
case SCTP_CID_HEARTBEAT:
@@ -858,12 +859,14 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
if (status != SCTP_XMIT_OK) {
/* put the chunk back */
list_add(&chunk->list, &q->control_chunk_list);
- } else if (chunk->chunk_hdr->type == SCTP_CID_FWD_TSN) {
+ } 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.
*/
- sctp_transport_reset_timers(transport);
+ if (chunk->chunk_hdr->type == SCTP_CID_FWD_TSN)
+ sctp_transport_reset_timers(transport);
}
break;
@@ -873,6 +876,9 @@ static 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:
@@ -896,6 +902,8 @@ static 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;
@@ -966,8 +974,11 @@ static 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) {
@@ -990,19 +1001,13 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
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, 0);
@@ -1014,10 +1019,10 @@ static 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;
@@ -1029,6 +1034,10 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
*/
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;
@@ -1051,8 +1060,6 @@ static 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.
*/
@@ -1075,7 +1082,7 @@ 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);
@@ -1114,9 +1121,10 @@ static void sctp_sack_update_unack_data(struct sctp_association *assoc,
* 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, *temp;
@@ -1124,7 +1132,7 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_sackhdr *sack)
__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;
@@ -1135,6 +1143,7 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_sackhdr *sack)
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
@@ -1185,7 +1194,7 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_sackhdr *sack)
/* 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_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.
@@ -1194,14 +1203,15 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_sackhdr *sack)
*/
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 ++;
+ count_of_newacks++;
}
/* Move the Cumulative TSN Ack Point if appropriate. */
@@ -1253,35 +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",
- __func__, sack_ctsn);
- SCTP_DEBUG_PRINTK("%s: Cumulative TSN Ack of association, "
- "%p is 0x%x. Adv 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->retransmit));
- if (!q->empty)
- goto finish;
+ 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);
- list_for_each_entry(transport, transport_list, 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);
}
/********************************************************************
@@ -1301,6 +1298,7 @@ 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)
{
@@ -1313,21 +1311,7 @@ static void sctp_check_transmitted(struct sctp_outq *q,
__u8 restart_timer = 0;
int bytes_acked = 0;
int migrate_bytes = 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 */
+ bool forward_progress = false;
sack_ctsn = ntohl(sack->cum_tsn_ack);
@@ -1374,6 +1358,7 @@ static void sctp_check_transmitted(struct sctp_outq *q,
* instance).
*/
if (!tchunk->tsn_gap_acked &&
+ !tchunk->resent &&
tchunk->rtt_in_progress) {
tchunk->rtt_in_progress = 0;
rtt = jiffies - tchunk->sent_at;
@@ -1390,10 +1375,12 @@ static void sctp_check_transmitted(struct sctp_outq *q,
*/
if (!tchunk->tsn_gap_acked) {
tchunk->tsn_gap_acked = 1;
- *highest_new_tsn_in_sack = tsn;
+ 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)) {
@@ -1407,6 +1394,7 @@ static void sctp_check_transmitted(struct sctp_outq *q,
* current RTO.
*/
restart_timer = 1;
+ forward_progress = true;
if (!tchunk->tsn_gap_acked) {
/*
@@ -1450,57 +1438,11 @@ static void sctp_check_transmitted(struct sctp_outq *q,
*/
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_CONT("-%08x",
- dbg_last_ack_tsn);
- }
-
- /* Start a new range. */
- SCTP_DEBUG_PRINTK_CONT(",%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_CONT("-%08x",
- dbg_last_kept_tsn);
- }
-
- SCTP_DEBUG_PRINTK_CONT("\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",
- __func__,
- tsn);
+ pr_debug("%s: receiver reneged on data TSN:0x%x\n",
+ __func__, tsn);
+
tchunk->tsn_gap_acked = 0;
if (tchunk->transport)
@@ -1519,61 +1461,13 @@ 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_CONT("-%08x",
- dbg_last_kept_tsn);
-
- SCTP_DEBUG_PRINTK_CONT(",%08x", tsn);
- dbg_kept_tsn = tsn;
- break;
-
- case 0:
- if (dbg_last_ack_tsn != dbg_ack_tsn)
- SCTP_DEBUG_PRINTK_CONT("-%08x",
- dbg_last_ack_tsn);
- SCTP_DEBUG_PRINTK_CONT("\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_CONT("-%08x\n", dbg_last_ack_tsn);
- } else {
- SCTP_DEBUG_PRINTK_CONT("\n");
- }
- break;
-
- case 1:
- if (dbg_last_kept_tsn != dbg_kept_tsn) {
- SCTP_DEBUG_PRINTK_CONT("-%08x\n", dbg_last_kept_tsn);
- } else {
- SCTP_DEBUG_PRINTK_CONT("\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
@@ -1591,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,
@@ -1621,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",
- __func__, 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;
}
@@ -1640,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);
@@ -1685,14 +1600,13 @@ static void sctp_mark_missing(struct sctp_outq *q,
/* 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",
- __func__, tsn,
- chunk->tsn_missing_report);
+ pr_debug("%s: tsn:0x%x missing counter:%d\n",
+ __func__, tsn, chunk->tsn_missing_report);
}
}
/*
@@ -1712,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",
- __func__, 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);
}
}
@@ -1870,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);
}
}