Commit ad0631ac authored by Sridhar Samudrala's avatar Sridhar Samudrala

sctp: Fixes a bug in the calculation of highest new tsn in sack.

parent 125903c5
No related merge requests found
...@@ -1315,6 +1315,9 @@ struct SCTP_association { ...@@ -1315,6 +1315,9 @@ struct SCTP_association {
__u32 ctsn_ack_point; __u32 ctsn_ack_point;
/* Highest TSN that is acknowledged by incoming SACKs. */
__u32 highest_sacked;
/* The number of unacknowledged data chunks. Reported through /* The number of unacknowledged data chunks. Reported through
* the SCTP_STATUS sockopt. * the SCTP_STATUS sockopt.
*/ */
......
...@@ -207,6 +207,7 @@ sctp_association_t *sctp_association_init(sctp_association_t *asoc, ...@@ -207,6 +207,7 @@ sctp_association_t *sctp_association_init(sctp_association_t *asoc,
asoc->next_tsn = asoc->c.initial_tsn; asoc->next_tsn = asoc->c.initial_tsn;
asoc->ctsn_ack_point = asoc->next_tsn - 1; asoc->ctsn_ack_point = asoc->next_tsn - 1;
asoc->highest_sacked = asoc->ctsn_ack_point;
asoc->unack_data = 0; asoc->unack_data = 0;
......
...@@ -59,7 +59,8 @@ static int sctp_acked(sctp_sackhdr_t *sack, __u32 tsn); ...@@ -59,7 +59,8 @@ static int sctp_acked(sctp_sackhdr_t *sack, __u32 tsn);
static void sctp_check_transmitted(sctp_outqueue_t *q, static void sctp_check_transmitted(sctp_outqueue_t *q,
struct list_head *transmitted_queue, struct list_head *transmitted_queue,
sctp_transport_t *transport, sctp_transport_t *transport,
sctp_sackhdr_t *sack); sctp_sackhdr_t *sack,
__u32 highest_new_tsn);
/* Generate a new outqueue. */ /* Generate a new outqueue. */
sctp_outqueue_t *sctp_outqueue_new(sctp_association_t *asoc) sctp_outqueue_t *sctp_outqueue_new(sctp_association_t *asoc)
...@@ -971,6 +972,36 @@ static void sctp_sack_update_unack_data(sctp_association_t *assoc, ...@@ -971,6 +972,36 @@ static void sctp_sack_update_unack_data(sctp_association_t *assoc,
assoc->unack_data = unack_data; assoc->unack_data = unack_data;
} }
/* Return the highest new tsn that is acknowledged by the given SACK chunk. */
static __u32 sctp_highest_new_tsn(sctp_sackhdr_t *sack,
sctp_association_t *asoc)
{
struct list_head *ltransport, *lchunk;
sctp_transport_t *transport;
sctp_chunk_t *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, sctp_transport_t,
transports);
list_for_each(lchunk, &transport->transmitted) {
chunk = list_entry(lchunk, sctp_chunk_t,
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. /* This is where we REALLY process a SACK.
* *
* Process the sack against the outqueue. Mostly, this just frees * Process the sack against the outqueue. Mostly, this just frees
...@@ -978,22 +1009,36 @@ static void sctp_sack_update_unack_data(sctp_association_t *assoc, ...@@ -978,22 +1009,36 @@ static void sctp_sack_update_unack_data(sctp_association_t *assoc,
*/ */
int sctp_sack_outqueue(sctp_outqueue_t *q, sctp_sackhdr_t *sack) int sctp_sack_outqueue(sctp_outqueue_t *q, sctp_sackhdr_t *sack)
{ {
sctp_association_t *asoc = q->asoc;
sctp_transport_t *transport;
sctp_chunk_t *tchunk; sctp_chunk_t *tchunk;
struct list_head *lchunk, *transport_list, *pos; struct list_head *lchunk, *transport_list, *pos;
__u32 tsn; sctp_sack_variable_t *frags = sack->variable;
__u32 sack_ctsn; __u32 sack_ctsn, ctsn, tsn;
__u32 ctsn; __u32 highest_tsn, highest_new_tsn;
sctp_transport_t *transport;
int outstanding;
__u32 sack_a_rwnd; __u32 sack_a_rwnd;
int outstanding;
/* Grab the association's destination address list. */ /* Grab the association's destination address list. */
transport_list = &q->asoc->peer.transport_addr_list; transport_list = &asoc->peer.transport_addr_list;
sack_ctsn = ntohl(sack->cum_tsn_ack);
/* Get the highest TSN in the sack. */
highest_tsn = sack_ctsn +
ntohs(frags[ntohs(sack->num_gap_ack_blocks) - 1].gab.end);
if (TSN_lt(asoc->highest_sacked, highest_tsn)) {
highest_new_tsn = highest_tsn;
asoc->highest_sacked = highest_tsn;
} else {
highest_new_tsn = sctp_highest_new_tsn(sack, asoc);
}
/* Run through the retransmit queue. Credit bytes received /* Run through the retransmit queue. Credit bytes received
* and free those chunks that we can. * and free those chunks that we can.
*/ */
sctp_check_transmitted(q, &q->retransmit, NULL, sack); sctp_check_transmitted(q, &q->retransmit, NULL, sack, highest_new_tsn);
/* Run through the transmitted queue. /* Run through the transmitted queue.
* Credit bytes received and free those chunks which we can. * Credit bytes received and free those chunks which we can.
...@@ -1003,23 +1048,22 @@ int sctp_sack_outqueue(sctp_outqueue_t *q, sctp_sackhdr_t *sack) ...@@ -1003,23 +1048,22 @@ int sctp_sack_outqueue(sctp_outqueue_t *q, sctp_sackhdr_t *sack)
list_for_each(pos, transport_list) { list_for_each(pos, transport_list) {
transport = list_entry(pos, sctp_transport_t, transports); transport = list_entry(pos, sctp_transport_t, transports);
sctp_check_transmitted(q, &transport->transmitted, sctp_check_transmitted(q, &transport->transmitted,
transport, sack); transport, sack, highest_new_tsn);
} }
/* Move the Cumulative TSN Ack Point if appropriate. */ /* Move the Cumulative TSN Ack Point if appropriate. */
sack_ctsn = ntohl(sack->cum_tsn_ack); if (TSN_lt(asoc->ctsn_ack_point, sack_ctsn))
if (TSN_lt(q->asoc->ctsn_ack_point, sack_ctsn)) asoc->ctsn_ack_point = sack_ctsn;
q->asoc->ctsn_ack_point = sack_ctsn;
/* Update unack_data field in the assoc. */ /* Update unack_data field in the assoc. */
sctp_sack_update_unack_data(q->asoc, sack); sctp_sack_update_unack_data(asoc, sack);
ctsn = q->asoc->ctsn_ack_point; ctsn = asoc->ctsn_ack_point;
SCTP_DEBUG_PRINTK("%s: sack Cumulative TSN Ack is 0x%x.\n", SCTP_DEBUG_PRINTK("%s: sack Cumulative TSN Ack is 0x%x.\n",
__FUNCTION__, sack_ctsn); __FUNCTION__, sack_ctsn);
SCTP_DEBUG_PRINTK("%s: Cumulative TSN Ack of association " SCTP_DEBUG_PRINTK("%s: Cumulative TSN Ack of association "
"%p is 0x%x.\n", __FUNCTION__, q->asoc, ctsn); "%p is 0x%x.\n", __FUNCTION__, asoc, ctsn);
/* Throw away stuff rotting on the sack queue. */ /* Throw away stuff rotting on the sack queue. */
list_for_each(lchunk, &q->sacked) { list_for_each(lchunk, &q->sacked) {
...@@ -1045,7 +1089,7 @@ int sctp_sack_outqueue(sctp_outqueue_t *q, sctp_sackhdr_t *sack) ...@@ -1045,7 +1089,7 @@ int sctp_sack_outqueue(sctp_outqueue_t *q, sctp_sackhdr_t *sack)
sack_a_rwnd = 0; sack_a_rwnd = 0;
} }
q->asoc->peer.rwnd = sack_a_rwnd; asoc->peer.rwnd = sack_a_rwnd;
/* See if all chunks are acked. /* See if all chunks are acked.
* Make sure the empty queue handler will get run later. * Make sure the empty queue handler will get run later.
...@@ -1092,7 +1136,8 @@ int sctp_outqueue_is_empty(const sctp_outqueue_t *q) ...@@ -1092,7 +1136,8 @@ int sctp_outqueue_is_empty(const sctp_outqueue_t *q)
static void sctp_check_transmitted(sctp_outqueue_t *q, static void sctp_check_transmitted(sctp_outqueue_t *q,
struct list_head *transmitted_queue, struct list_head *transmitted_queue,
sctp_transport_t *transport, sctp_transport_t *transport,
sctp_sackhdr_t *sack) sctp_sackhdr_t *sack,
__u32 highest_new_tsn_in_sack)
{ {
struct list_head *lchunk; struct list_head *lchunk;
sctp_chunk_t *tchunk; sctp_chunk_t *tchunk;
...@@ -1100,7 +1145,6 @@ static void sctp_check_transmitted(sctp_outqueue_t *q, ...@@ -1100,7 +1145,6 @@ static void sctp_check_transmitted(sctp_outqueue_t *q,
__u32 tsn; __u32 tsn;
__u32 sack_ctsn; __u32 sack_ctsn;
__u32 rtt; __u32 rtt;
__u32 highest_new_tsn_in_sack;
__u8 restart_timer = 0; __u8 restart_timer = 0;
__u8 do_fast_retransmit = 0; __u8 do_fast_retransmit = 0;
int bytes_acked = 0; int bytes_acked = 0;
...@@ -1121,7 +1165,6 @@ static void sctp_check_transmitted(sctp_outqueue_t *q, ...@@ -1121,7 +1165,6 @@ static void sctp_check_transmitted(sctp_outqueue_t *q,
#endif /* SCTP_DEBUG */ #endif /* SCTP_DEBUG */
sack_ctsn = ntohl(sack->cum_tsn_ack); sack_ctsn = ntohl(sack->cum_tsn_ack);
highest_new_tsn_in_sack = sack_ctsn;
INIT_LIST_HEAD(&tlist); INIT_LIST_HEAD(&tlist);
...@@ -1194,10 +1237,6 @@ static void sctp_check_transmitted(sctp_outqueue_t *q, ...@@ -1194,10 +1237,6 @@ static void sctp_check_transmitted(sctp_outqueue_t *q,
if (!tchunk->tsn_gap_acked) { if (!tchunk->tsn_gap_acked) {
tchunk->tsn_gap_acked = 1; tchunk->tsn_gap_acked = 1;
bytes_acked += sctp_data_size(tchunk); bytes_acked += sctp_data_size(tchunk);
if (TSN_lt(highest_new_tsn_in_sack,
tsn)) {
highest_new_tsn_in_sack = tsn;
}
} }
list_add_tail(lchunk, &tlist); list_add_tail(lchunk, &tlist);
} }
......
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