* Re: Try to Implement load balancer for transmit & retransmit
2013-12-12 20:42 Try to Implement load balancer for transmit & retransmit Chang Xiangzhong
@ 2013-12-12 21:05 ` Vlad Yasevich
2013-12-12 21:39 ` Neil Horman
2013-12-12 21:48 ` Chang
2 siblings, 0 replies; 4+ messages in thread
From: Vlad Yasevich @ 2013-12-12 21:05 UTC (permalink / raw)
To: linux-sctp
On 12/12/2013 03:42 PM, Chang Xiangzhong wrote:
> The current implementation uses only one path as the "active path" and one
> path as a "retran path". This patch tries to use all of the pathes for
> transmission. But I'm afraid there must be something missing, such things like
> state update and etcs.
>
> The current implementation is very simple - just distribute the load on all of
> the pathes - just check if it satisifes cwnd>flight_size.
>
If you are interested in pursuing this work I recommend you look at
https://tools.ietf.org/id/draft-tuexen-tsvwg-sctp-multipath-06.txt
-vlad
> And comments would be appreciated!
> ---
> include/net/sctp/cmt.h | 5 +++-
> net/sctp/Makefile | 4 +--
> net/sctp/associola.c | 33 +++++++++++++++++++--
> net/sctp/cmt_debug.c | 2 +-
> net/sctp/outqueue.c | 73 +++++++++++++++++++++++++++++++++++-----------
> net/sctp/protocol.c | 1 +
> net/sctp/sm_sideeffect.c | 2 +-
> 7 files changed, 95 insertions(+), 25 deletions(-)
>
> diff --git a/include/net/sctp/cmt.h b/include/net/sctp/cmt.h
> index edbbfc2..858def8 100644
> --- a/include/net/sctp/cmt.h
> +++ b/include/net/sctp/cmt.h
> @@ -47,8 +47,11 @@
> extern char* cmt_print_assoc(struct sctp_association *asoc);
> extern char* cmt_print_sackhdr(struct sctp_sackhdr *sack);
> extern char* cmt_print_queued_tsn(struct list_head *queue,
> - struct sctp_transport *transport);
> + struct sctp_transport *t);
> extern char* cmt_print_cwnd(struct list_head *transport_list);
> +
> + extern struct sctp_transport* sctp_assoc_most_vacant_path(
> + struct sctp_association *asoc);
> #endif
>
> /* SACK Chunk Specific Flags*/
> diff --git a/net/sctp/associola.c b/net/sctp/associola.c
> index 626bdca..28f1f60 100644
> --- a/net/sctp/associola.c
> +++ b/net/sctp/associola.c
> @@ -1295,6 +1295,33 @@ void sctp_assoc_update(struct sctp_association *asoc,
> sctp_auth_asoc_init_active_key(asoc, GFP_ATOMIC);
> }
>
> +struct sctp_transport*
> +sctp_assoc_most_vacant_path(struct sctp_association *asoc, int threshold)
> +{
> + struct sctp_transport *t, *ret = NULL;
> + int vacancy, best=-1;
> + if (threshold < 0)
> + threshold = 0;
> + list_for_each_entry(t, &asoc->peer.transport_addr_list, transports) {
> + vacancy = t->cwnd - t->flight_size;
> + if (t->state != SCTP_ACTIVE || vacancy <= threshold /*t->pathmtu/4*/)
> + continue;
> + // This is in slow-start state
> + if (t->ssthresh > t->cwnd) {
> + ret = t;
> + break;
> + }
> + if (vacancy > best) {
> + best = vacancy;
> + ret = t;
> + }
> +
> +
> + }
> + cmt_debug("==>Find a most vacant path: %p\n", ret);
> + return ret;
> +}
> +
>
> diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c
> index d20816d..9010080 100644
> --- a/net/sctp/outqueue.c
> +++ b/net/sctp/outqueue.c
> @@ -57,13 +57,13 @@
>
> #include <net/sctp/sctp.h>
> #include <net/sctp/sm.h>
> -#include <net/sctp/cmt_debug.h>
> +#include <net/sctp/cmt.h>
>
>
> /* Declare internal functions here. */
> @@ -920,14 +920,32 @@ 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;
>
> - /* Switch transports & prepare the packet. */
> -
> - transport = asoc->peer.retran_path;
> +if (q->fast_rtx && !rtx_timeout)
> +{
> + struct sctp_chunk *chunk, *chunk1;
> + int chunk_size = 0; // not count the header!
> + list_for_each_entry_safe(
> + chunk, chunk1, &q->retransmit, transmitted_list) {
> + // not the one to fast_rtx
> + if (sctp_chunk_abandoned(chunk)
> + || chunk->tsn_gap_acked
> + || !chunk->fast_retransmit) {
> + continue;
> + }
> + // found the chunk to fast_rtx
> + chunk_size = ntohs(chunk->chunk_hdr->length);
> + break;
> + }
> + transport = sctp_assoc_most_vacant_path(asoc, chunk_size);
> + cmt_debug("%s: fast_rtx most vacant=%p, chunk_size=%d", __func__, transport, chunk_size);
> +} else {
> + transport = sctp_assoc_most_vacant_path(asoc, -1);
> +}
> + if (!transport) {
> + cmt_debug("could not find a vacant path!\n");
> + goto sctp_flush_out;
> + }
>
> if (list_empty(&transport->send_ready)) {
> list_add_tail(&transport->send_ready,
> @@ -989,12 +1007,22 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
> /* If there is a specified transport, use it.
> * Otherwise, we want to use the active path.
> */
> - new_transport = chunk->transport;
> + bool path_specified;
> +retry: new_transport = chunk->transport;
> if (!new_transport ||
> ((new_transport->state = SCTP_INACTIVE) ||
> (new_transport->state = SCTP_UNCONFIRMED) ||
> - (new_transport->state = SCTP_PF)))
> - new_transport = asoc->peer.active_path;
> + (new_transport->state = SCTP_PF))) {
> + path_specified = false;
> + // new_transport = asoc->peer.active_path;
> + new_transport = sctp_assoc_most_vacant_path(
> + asoc, -1);
> + if (!new_transport) {// no available path
> + sctp_outq_head_data(q, chunk);
> + goto sctp_flush_out;
> + }
> + } else
> + path_specified = true;
> if (new_transport->state = SCTP_UNCONFIRMED)
> continue;
>
> @@ -1032,7 +1060,18 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
>
> switch (status) {
> case SCTP_XMIT_CWND_FULL:// 3
> - case SCTP_XMIT_PMTU_FULL://1
> + cmt_debug("==>CWND full\n");
> + // The App didn't specify a path, so the path
> + // was chosen by us. Then we could retry with
> + // another path
> + if (!path_specified) {
> + cmt_debug("==>retry!\n");
> + goto retry;
> + }
> + // PMTU_FULL not likely. As sctp_packet_transmit_chunk
> + // would flush the transport and transmit the next
> + // chunk in the queue if PMTU_FULL happens.
> + case SCTP_XMIT_PMTU_FULL://1
> case SCTP_XMIT_RWND_FULL://2
> case SCTP_XMIT_NAGLE_DELAY://4
> /* We could not append this chunk, so put
>
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: Try to Implement load balancer for transmit & retransmit
2013-12-12 20:42 Try to Implement load balancer for transmit & retransmit Chang Xiangzhong
2013-12-12 21:05 ` Vlad Yasevich
@ 2013-12-12 21:39 ` Neil Horman
2013-12-12 21:48 ` Chang
2 siblings, 0 replies; 4+ messages in thread
From: Neil Horman @ 2013-12-12 21:39 UTC (permalink / raw)
To: linux-sctp
On Thu, Dec 12, 2013 at 09:42:32PM +0100, Chang Xiangzhong wrote:
> The current implementation uses only one path as the "active path" and one
> path as a "retran path". This patch tries to use all of the pathes for
> transmission. But I'm afraid there must be something missing, such things like
> state update and etcs.
>
> The current implementation is very simple - just distribute the load on all of
> the pathes - just check if it satisifes cwnd>flight_size.
>
> And comments would be appreciated!
> ---
> include/net/sctp/cmt.h | 5 +++-
> net/sctp/Makefile | 4 +--
> net/sctp/associola.c | 33 +++++++++++++++++++--
> net/sctp/cmt_debug.c | 2 +-
> net/sctp/outqueue.c | 73 +++++++++++++++++++++++++++++++++++-----------
> net/sctp/protocol.c | 1 +
> net/sctp/sm_sideeffect.c | 2 +-
> 7 files changed, 95 insertions(+), 25 deletions(-)
>
> diff --git a/include/net/sctp/cmt.h b/include/net/sctp/cmt.h
> index edbbfc2..858def8 100644
> --- a/include/net/sctp/cmt.h
> +++ b/include/net/sctp/cmt.h
> @@ -47,8 +47,11 @@
> extern char* cmt_print_assoc(struct sctp_association *asoc);
> extern char* cmt_print_sackhdr(struct sctp_sackhdr *sack);
> extern char* cmt_print_queued_tsn(struct list_head *queue,
> - struct sctp_transport *transport);
> + struct sctp_transport *t);
> extern char* cmt_print_cwnd(struct list_head *transport_list);
> +
> + extern struct sctp_transport* sctp_assoc_most_vacant_path(
> + struct sctp_association *asoc);
> #endif
>
> /* SACK Chunk Specific Flags*/
> diff --git a/net/sctp/associola.c b/net/sctp/associola.c
> index 626bdca..28f1f60 100644
> --- a/net/sctp/associola.c
> +++ b/net/sctp/associola.c
> @@ -1295,6 +1295,33 @@ void sctp_assoc_update(struct sctp_association *asoc,
> sctp_auth_asoc_init_active_key(asoc, GFP_ATOMIC);
> }
>
> +struct sctp_transport*
> +sctp_assoc_most_vacant_path(struct sctp_association *asoc, int threshold)
> +{
> + struct sctp_transport *t, *ret = NULL;
> + int vacancy, best=-1;
> + if (threshold < 0)
> + threshold = 0;
> + list_for_each_entry(t, &asoc->peer.transport_addr_list, transports) {
> + vacancy = t->cwnd - t->flight_size;
> + if (t->state != SCTP_ACTIVE || vacancy <= threshold /*t->pathmtu/4*/)
> + continue;
> + // This is in slow-start state
> + if (t->ssthresh > t->cwnd) {
> + ret = t;
> + break;
> + }
> + if (vacancy > best) {
> + best = vacancy;
> + ret = t;
> + }
> +
> +
> + }
> + cmt_debug("==>Find a most vacant path: %p\n", ret);
> + return ret;
> +}
> +
>
> diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c
> index d20816d..9010080 100644
> --- a/net/sctp/outqueue.c
> +++ b/net/sctp/outqueue.c
> @@ -57,13 +57,13 @@
>
> #include <net/sctp/sctp.h>
> #include <net/sctp/sm.h>
> -#include <net/sctp/cmt_debug.h>
> +#include <net/sctp/cmt.h>
>
>
> /* Declare internal functions here. */
> @@ -920,14 +920,32 @@ 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;
>
> - /* Switch transports & prepare the packet. */
> -
> - transport = asoc->peer.retran_path;
> +if (q->fast_rtx && !rtx_timeout)
> +{
> + struct sctp_chunk *chunk, *chunk1;
> + int chunk_size = 0; // not count the header!
> + list_for_each_entry_safe(
> + chunk, chunk1, &q->retransmit, transmitted_list) {
> + // not the one to fast_rtx
> + if (sctp_chunk_abandoned(chunk)
> + || chunk->tsn_gap_acked
> + || !chunk->fast_retransmit) {
> + continue;
> + }
> + // found the chunk to fast_rtx
> + chunk_size = ntohs(chunk->chunk_hdr->length);
> + break;
> + }
> + transport = sctp_assoc_most_vacant_path(asoc, chunk_size);
> + cmt_debug("%s: fast_rtx most vacant=%p, chunk_size=%d", __func__, transport, chunk_size);
> +} else {
> + transport = sctp_assoc_most_vacant_path(asoc, -1);
> +}
> + if (!transport) {
> + cmt_debug("could not find a vacant path!\n");
> + goto sctp_flush_out;
> + }
>
> if (list_empty(&transport->send_ready)) {
> list_add_tail(&transport->send_ready,
> @@ -989,12 +1007,22 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
> /* If there is a specified transport, use it.
> * Otherwise, we want to use the active path.
> */
> - new_transport = chunk->transport;
> + bool path_specified;
> +retry: new_transport = chunk->transport;
> if (!new_transport ||
> ((new_transport->state = SCTP_INACTIVE) ||
> (new_transport->state = SCTP_UNCONFIRMED) ||
> - (new_transport->state = SCTP_PF)))
> - new_transport = asoc->peer.active_path;
> + (new_transport->state = SCTP_PF))) {
> + path_specified = false;
> + // new_transport = asoc->peer.active_path;
> + new_transport = sctp_assoc_most_vacant_path(
> + asoc, -1);
> + if (!new_transport) {// no available path
> + sctp_outq_head_data(q, chunk);
> + goto sctp_flush_out;
> + }
> + } else
> + path_specified = true;
> if (new_transport->state = SCTP_UNCONFIRMED)
> continue;
>
> @@ -1032,7 +1060,18 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
>
> switch (status) {
> case SCTP_XMIT_CWND_FULL:// 3
> - case SCTP_XMIT_PMTU_FULL://1
> + cmt_debug("==>CWND full\n");
> + // The App didn't specify a path, so the path
> + // was chosen by us. Then we could retry with
> + // another path
> + if (!path_specified) {
> + cmt_debug("==>retry!\n");
> + goto retry;
> + }
> + // PMTU_FULL not likely. As sctp_packet_transmit_chunk
> + // would flush the transport and transmit the next
> + // chunk in the queue if PMTU_FULL happens.
> + case SCTP_XMIT_PMTU_FULL://1
> case SCTP_XMIT_RWND_FULL://2
> case SCTP_XMIT_NAGLE_DELAY://4
> /* We could not append this chunk, so put
> --
> To unsubscribe from this list: send the line "unsubscribe linux-sctp" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
Agree with vlad. This patch just doles out new aasociations accross less loaded
transports (using a very transient metric). What you want to do is
multipathing.
Neil
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: Try to Implement load balancer for transmit & retransmit
2013-12-12 20:42 Try to Implement load balancer for transmit & retransmit Chang Xiangzhong
2013-12-12 21:05 ` Vlad Yasevich
2013-12-12 21:39 ` Neil Horman
@ 2013-12-12 21:48 ` Chang
2 siblings, 0 replies; 4+ messages in thread
From: Chang @ 2013-12-12 21:48 UTC (permalink / raw)
To: linux-sctp
Yeah,
In my essay, I would implement SFR and CUC algorithms to manage the cwnd
increment/decrement, but in current implementation, the load is not
balanced to all of the pathes. So my 1st step is to evenly distribute
the load
Cheers
On 12/12/2013 10:39 PM, Neil Horman wrote:
> On Thu, Dec 12, 2013 at 09:42:32PM +0100, Chang Xiangzhong wrote:
>> The current implementation uses only one path as the "active path" and one
>> path as a "retran path". This patch tries to use all of the pathes for
>> transmission. But I'm afraid there must be something missing, such things like
>> state update and etcs.
>>
>> The current implementation is very simple - just distribute the load on all of
>> the pathes - just check if it satisifes cwnd>flight_size.
>>
>> And comments would be appreciated!
>> ---
>> include/net/sctp/cmt.h | 5 +++-
>> net/sctp/Makefile | 4 +--
>> net/sctp/associola.c | 33 +++++++++++++++++++--
>> net/sctp/cmt_debug.c | 2 +-
>> net/sctp/outqueue.c | 73 +++++++++++++++++++++++++++++++++++-----------
>> net/sctp/protocol.c | 1 +
>> net/sctp/sm_sideeffect.c | 2 +-
>> 7 files changed, 95 insertions(+), 25 deletions(-)
>>
>> diff --git a/include/net/sctp/cmt.h b/include/net/sctp/cmt.h
>> index edbbfc2..858def8 100644
>> --- a/include/net/sctp/cmt.h
>> +++ b/include/net/sctp/cmt.h
>> @@ -47,8 +47,11 @@
>> extern char* cmt_print_assoc(struct sctp_association *asoc);
>> extern char* cmt_print_sackhdr(struct sctp_sackhdr *sack);
>> extern char* cmt_print_queued_tsn(struct list_head *queue,
>> - struct sctp_transport *transport);
>> + struct sctp_transport *t);
>> extern char* cmt_print_cwnd(struct list_head *transport_list);
>> +
>> + extern struct sctp_transport* sctp_assoc_most_vacant_path(
>> + struct sctp_association *asoc);
>> #endif
>>
>> /* SACK Chunk Specific Flags*/
>> diff --git a/net/sctp/associola.c b/net/sctp/associola.c
>> index 626bdca..28f1f60 100644
>> --- a/net/sctp/associola.c
>> +++ b/net/sctp/associola.c
>> @@ -1295,6 +1295,33 @@ void sctp_assoc_update(struct sctp_association *asoc,
>> sctp_auth_asoc_init_active_key(asoc, GFP_ATOMIC);
>> }
>>
>> +struct sctp_transport*
>> +sctp_assoc_most_vacant_path(struct sctp_association *asoc, int threshold)
>> +{
>> + struct sctp_transport *t, *ret = NULL;
>> + int vacancy, best=-1;
>> + if (threshold < 0)
>> + threshold = 0;
>> + list_for_each_entry(t, &asoc->peer.transport_addr_list, transports) {
>> + vacancy = t->cwnd - t->flight_size;
>> + if (t->state != SCTP_ACTIVE || vacancy <= threshold /*t->pathmtu/4*/)
>> + continue;
>> + // This is in slow-start state
>> + if (t->ssthresh > t->cwnd) {
>> + ret = t;
>> + break;
>> + }
>> + if (vacancy > best) {
>> + best = vacancy;
>> + ret = t;
>> + }
>> +
>> +
>> + }
>> + cmt_debug("==>Find a most vacant path: %p\n", ret);
>> + return ret;
>> +}
>> +
>>
>> diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c
>> index d20816d..9010080 100644
>> --- a/net/sctp/outqueue.c
>> +++ b/net/sctp/outqueue.c
>> @@ -57,13 +57,13 @@
>>
>> #include <net/sctp/sctp.h>
>> #include <net/sctp/sm.h>
>> -#include <net/sctp/cmt_debug.h>
>> +#include <net/sctp/cmt.h>
>>
>>
>> /* Declare internal functions here. */
>> @@ -920,14 +920,32 @@ 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;
>>
>> - /* Switch transports & prepare the packet. */
>> -
>> - transport = asoc->peer.retran_path;
>> +if (q->fast_rtx && !rtx_timeout)
>> +{
>> + struct sctp_chunk *chunk, *chunk1;
>> + int chunk_size = 0; // not count the header!
>> + list_for_each_entry_safe(
>> + chunk, chunk1, &q->retransmit, transmitted_list) {
>> + // not the one to fast_rtx
>> + if (sctp_chunk_abandoned(chunk)
>> + || chunk->tsn_gap_acked
>> + || !chunk->fast_retransmit) {
>> + continue;
>> + }
>> + // found the chunk to fast_rtx
>> + chunk_size = ntohs(chunk->chunk_hdr->length);
>> + break;
>> + }
>> + transport = sctp_assoc_most_vacant_path(asoc, chunk_size);
>> + cmt_debug("%s: fast_rtx most vacant=%p, chunk_size=%d", __func__, transport, chunk_size);
>> +} else {
>> + transport = sctp_assoc_most_vacant_path(asoc, -1);
>> +}
>> + if (!transport) {
>> + cmt_debug("could not find a vacant path!\n");
>> + goto sctp_flush_out;
>> + }
>>
>> if (list_empty(&transport->send_ready)) {
>> list_add_tail(&transport->send_ready,
>> @@ -989,12 +1007,22 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
>> /* If there is a specified transport, use it.
>> * Otherwise, we want to use the active path.
>> */
>> - new_transport = chunk->transport;
>> + bool path_specified;
>> +retry: new_transport = chunk->transport;
>> if (!new_transport ||
>> ((new_transport->state = SCTP_INACTIVE) ||
>> (new_transport->state = SCTP_UNCONFIRMED) ||
>> - (new_transport->state = SCTP_PF)))
>> - new_transport = asoc->peer.active_path;
>> + (new_transport->state = SCTP_PF))) {
>> + path_specified = false;
>> + // new_transport = asoc->peer.active_path;
>> + new_transport = sctp_assoc_most_vacant_path(
>> + asoc, -1);
>> + if (!new_transport) {// no available path
>> + sctp_outq_head_data(q, chunk);
>> + goto sctp_flush_out;
>> + }
>> + } else
>> + path_specified = true;
>> if (new_transport->state = SCTP_UNCONFIRMED)
>> continue;
>>
>> @@ -1032,7 +1060,18 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
>>
>> switch (status) {
>> case SCTP_XMIT_CWND_FULL:// 3
>> - case SCTP_XMIT_PMTU_FULL://1
>> + cmt_debug("==>CWND full\n");
>> + // The App didn't specify a path, so the path
>> + // was chosen by us. Then we could retry with
>> + // another path
>> + if (!path_specified) {
>> + cmt_debug("==>retry!\n");
>> + goto retry;
>> + }
>> + // PMTU_FULL not likely. As sctp_packet_transmit_chunk
>> + // would flush the transport and transmit the next
>> + // chunk in the queue if PMTU_FULL happens.
>> + case SCTP_XMIT_PMTU_FULL://1
>> case SCTP_XMIT_RWND_FULL://2
>> case SCTP_XMIT_NAGLE_DELAY://4
>> /* We could not append this chunk, so put
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-sctp" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>>
> Agree with vlad. This patch just doles out new aasociations accross less loaded
> transports (using a very transient metric). What you want to do is
> multipathing.
>
> Neil
>
^ permalink raw reply [flat|nested] 4+ messages in thread