Commit 506ab679 authored by Jon Grimm's avatar Jon Grimm Committed by Jon Grimm

[SCTP] Add SFR-CACC support. (Ardelle.Fan)

Submitted by Ardelle Fan.  Implementation of Split Fast Retransmit -
Changeover Aware Congestion Control.  Certain conditions involving
multihoming changover can result in TCP-unfriendliness.  This
implements the IETF proposed method of dealing with this corner
case.
parent d1256358
......@@ -804,9 +804,6 @@ struct sctp_transport {
*/
int max_retrans;
/* We use this name for debugging output... */
char *debug_name;
/* Per : A timer used by each destination.
* Destination :
* Timer :
......@@ -832,6 +829,35 @@ struct sctp_transport {
struct list_head send_ready;
int malloced; /* Is this structure kfree()able? */
/* State information saved for SFR_CACC algorithm. The key
* idea in SFR_CACC is to maintain state at the sender on a
* per-destination basis when a changeover happens.
* char changeover_active;
* char cycling_changeover;
* __u32 next_tsn_at_change;
* char cacc_saw_newack;
*/
struct {
/* An unsigned integer, which stores the next TSN to be
* used by the sender, at the moment of changeover.
*/
__u32 next_tsn_at_change;
/* A flag which indicates the occurrence of a changeover */
char changeover_active;
/* A glag which indicates whether the change of primary is
* the first switch to this destination address during an
* active switch.
*/
char cycling_changeover;
/* A temporary flag, which is used during the processing of
* a SACK to estimate the causative TSN(s)'s group.
*/
char cacc_saw_newack;
} cacc;
};
struct sctp_transport *sctp_transport_new(const union sctp_addr *, int);
......
......@@ -256,7 +256,7 @@ struct sctp_association *sctp_association_init(struct sctp_association *asoc,
sctp_packet_transmit_chunk,
sctp_packet_transmit);
if (NULL == sctp_ulpq_init(&asoc->ulpq, asoc))
if (!sctp_ulpq_init(&asoc->ulpq, asoc))
goto fail_init;
/* Set up the tsn tracking. */
......@@ -266,7 +266,6 @@ struct sctp_association *sctp_association_init(struct sctp_association *asoc,
asoc->need_ecne = 0;
asoc->debug_name = "unnamedasoc";
asoc->eyecatcher = SCTP_ASSOC_EYECATCHER;
/* Assume that peer would support both address types unless we are
......@@ -380,6 +379,30 @@ void sctp_assoc_set_primary(struct sctp_association *asoc,
*/
if (transport->active)
asoc->peer.active_path = transport;
/*
* SFR-CACC algorithm:
* Upon the receipt of a request to change the primary
* destination address, on the data structure for the new
* primary destination, the sender MUST do the following:
*
* 1) If CHANGEOVER_ACTIVE is set, then there was a switch
* to this destination address earlier. The sender MUST set
* CYCLING_CHANGEOVER to indicate that this switch is a
* double switch to the same destination address.
*/
if (transport->cacc.changeover_active)
transport->cacc.cycling_changeover = 1;
/* 2) The sender MUST set CHANGEOVER_ACTIVE to indicate that
* a changeover has occurred.
*/
transport->cacc.changeover_active = 1;
/* 3) The sender MUST store the next TSN to be sent in
* next_tsn_at_change.
*/
transport->cacc.next_tsn_at_change = asoc->next_tsn;
}
/* Add a transport address to an association. */
......@@ -479,7 +502,7 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
list_add_tail(&peer->transports, &asoc->peer.transport_addr_list);
/* If we do not yet have a primary path, set one. */
if (NULL == asoc->peer.primary_path) {
if (!asoc->peer.primary_path) {
sctp_assoc_set_primary(asoc, peer);
asoc->peer.retran_path = peer;
}
......@@ -592,7 +615,7 @@ void sctp_assoc_control_transport(struct sctp_association *asoc,
/* If we failed to find a usable transport, just camp on the
* primary, even if it is inactive.
*/
if (NULL == first) {
if (!first) {
first = asoc->peer.primary_path;
second = asoc->peer.primary_path;
}
......
......@@ -55,13 +55,18 @@
#include <net/sctp/sctp.h>
/* Declare internal functions here. */
static int sctp_acked(sctp_sackhdr_t *sack, __u32 tsn);
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,
sctp_sackhdr_t *sack,
struct sctp_sackhdr *sack,
__u32 highest_new_tsn);
static void sctp_mark_missing(struct sctp_outq *q,
struct sctp_transport *transport,
__u32 highest_new_tsn,
int count_of_newacks);
/* Add data to the front of the queue. */
static inline void sctp_outq_head_data(struct sctp_outq *q,
struct sctp_chunk *ch)
......@@ -99,6 +104,98 @@ static inline void sctp_outq_insert_data(struct sctp_outq *q,
q->out_qlen += ch->skb->len;
}
/*
* SFR-CACC algorithm:
* D) If count_of_newacks is greater than or equal to 2
* and t was not sent to the current primary then the
* sender MUST NOT increment missing report count for t.
*/
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)
return 1;
return 0;
}
/*
* SFR-CACC algorithm:
* F) If count_of_newacks is less than 2, let d be the
* destination to which t was sent. If cacc_saw_newack
* is 0 for destination d, then the sender MUST NOT
* increment missing report count for t.
*/
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)
return 1;
return 0;
}
/*
* SFR-CACC algorithm:
* 3.1) If CYCLING_CHANGEOVER is 0, the sender SHOULD
* execute steps C, D, F.
*
* C has been implemented in sctp_outq_sack
*/
static inline int sctp_cacc_skip_3_1(struct sctp_transport *primary,
struct sctp_transport *transport,
int count_of_newacks)
{
if (!primary->cacc.cycling_changeover) {
if (sctp_cacc_skip_3_1_d(primary, transport, count_of_newacks))
return 1;
if (sctp_cacc_skip_3_1_f(transport, count_of_newacks));
return 1;
return 0;
}
return 0;
}
/*
* SFR-CACC algorithm:
* 3.2) Else if CYCLING_CHANGEOVER is 1, and t is less
* than next_tsn_at_change of the current primary, then
* the sender MUST NOT increment missing report count
* for t.
*/
static inline int sctp_cacc_skip_3_2(struct sctp_transport *primary, __u32 tsn)
{
if (primary->cacc.cycling_changeover &&
TSN_lt(tsn, primary->cacc.next_tsn_at_change))
return 1;
return 0;
}
/*
* SFR-CACC algorithm:
* 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
* 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
* t (according to [RFC2960] and [SCTP_STEWART_2002]).
*/
static inline int sctp_cacc_skip(struct sctp_transport *primary,
struct sctp_transport *transport,
int count_of_newacks,
__u32 tsn)
{
if (primary->cacc.changeover_active &&
(sctp_cacc_skip_3_1(primary, transport, count_of_newacks)
|| sctp_cacc_skip_3_2(primary, tsn)))
return 1;
return 0;
}
/* Generate a new outqueue. */
struct sctp_outq *sctp_outq_new(struct sctp_association *asoc)
{
......@@ -1085,7 +1182,7 @@ int sctp_outq_set_output_handlers(struct sctp_outq *q,
/* Update unack_data based on the incoming SACK chunk */
static void sctp_sack_update_unack_data(struct sctp_association *assoc,
sctp_sackhdr_t *sack)
struct sctp_sackhdr *sack)
{
sctp_sack_variable_t *frags;
__u16 unack_data;
......@@ -1103,7 +1200,7 @@ static void sctp_sack_update_unack_data(struct sctp_association *assoc,
}
/* Return the highest new tsn that is acknowledged by the given SACK chunk. */
static __u32 sctp_highest_new_tsn(sctp_sackhdr_t *sack,
static __u32 sctp_highest_new_tsn(struct sctp_sackhdr *sack,
struct sctp_association *asoc)
{
struct list_head *ltransport, *lchunk;
......@@ -1137,7 +1234,7 @@ static __u32 sctp_highest_new_tsn(sctp_sackhdr_t *sack,
* Process the SACK against the outqueue. Mostly, this just frees
* things off the transmitted queue.
*/
int sctp_outq_sack(struct sctp_outq *q, sctp_sackhdr_t *sack)
int sctp_outq_sack(struct sctp_outq *q, struct sctp_sackhdr *sack)
{
struct sctp_association *asoc = q->asoc;
struct sctp_transport *transport;
......@@ -1148,12 +1245,50 @@ int sctp_outq_sack(struct sctp_outq *q, sctp_sackhdr_t *sack)
__u32 highest_tsn, highest_new_tsn;
__u32 sack_a_rwnd;
int outstanding;
struct sctp_transport *primary = asoc->peer.primary_path;
int count_of_newacks = 0;
/* Grab the association's destination address list. */
transport_list = &asoc->peer.transport_addr_list;
sack_ctsn = ntohl(sack->cum_tsn_ack);
/*
* SFR-CACC algorithm:
* On receipt of a SACK the sender SHOULD execute the
* following statements.
*
* 1) If the cumulative ack in the SACK passes next tsn_at_change
* 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.
*/
if (sack->num_gap_ack_blocks > 0 &&
primary->cacc.changeover_active) {
list_for_each(pos, transport_list) {
transport = list_entry(pos, struct sctp_transport,
transports);
transport->cacc.cacc_saw_newack = 0;
}
}
/* Get the highest TSN in the sack. */
highest_tsn = sack_ctsn +
ntohs(frags[ntohs(sack->num_gap_ack_blocks) - 1].gab.end);
......@@ -1180,6 +1315,20 @@ int sctp_outq_sack(struct sctp_outq *q, sctp_sackhdr_t *sack)
transports);
sctp_check_transmitted(q, &transport->transmitted,
transport, 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, highest_new_tsn,
count_of_newacks);
}
/* Move the Cumulative TSN Ack Point if appropriate. */
......@@ -1252,12 +1401,9 @@ int sctp_outq_is_empty(const struct sctp_outq *q)
* 2nd Level Abstractions
********************************************************************/
/* Go through a transport's transmitted list or the assocication's retransmit
/* Go through a transport's transmitted list or the association's retransmit
* list and move chunks that are acked by the Cumulative TSN Ack to q->sacked.
* The retransmit list will not have an associated transport. In case of a
* transmitted list with a transport, the transport's congestion, rto and fast
* retransmit parameters are also updated and if needed a fast retransmit
* process is started.
* The retransmit list will not have an associated transport.
*
* I added coherent debug information output. --xguo
*
......@@ -1268,7 +1414,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,
sctp_sackhdr_t *sack,
struct sctp_sackhdr *sack,
__u32 highest_new_tsn_in_sack)
{
struct list_head *lchunk;
......@@ -1278,7 +1424,6 @@ static void sctp_check_transmitted(struct sctp_outq *q,
__u32 sack_ctsn;
__u32 rtt;
__u8 restart_timer = 0;
__u8 do_fast_retransmit = 0;
int bytes_acked = 0;
/* These state variables are for coherent debug output. --xguo */
......@@ -1347,6 +1492,25 @@ static void sctp_check_transmitted(struct sctp_outq *q,
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
* and the flag CHANGEOVER_ACTIVE is
* set the receiver of the SACK MUST
* take the following action:
*
* B) For each TSN t being acked that
* has not been acked in any SACK so
* far, set cacc_saw_newack to 1 for
* the destination that the TSN was
* sent to.
*/
if (transport &&
sack->num_gap_ack_blocks &&
q->asoc->peer.primary_path->cacc.
changeover_active)
transport->cacc.cacc_saw_newack
= 1;
}
list_add_tail(&tchunk->transmitted_list,
......@@ -1562,13 +1726,25 @@ static void sctp_check_transmitted(struct sctp_outq *q,
}
}
/* Reconstruct the transmitted list with chunks that are not yet
* acked by the Cumulative TSN Ack.
*/
while (NULL != (lchunk = sctp_list_dequeue(&tlist))) {
tchunk = list_entry(lchunk, struct sctp_chunk,
transmitted_list);
tsn = ntohl(tchunk->subh.data_hdr->tsn);
list_splice(&tlist, transmitted_queue);
}
/* Mark chunks as missing and consequently may get retransmitted. */
static void sctp_mark_missing(struct sctp_outq *q,
struct sctp_transport *transport,
__u32 highest_new_tsn_in_sack,
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;
list_for_each(pos, &transport->transmitted) {
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
* 'Unacknowledged TSN's', if the TSN number of an
......@@ -1576,26 +1752,35 @@ static void sctp_check_transmitted(struct sctp_outq *q,
* value, increment the 'TSN.Missing.Report' count on that
* chunk if it has NOT been fast retransmitted or marked for
* fast retransmit already.
*
*/
if (!chunk->fast_retransmit &&
!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 (!sctp_cacc_skip(primary, 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);
}
}
/*
* M4) If any DATA chunk is found to have a
* 'TSN.Missing.Report'
* value larger than or equal to 4, mark that chunk for
* retransmission and start the fast retransmit procedure.
*/
if ((!tchunk->fast_retransmit) &&
(!tchunk->tsn_gap_acked) &&
(TSN_lt(tsn, highest_new_tsn_in_sack))) {
tchunk->tsn_missing_report++;
SCTP_DEBUG_PRINTK("%s: TSN 0x%x missing counter: %d\n",
__FUNCTION__, tsn,
tchunk->tsn_missing_report);
}
if (tchunk->tsn_missing_report >= 4) {
tchunk->fast_retransmit = 1;
if (chunk->tsn_missing_report >= 4) {
chunk->fast_retransmit = 1;
do_fast_retransmit = 1;
}
list_add_tail(lchunk, transmitted_queue);
}
if (transport) {
......@@ -1611,7 +1796,7 @@ static void sctp_check_transmitted(struct sctp_outq *q,
}
/* Is the given TSN acked by this packet? */
static int sctp_acked(sctp_sackhdr_t *sack, __u32 tsn)
static int sctp_acked(struct sctp_sackhdr *sack, __u32 tsn)
{
int i;
sctp_sack_variable_t *frags;
......
......@@ -116,8 +116,6 @@ struct sctp_transport *sctp_transport_init(struct sctp_transport *peer,
peer->error_threshold = 0;
peer->error_count = 0;
peer->debug_name = "unnamedtransport";
INIT_LIST_HEAD(&peer->transmitted);
INIT_LIST_HEAD(&peer->send_ready);
INIT_LIST_HEAD(&peer->transports);
......@@ -137,6 +135,13 @@ struct sctp_transport *sctp_transport_init(struct sctp_transport *peer,
peer->dead = 0;
peer->malloced = 0;
/* Initialize the state information for SFR-CACC */
peer->cacc.changeover_active = 0;
peer->cacc.cycling_changeover = 0;
peer->cacc.next_tsn_at_change = 0;
peer->cacc.cacc_saw_newack = 0;
return peer;
}
......
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