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 > -- To unsubscribe from this list: send the line "unsubscribe linux-sctp" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html