From f7a77f6fd38b22bcf761204ee9993f4d05a5db99 Mon Sep 17 00:00:00 2001 From: Michael Tuexen Date: Sun, 23 Jan 2011 19:36:28 +0000 Subject: [PATCH] Add stream scheduling support. This work is based on a patch received from Robin Seggelmann. MFC after: 3 months. --- sys/conf/files | 1 + sys/netinet/sctp.h | 19 + sys/netinet/sctp_input.c | 23 +- sys/netinet/sctp_output.c | 145 +---- sys/netinet/sctp_output.h | 5 - sys/netinet/sctp_pcb.c | 1 + sys/netinet/sctp_pcb.h | 1 + sys/netinet/sctp_ss_functions.c | 914 ++++++++++++++++++++++++++++++++ sys/netinet/sctp_structs.h | 80 ++- sys/netinet/sctp_sysctl.c | 7 + sys/netinet/sctp_sysctl.h | 8 + sys/netinet/sctp_timer.c | 19 +- sys/netinet/sctp_uio.h | 6 + sys/netinet/sctp_usrreq.c | 138 ++++- sys/netinet/sctputil.c | 10 +- 15 files changed, 1199 insertions(+), 178 deletions(-) create mode 100644 sys/netinet/sctp_ss_functions.c diff --git a/sys/conf/files b/sys/conf/files index 8d5cd2476287..b75d3d6721eb 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -2638,6 +2638,7 @@ netinet/sctp_input.c optional inet sctp netinet/sctp_output.c optional inet sctp netinet/sctp_pcb.c optional inet sctp netinet/sctp_peeloff.c optional inet sctp +netinet/sctp_ss_functions.c optional inet sctp netinet/sctp_sysctl.c optional inet sctp netinet/sctp_timer.c optional inet sctp netinet/sctp_usrreq.c optional inet sctp diff --git a/sys/netinet/sctp.h b/sys/netinet/sctp.h index 14acd9dac612..8e9c0c124b47 100644 --- a/sys/netinet/sctp.h +++ b/sys/netinet/sctp.h @@ -158,6 +158,9 @@ struct sctp_paramhdr { #define SCTP_CMT_USE_DAC 0x00001201 /* JRS - Pluggable Congestion Control Socket option */ #define SCTP_PLUGGABLE_CC 0x00001202 +/* RS - Pluggable Stream Scheduling Socket option */ +#define SCTP_PLUGGABLE_SS 0x00001203 +#define SCTP_SS_VALUE 0x00001204 /* read only */ #define SCTP_GET_SNDBUF_USE 0x00001101 @@ -253,6 +256,22 @@ struct sctp_paramhdr { /* HTCP Congestion Control */ #define SCTP_CC_HTCP 0x00000002 +/* RS - Supported stream scheduling modules for pluggable + * stream scheduling + */ +/* Default simple round-robin */ +#define SCTP_SS_DEFAULT 0x00000000 +/* Real round-robin */ +#define SCTP_SS_ROUND_ROBIN 0x00000001 +/* Real round-robin per packet */ +#define SCTP_SS_ROUND_ROBIN_PACKET 0x00000002 +/* Priority */ +#define SCTP_SS_PRIORITY 0x00000003 +/* Fair Bandwidth */ +#define SCTP_SS_FAIR_BANDWITH 0x00000004 +/* First-come, first-serve */ +#define SCTP_SS_FIRST_COME 0x00000005 + /* fragment interleave constants * setting must be one of these or diff --git a/sys/netinet/sctp_input.c b/sys/netinet/sctp_input.c index acbad4f563c9..bb8722f8f299 100644 --- a/sys/netinet/sctp_input.c +++ b/sys/netinet/sctp_input.c @@ -193,8 +193,8 @@ int sctp_is_there_unsent_data(struct sctp_tcb *stcb) { int unsent_data = 0; - struct sctp_stream_queue_pending *sp, *nsp; - struct sctp_stream_out *strq; + unsigned int i; + struct sctp_stream_queue_pending *sp; struct sctp_association *asoc; /* @@ -205,9 +205,14 @@ sctp_is_there_unsent_data(struct sctp_tcb *stcb) */ asoc = &stcb->asoc; SCTP_TCB_SEND_LOCK(stcb); - TAILQ_FOREACH(strq, &asoc->out_wheel, next_spoke) { - /* sa_ignore FREED_MEMORY */ - TAILQ_FOREACH_SAFE(sp, &strq->outqueue, next, nsp) { + if (!stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc)) { + /* Check to see if some data queued */ + for (i = 0; i < stcb->asoc.streamoutcnt; i++) { + /* sa_ignore FREED_MEMORY */ + sp = TAILQ_FIRST(&stcb->asoc.strmout[i].outqueue); + if (sp == NULL) { + continue; + } if ((sp->msg_is_complete) && (sp->length == 0) && (sp->sender_all_done)) { @@ -224,8 +229,8 @@ sctp_is_there_unsent_data(struct sctp_tcb *stcb) sp->msg_is_complete, sp->put_last_out); } - atomic_subtract_int(&asoc->stream_queue_cnt, 1); - TAILQ_REMOVE(&strq->outqueue, sp, next); + atomic_subtract_int(&stcb->asoc.stream_queue_cnt, 1); + TAILQ_REMOVE(&stcb->asoc.strmout[i].outqueue, sp, next); if (sp->net) { sctp_free_remote_addr(sp->net); sp->net = NULL; @@ -957,7 +962,7 @@ sctp_handle_shutdown_ack(struct sctp_shutdown_ack_chunk *cp, /* are the queues empty? */ if (!TAILQ_EMPTY(&asoc->send_queue) || !TAILQ_EMPTY(&asoc->sent_queue) || - !TAILQ_EMPTY(&asoc->out_wheel)) { + !stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc)) { sctp_report_all_outbound(stcb, 0, SCTP_SO_NOT_LOCKED); } /* stop the timer */ @@ -3031,7 +3036,7 @@ sctp_handle_shutdown_complete(struct sctp_shutdown_complete_chunk *cp, /* are the queues empty? they should be */ if (!TAILQ_EMPTY(&asoc->send_queue) || !TAILQ_EMPTY(&asoc->sent_queue) || - !TAILQ_EMPTY(&asoc->out_wheel)) { + !stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc)) { sctp_report_all_outbound(stcb, 0, SCTP_SO_NOT_LOCKED); } } diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c index ad908f098abf..027204b5c18b 100644 --- a/sys/netinet/sctp_output.c +++ b/sys/netinet/sctp_output.c @@ -5616,52 +5616,6 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb, } -void -sctp_insert_on_wheel(struct sctp_tcb *stcb, - struct sctp_association *asoc, - struct sctp_stream_out *strq, int holds_lock) -{ - if (holds_lock == 0) { - SCTP_TCB_SEND_LOCK(stcb); - } - if ((strq->next_spoke.tqe_next == NULL) && - (strq->next_spoke.tqe_prev == NULL)) { - TAILQ_INSERT_TAIL(&asoc->out_wheel, strq, next_spoke); - } - if (holds_lock == 0) { - SCTP_TCB_SEND_UNLOCK(stcb); - } -} - -void -sctp_remove_from_wheel(struct sctp_tcb *stcb, - struct sctp_association *asoc, - struct sctp_stream_out *strq, - int holds_lock) -{ - /* take off and then setup so we know it is not on the wheel */ - if (holds_lock == 0) { - SCTP_TCB_SEND_LOCK(stcb); - } - if (TAILQ_EMPTY(&strq->outqueue)) { - if (asoc->last_out_stream == strq) { - asoc->last_out_stream = TAILQ_PREV(asoc->last_out_stream, sctpwheel_listhead, next_spoke); - if (asoc->last_out_stream == NULL) { - asoc->last_out_stream = TAILQ_LAST(&asoc->out_wheel, sctpwheel_listhead); - } - if (asoc->last_out_stream == strq) { - asoc->last_out_stream = NULL; - } - } - TAILQ_REMOVE(&asoc->out_wheel, strq, next_spoke); - strq->next_spoke.tqe_next = NULL; - strq->next_spoke.tqe_prev = NULL; - } - if (holds_lock == 0) { - SCTP_TCB_SEND_UNLOCK(stcb); - } -} - static void sctp_prune_prsctp(struct sctp_tcb *stcb, struct sctp_association *asoc, @@ -5924,11 +5878,7 @@ sctp_msg_append(struct sctp_tcb *stcb, sp->strseq = strm->next_sequence_sent; strm->next_sequence_sent++; } - if ((strm->next_spoke.tqe_next == NULL) && - (strm->next_spoke.tqe_prev == NULL)) { - /* Not on wheel, insert */ - sctp_insert_on_wheel(stcb, &stcb->asoc, strm, 1); - } + stcb->asoc.ss_functions.sctp_ss_add_to_stream(stcb, &stcb->asoc, strm, sp, 1); m = NULL; SCTP_TCB_SEND_UNLOCK(stcb); out_now: @@ -6743,6 +6693,7 @@ sctp_move_to_outqueue(struct sctp_tcb *stcb, } atomic_subtract_int(&asoc->stream_queue_cnt, 1); TAILQ_REMOVE(&strq->outqueue, sp, next); + stcb->asoc.ss_functions.sctp_ss_remove_from_stream(stcb, asoc, strq, sp, send_lock_up); if (sp->net) { sctp_free_remote_addr(sp->net); sp->net = NULL; @@ -7152,6 +7103,7 @@ sctp_move_to_outqueue(struct sctp_tcb *stcb, send_lock_up = 1; } TAILQ_REMOVE(&strq->outqueue, sp, next); + stcb->asoc.ss_functions.sctp_ss_remove_from_stream(stcb, asoc, strq, sp, send_lock_up); if (sp->net) { sctp_free_remote_addr(sp->net); sp->net = NULL; @@ -7181,24 +7133,6 @@ sctp_move_to_outqueue(struct sctp_tcb *stcb, } -static struct sctp_stream_out * -sctp_select_a_stream(struct sctp_tcb *stcb, struct sctp_association *asoc) -{ - struct sctp_stream_out *strq; - - /* Find the next stream to use */ - if (asoc->last_out_stream == NULL) { - strq = TAILQ_FIRST(&asoc->out_wheel); - } else { - strq = TAILQ_NEXT(asoc->last_out_stream, next_spoke); - if (strq == NULL) { - strq = TAILQ_FIRST(&asoc->out_wheel); - } - } - return (strq); -} - - static void sctp_fill_outqueue(struct sctp_tcb *stcb, struct sctp_nets *net, int frag_point, int eeor_mode, int *quit_now) @@ -7207,7 +7141,6 @@ sctp_fill_outqueue(struct sctp_tcb *stcb, struct sctp_stream_out *strq, *strqn; int goal_mtu, moved_how_much, total_moved = 0, bail = 0; int locked, giveup; - struct sctp_stream_queue_pending *sp; SCTP_TCB_LOCK_ASSERT(stcb); asoc = &stcb->asoc; @@ -7231,46 +7164,17 @@ sctp_fill_outqueue(struct sctp_tcb *stcb, strq = asoc->locked_on_sending; locked = 1; } else { - strq = sctp_select_a_stream(stcb, asoc); + strq = stcb->asoc.ss_functions.sctp_ss_select_stream(stcb, net, asoc); locked = 0; } strqn = strq; while ((goal_mtu > 0) && strq) { - sp = TAILQ_FIRST(&strq->outqueue); - if (sp == NULL) { - break; - } - /** - * Honor the users' choice if given. If not given, - * pull it only to the primary path in case of not using - * CMT. - */ - if (((sp->net != NULL) && - (sp->net != net)) || - ((sp->net == NULL) && - (asoc->sctp_cmt_on_off == 0) && - (asoc->primary_destination != net))) { - /* Do not pull to this network */ - if (locked) { - break; - } else { - strq = sctp_select_a_stream(stcb, asoc); - if (strq == NULL) - /* none left */ - break; - if (strqn == strq) { - /* I have circled */ - break; - } - continue; - } - } giveup = 0; bail = 0; moved_how_much = sctp_move_to_outqueue(stcb, strq, goal_mtu, frag_point, &locked, &giveup, eeor_mode, &bail); if (moved_how_much) - asoc->last_out_stream = strq; + stcb->asoc.ss_functions.sctp_ss_scheduled(stcb, net, asoc, strq, moved_how_much); if (locked) { asoc->locked_on_sending = strq; @@ -7279,23 +7183,10 @@ sctp_fill_outqueue(struct sctp_tcb *stcb, break; } else { asoc->locked_on_sending = NULL; - if (TAILQ_EMPTY(&strq->outqueue)) { - if (strq == strqn) { - /* Must move start to next one */ - strqn = TAILQ_NEXT(strq, next_spoke); - if (strqn == NULL) { - strqn = TAILQ_FIRST(&asoc->out_wheel); - if (strqn == NULL) { - break; - } - } - } - sctp_remove_from_wheel(stcb, asoc, strq, 0); - } if ((giveup) || bail) { break; } - strq = sctp_select_a_stream(stcb, asoc); + strq = stcb->asoc.ss_functions.sctp_ss_select_stream(stcb, net, asoc); if (strq == NULL) { break; } @@ -7307,6 +7198,8 @@ sctp_fill_outqueue(struct sctp_tcb *stcb, if (bail) *quit_now = 1; + stcb->asoc.ss_functions.sctp_ss_packet_done(stcb, net, asoc); + if (total_moved == 0) { if ((stcb->asoc.sctp_cmt_on_off == 0) && (net == stcb->asoc.primary_destination)) { @@ -7335,16 +7228,16 @@ void sctp_move_chunks_from_net(struct sctp_tcb *stcb, struct sctp_nets *net) { struct sctp_association *asoc; - struct sctp_stream_out *outs; struct sctp_tmit_chunk *chk; struct sctp_stream_queue_pending *sp; + unsigned int i; if (net == NULL) { return; } asoc = &stcb->asoc; - TAILQ_FOREACH(outs, &asoc->out_wheel, next_spoke) { - TAILQ_FOREACH(sp, &outs->outqueue, next) { + for (i = 0; i < stcb->asoc.streamoutcnt; i++) { + TAILQ_FOREACH(sp, &stcb->asoc.strmout[i].outqueue, next) { if (sp->net == net) { sctp_free_remote_addr(sp->net); sp->net = NULL; @@ -7437,7 +7330,7 @@ sctp_med_chunk_output(struct sctp_inpcb *inp, if (TAILQ_EMPTY(&asoc->control_send_queue) && TAILQ_EMPTY(&asoc->asconf_send_queue) && TAILQ_EMPTY(&asoc->send_queue) && - TAILQ_EMPTY(&asoc->out_wheel)) { + stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc)) { *reason_code = 9; return (0); } @@ -7454,7 +7347,8 @@ sctp_med_chunk_output(struct sctp_inpcb *inp, max_send_per_dest = SCTP_SB_LIMIT_SND(stcb->sctp_socket) / asoc->numnets; else max_send_per_dest = 0; - if ((no_data_chunks == 0) && (!TAILQ_EMPTY(&asoc->out_wheel))) { + if ((no_data_chunks == 0) && + (!stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc))) { TAILQ_FOREACH(net, &asoc->nets, sctp_next) { /* * This for loop we are in takes in each net, if @@ -9638,7 +9532,7 @@ sctp_chunk_output(struct sctp_inpcb *inp, } if (TAILQ_EMPTY(&asoc->control_send_queue) && TAILQ_EMPTY(&asoc->send_queue) && - TAILQ_EMPTY(&asoc->out_wheel)) { + stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc)) { /* Nothing left to send */ break; } @@ -12447,8 +12341,7 @@ sctp_lower_sosend(struct socket *so, TAILQ_INIT(&asoc->strmout[i].outqueue); asoc->strmout[i].stream_no = i; asoc->strmout[i].last_msg_incomplete = 0; - asoc->strmout[i].next_spoke.tqe_next = 0; - asoc->strmout[i].next_spoke.tqe_prev = 0; + asoc->ss_functions.sctp_ss_init_stream(&asoc->strmout[i]); } } } @@ -12841,11 +12734,7 @@ sctp_lower_sosend(struct socket *so, SCTP_STAT_INCR(sctps_sends_with_unord); } TAILQ_INSERT_TAIL(&strm->outqueue, sp, next); - if ((strm->next_spoke.tqe_next == NULL) && - (strm->next_spoke.tqe_prev == NULL)) { - /* Not on wheel, insert */ - sctp_insert_on_wheel(stcb, asoc, strm, 1); - } + stcb->asoc.ss_functions.sctp_ss_add_to_stream(stcb, asoc, strm, sp, 1); SCTP_TCB_SEND_UNLOCK(stcb); } else { SCTP_TCB_SEND_LOCK(stcb); diff --git a/sys/netinet/sctp_output.h b/sys/netinet/sctp_output.h index d655c3aa218d..70fa719fd2b8 100644 --- a/sys/netinet/sctp_output.h +++ b/sys/netinet/sctp_output.h @@ -135,11 +135,6 @@ int sctp_output(struct sctp_inpcb *, struct mbuf *, struct sockaddr *, struct mbuf *, struct thread *, int); -void -sctp_insert_on_wheel(struct sctp_tcb *stcb, - struct sctp_association *asoc, - struct sctp_stream_out *strq, int holdslock); - void sctp_chunk_output(struct sctp_inpcb *, struct sctp_tcb *, int, int #if !defined(__APPLE__) && !defined(SCTP_SO_LOCK_TESTING) diff --git a/sys/netinet/sctp_pcb.c b/sys/netinet/sctp_pcb.c index bbbf34403ae6..f48ed6a89e52 100644 --- a/sys/netinet/sctp_pcb.c +++ b/sys/netinet/sctp_pcb.c @@ -2517,6 +2517,7 @@ sctp_inpcb_alloc(struct socket *so, uint32_t vrf_id) m->sctp_sws_receiver = SCTP_SWS_RECEIVER_DEF; m->max_burst = SCTP_BASE_SYSCTL(sctp_max_burst_default); m->sctp_default_cc_module = SCTP_BASE_SYSCTL(sctp_default_cc_module); + m->sctp_default_ss_module = SCTP_BASE_SYSCTL(sctp_default_ss_module); /* number of streams to pre-open on a association */ m->pre_open_stream_count = SCTP_BASE_SYSCTL(sctp_nr_outgoing_streams_default); diff --git a/sys/netinet/sctp_pcb.h b/sys/netinet/sctp_pcb.h index 23ebc4f1756c..29f724c7b471 100644 --- a/sys/netinet/sctp_pcb.h +++ b/sys/netinet/sctp_pcb.h @@ -276,6 +276,7 @@ struct sctp_pcb { uint32_t sctp_sws_receiver; uint32_t sctp_default_cc_module; + uint32_t sctp_default_ss_module; /* authentication related fields */ struct sctp_keyhead shared_keys; sctp_auth_chklist_t *local_auth_chunks; diff --git a/sys/netinet/sctp_ss_functions.c b/sys/netinet/sctp_ss_functions.c new file mode 100644 index 000000000000..a2056a8c38c2 --- /dev/null +++ b/sys/netinet/sctp_ss_functions.c @@ -0,0 +1,914 @@ +/*- + * Copyright (c) 2010, by Randall Stewart & Michael Tuexen, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * a) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * b) Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * + * c) Neither the name of Cisco Systems, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include + +/* + * Default simple round-robin algorithm. + * Just interates the streams in the order they appear. + */ + +static void +sctp_ss_default_add(struct sctp_tcb *, struct sctp_association *, + struct sctp_stream_out *, + struct sctp_stream_queue_pending *, int); + +static void +sctp_ss_default_remove(struct sctp_tcb *, struct sctp_association *, + struct sctp_stream_out *, + struct sctp_stream_queue_pending *, int); + +static void +sctp_ss_default_init(struct sctp_tcb *stcb, struct sctp_association *asoc, + int holds_lock) +{ + uint16_t i; + + TAILQ_INIT(&asoc->ss_data.out_wheel); + for (i = 0; i < stcb->asoc.streamoutcnt; i++) { + if (!TAILQ_EMPTY(&stcb->asoc.strmout[i].outqueue)) { + sctp_ss_default_add(stcb, &stcb->asoc, + &stcb->asoc.strmout[i], + NULL, holds_lock); + } + } + return; +} + +static void +sctp_ss_default_clear(struct sctp_tcb *stcb, struct sctp_association *asoc, + int clear_values, int holds_lock) +{ + uint16_t i; + + for (i = 0; i < stcb->asoc.streamoutcnt; i++) { + if (!TAILQ_EMPTY(&stcb->asoc.strmout[i].outqueue)) { + sctp_ss_default_remove(stcb, &stcb->asoc, + &stcb->asoc.strmout[i], + NULL, holds_lock); + } + } + return; +} + +static void +sctp_ss_default_init_stream(struct sctp_stream_out *strq) +{ + strq->ss_params.rr.next_spoke.tqe_next = NULL; + strq->ss_params.rr.next_spoke.tqe_prev = NULL; + return; +} + +static void +sctp_ss_default_add(struct sctp_tcb *stcb, struct sctp_association *asoc, + struct sctp_stream_out *strq, + struct sctp_stream_queue_pending *sp, int holds_lock) +{ + if (holds_lock == 0) { + SCTP_TCB_SEND_LOCK(stcb); + } + if ((strq->ss_params.rr.next_spoke.tqe_next == NULL) && + (strq->ss_params.rr.next_spoke.tqe_prev == NULL)) { + TAILQ_INSERT_TAIL(&asoc->ss_data.out_wheel, + strq, ss_params.rr.next_spoke); + } + if (holds_lock == 0) { + SCTP_TCB_SEND_UNLOCK(stcb); + } + return; +} + +static int +sctp_ss_default_is_empty(struct sctp_tcb *stcb, struct sctp_association *asoc) +{ + if (TAILQ_EMPTY(&asoc->ss_data.out_wheel)) { + return (1); + } else { + return (0); + } +} + +static void +sctp_ss_default_remove(struct sctp_tcb *stcb, struct sctp_association *asoc, + struct sctp_stream_out *strq, + struct sctp_stream_queue_pending *sp, int holds_lock) +{ + /* take off and then setup so we know it is not on the wheel */ + if (holds_lock == 0) { + SCTP_TCB_SEND_LOCK(stcb); + } + if (TAILQ_EMPTY(&strq->outqueue)) { + if (asoc->last_out_stream == strq) { + asoc->last_out_stream = TAILQ_PREV(asoc->last_out_stream, + sctpwheel_listhead, + ss_params.rr.next_spoke); + if (asoc->last_out_stream == NULL) { + asoc->last_out_stream = TAILQ_LAST(&asoc->ss_data.out_wheel, + sctpwheel_listhead); + } + if (asoc->last_out_stream == strq) { + asoc->last_out_stream = NULL; + } + } + TAILQ_REMOVE(&asoc->ss_data.out_wheel, strq, ss_params.rr.next_spoke); + strq->ss_params.rr.next_spoke.tqe_next = NULL; + strq->ss_params.rr.next_spoke.tqe_prev = NULL; + } + if (holds_lock == 0) { + SCTP_TCB_SEND_UNLOCK(stcb); + } + return; +} + + +static struct sctp_stream_out * +sctp_ss_default_select(struct sctp_tcb *stcb, struct sctp_nets *net, + struct sctp_association *asoc) +{ + struct sctp_stream_out *strq, *strqt; + + strqt = asoc->last_out_stream; +default_again: + /* Find the next stream to use */ + if (strqt == NULL) { + strq = TAILQ_FIRST(&asoc->ss_data.out_wheel); + } else { + strq = TAILQ_NEXT(strqt, ss_params.rr.next_spoke); + if (strq == NULL) { + strq = TAILQ_FIRST(&asoc->ss_data.out_wheel); + } + } + + /* + * If CMT is off, we must validate that the stream in question has + * the first item pointed towards are network destionation requested + * by the caller. Note that if we turn out to be locked to a stream + * (assigning TSN's then we must stop, since we cannot look for + * another stream with data to send to that destination). In CMT's + * case, by skipping this check, we will send one data packet + * towards the requested net. + */ + if (net != NULL && strq != NULL && + SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) { + if (TAILQ_FIRST(&strq->outqueue) && + TAILQ_FIRST(&strq->outqueue)->net != NULL && + TAILQ_FIRST(&strq->outqueue)->net != net) { + if (strq == asoc->last_out_stream) { + return (NULL); + } else { + strqt = strq; + goto default_again; + } + } + } + return (strq); +} + +static void +sctp_ss_default_scheduled(struct sctp_tcb *stcb, struct sctp_nets *net, + struct sctp_association *asoc, + struct sctp_stream_out *strq, int moved_how_much) +{ + asoc->last_out_stream = strq; + return; +} + +static void +sctp_ss_default_packet_done(struct sctp_tcb *stcb, struct sctp_nets *net, + struct sctp_association *asoc) +{ + /* Nothing to be done here */ + return; +} + +static int +sctp_ss_default_get_value(struct sctp_tcb *stcb, struct sctp_association *asoc, + struct sctp_stream_out *strq, uint16_t * value) +{ + /* Nothing to be done here */ + return (-1); +} + +static int +sctp_ss_default_set_value(struct sctp_tcb *stcb, struct sctp_association *asoc, + struct sctp_stream_out *strq, uint16_t value) +{ + /* Nothing to be done here */ + return (-1); +} + +/* + * Real round-robin algorithm. + * Always interates the streams in ascending order. + */ +static void +sctp_ss_rr_add(struct sctp_tcb *stcb, struct sctp_association *asoc, + struct sctp_stream_out *strq, + struct sctp_stream_queue_pending *sp, int holds_lock) +{ + struct sctp_stream_out *strqt; + + if (holds_lock == 0) { + SCTP_TCB_SEND_LOCK(stcb); + } + if ((strq->ss_params.rr.next_spoke.tqe_next == NULL) && + (strq->ss_params.rr.next_spoke.tqe_prev == NULL)) { + if (TAILQ_EMPTY(&asoc->ss_data.out_wheel)) { + TAILQ_INSERT_HEAD(&asoc->ss_data.out_wheel, strq, ss_params.rr.next_spoke); + } else { + strqt = TAILQ_FIRST(&asoc->ss_data.out_wheel); + while (strqt != NULL && (strqt->stream_no < strq->stream_no)) { + strqt = TAILQ_NEXT(strqt, ss_params.rr.next_spoke); + } + if (strqt != NULL) { + TAILQ_INSERT_BEFORE(strqt, strq, ss_params.rr.next_spoke); + } else { + TAILQ_INSERT_TAIL(&asoc->ss_data.out_wheel, strq, ss_params.rr.next_spoke); + } + } + } + if (holds_lock == 0) { + SCTP_TCB_SEND_UNLOCK(stcb); + } + return; +} + +/* + * Real round-robin per packet algorithm. + * Always interates the streams in ascending order and + * only fills messages of the same stream in a packet. + */ +static void +sctp_ss_rrp_add(struct sctp_tcb *stcb, struct sctp_association *asoc, + struct sctp_stream_out *strq, + struct sctp_stream_queue_pending *sp, int holds_lock) +{ + struct sctp_stream_out *strqt; + + if (holds_lock == 0) { + SCTP_TCB_SEND_LOCK(stcb); + } + if ((strq->ss_params.rr.next_spoke.tqe_next == NULL) && + (strq->ss_params.rr.next_spoke.tqe_prev == NULL)) { + + if (TAILQ_EMPTY(&asoc->ss_data.out_wheel)) { + TAILQ_INSERT_HEAD(&asoc->ss_data.out_wheel, strq, ss_params.rr.next_spoke); + } else { + strqt = TAILQ_FIRST(&asoc->ss_data.out_wheel); + while (strqt != NULL && strqt->stream_no < strq->stream_no) { + strqt = TAILQ_NEXT(strqt, ss_params.rr.next_spoke); + } + if (strqt != NULL) { + TAILQ_INSERT_BEFORE(strqt, strq, ss_params.rr.next_spoke); + } else { + TAILQ_INSERT_TAIL(&asoc->ss_data.out_wheel, strq, ss_params.rr.next_spoke); + } + } + } + if (holds_lock == 0) { + SCTP_TCB_SEND_UNLOCK(stcb); + } + return; +} + +static struct sctp_stream_out * +sctp_ss_rrp_select(struct sctp_tcb *stcb, struct sctp_nets *net, + struct sctp_association *asoc) +{ + struct sctp_stream_out *strq, *strqt; + + strqt = asoc->last_out_stream; + if (strqt != NULL && !TAILQ_EMPTY(&strqt->outqueue)) { + return (strqt); + } +rrp_again: + /* Find the next stream to use */ + if (strqt == NULL) { + strq = TAILQ_FIRST(&asoc->ss_data.out_wheel); + } else { + strq = TAILQ_NEXT(strqt, ss_params.rr.next_spoke); + if (strq == NULL) { + strq = TAILQ_FIRST(&asoc->ss_data.out_wheel); + } + } + + /* + * If CMT is off, we must validate that the stream in question has + * the first item pointed towards are network destionation requested + * by the caller. Note that if we turn out to be locked to a stream + * (assigning TSN's then we must stop, since we cannot look for + * another stream with data to send to that destination). In CMT's + * case, by skipping this check, we will send one data packet + * towards the requested net. + */ + if (net != NULL && strq != NULL && + SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) { + if (TAILQ_FIRST(&strq->outqueue) && + TAILQ_FIRST(&strq->outqueue)->net != NULL && + TAILQ_FIRST(&strq->outqueue)->net != net) { + if (strq == asoc->last_out_stream) { + return (NULL); + } else { + strqt = strq; + goto rrp_again; + } + } + } + return (strq); +} + +static void +sctp_ss_rrp_packet_done(struct sctp_tcb *stcb, struct sctp_nets *net, + struct sctp_association *asoc) +{ + struct sctp_stream_out *strq, *strqt; + + strqt = asoc->last_out_stream; +rrp_pd_again: + /* Find the next stream to use */ + if (strqt == NULL) { + strq = TAILQ_FIRST(&asoc->ss_data.out_wheel); + } else { + strq = TAILQ_NEXT(strqt, ss_params.rr.next_spoke); + if (strq == NULL) { + strq = TAILQ_FIRST(&asoc->ss_data.out_wheel); + } + } + + /* + * If CMT is off, we must validate that the stream in question has + * the first item pointed towards are network destionation requested + * by the caller. Note that if we turn out to be locked to a stream + * (assigning TSN's then we must stop, since we cannot look for + * another stream with data to send to that destination). In CMT's + * case, by skipping this check, we will send one data packet + * towards the requested net. + */ + if ((strq != NULL) && TAILQ_FIRST(&strq->outqueue) && + (net != NULL && TAILQ_FIRST(&strq->outqueue)->net != net) && + (SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0)) { + if (strq == asoc->last_out_stream) { + strq = NULL; + } else { + strqt = strq; + goto rrp_pd_again; + } + } + asoc->last_out_stream = strq; + return; +} + + +/* + * Priority algorithm. + * Always prefers streams based on their priority id. + */ +static void +sctp_ss_prio_clear(struct sctp_tcb *stcb, struct sctp_association *asoc, + int clear_values, int holds_lock) +{ + uint16_t i; + + for (i = 0; i < stcb->asoc.streamoutcnt; i++) { + if (!TAILQ_EMPTY(&stcb->asoc.strmout[i].outqueue)) { + if (clear_values) + stcb->asoc.strmout[i].ss_params.prio.priority = 0; + sctp_ss_default_remove(stcb, &stcb->asoc, &stcb->asoc.strmout[i], NULL, holds_lock); + } + } + return; +} + +static void +sctp_ss_prio_init_stream(struct sctp_stream_out *strq) +{ + strq->ss_params.prio.next_spoke.tqe_next = NULL; + strq->ss_params.prio.next_spoke.tqe_prev = NULL; + strq->ss_params.prio.priority = 0; + return; +} + +static void +sctp_ss_prio_add(struct sctp_tcb *stcb, struct sctp_association *asoc, + struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp, + int holds_lock) +{ + struct sctp_stream_out *strqt; + + if (holds_lock == 0) { + SCTP_TCB_SEND_LOCK(stcb); + } + if ((strq->ss_params.prio.next_spoke.tqe_next == NULL) && + (strq->ss_params.prio.next_spoke.tqe_prev == NULL)) { + + if (TAILQ_EMPTY(&asoc->ss_data.out_wheel)) { + TAILQ_INSERT_HEAD(&asoc->ss_data.out_wheel, strq, ss_params.prio.next_spoke); + } else { + strqt = TAILQ_FIRST(&asoc->ss_data.out_wheel); + while (strqt != NULL && strqt->ss_params.prio.priority < strq->ss_params.prio.priority) { + strqt = TAILQ_NEXT(strqt, ss_params.prio.next_spoke); + } + if (strqt != NULL) { + TAILQ_INSERT_BEFORE(strqt, strq, ss_params.prio.next_spoke); + } else { + TAILQ_INSERT_TAIL(&asoc->ss_data.out_wheel, strq, ss_params.prio.next_spoke); + } + } + } + if (holds_lock == 0) { + SCTP_TCB_SEND_UNLOCK(stcb); + } + return; +} + +static void +sctp_ss_prio_remove(struct sctp_tcb *stcb, struct sctp_association *asoc, + struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp, + int holds_lock) +{ + /* take off and then setup so we know it is not on the wheel */ + if (holds_lock == 0) { + SCTP_TCB_SEND_LOCK(stcb); + } + if (TAILQ_EMPTY(&strq->outqueue)) { + if (asoc->last_out_stream == strq) { + asoc->last_out_stream = TAILQ_PREV(asoc->last_out_stream, sctpwheel_listhead, + ss_params.prio.next_spoke); + if (asoc->last_out_stream == NULL) { + asoc->last_out_stream = TAILQ_LAST(&asoc->ss_data.out_wheel, + sctpwheel_listhead); + } + if (asoc->last_out_stream == strq) { + asoc->last_out_stream = NULL; + } + } + TAILQ_REMOVE(&asoc->ss_data.out_wheel, strq, ss_params.rr.next_spoke); + strq->ss_params.prio.next_spoke.tqe_next = NULL; + strq->ss_params.prio.next_spoke.tqe_prev = NULL; + } + if (holds_lock == 0) { + SCTP_TCB_SEND_UNLOCK(stcb); + } + return; +} + +static struct sctp_stream_out * +sctp_ss_prio_select(struct sctp_tcb *stcb, struct sctp_nets *net, + struct sctp_association *asoc) +{ + struct sctp_stream_out *strq, *strqt, *strqn; + + strqt = asoc->last_out_stream; +prio_again: + /* Find the next stream to use */ + if (strqt == NULL) { + strq = TAILQ_FIRST(&asoc->ss_data.out_wheel); + } else { + strqn = TAILQ_NEXT(strqt, ss_params.prio.next_spoke); + if (strqn != NULL && + strqn->ss_params.prio.priority == strqt->ss_params.prio.priority) { + strq = TAILQ_NEXT(strqt, ss_params.prio.next_spoke); + } else { + strq = TAILQ_FIRST(&asoc->ss_data.out_wheel); + } + } + + /* + * If CMT is off, we must validate that the stream in question has + * the first item pointed towards are network destionation requested + * by the caller. Note that if we turn out to be locked to a stream + * (assigning TSN's then we must stop, since we cannot look for + * another stream with data to send to that destination). In CMT's + * case, by skipping this check, we will send one data packet + * towards the requested net. + */ + if (net != NULL && strq != NULL && + SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) { + if (TAILQ_FIRST(&strq->outqueue) && + TAILQ_FIRST(&strq->outqueue)->net != NULL && + TAILQ_FIRST(&strq->outqueue)->net != net) { + if (strq == asoc->last_out_stream) { + return (NULL); + } else { + strqt = strq; + goto prio_again; + } + } + } + return (strq); +} + +static int +sctp_ss_prio_get_value(struct sctp_tcb *stcb, struct sctp_association *asoc, + struct sctp_stream_out *strq, uint16_t * value) +{ + if (strq == NULL) { + return (-1); + } + *value = strq->ss_params.prio.priority; + return (1); +} + +static int +sctp_ss_prio_set_value(struct sctp_tcb *stcb, struct sctp_association *asoc, + struct sctp_stream_out *strq, uint16_t value) +{ + if (strq == NULL) { + return (-1); + } + strq->ss_params.prio.priority = value; + sctp_ss_prio_remove(stcb, asoc, strq, NULL, 1); + sctp_ss_prio_add(stcb, asoc, strq, NULL, 1); + return (1); +} + +/* + * Fair bandwidth algorithm. + * Maintains an equal troughput per stream. + */ +static void +sctp_ss_fb_clear(struct sctp_tcb *stcb, struct sctp_association *asoc, + int clear_values, int holds_lock) +{ + uint16_t i; + + for (i = 0; i < stcb->asoc.streamoutcnt; i++) { + if (!TAILQ_EMPTY(&stcb->asoc.strmout[i].outqueue)) { + if (clear_values) { + stcb->asoc.strmout[i].ss_params.fb.rounds = -1; + } + sctp_ss_default_remove(stcb, &stcb->asoc, &stcb->asoc.strmout[i], NULL, holds_lock); + } + } + return; +} + +static void +sctp_ss_fb_init_stream(struct sctp_stream_out *strq) +{ + strq->ss_params.fb.next_spoke.tqe_next = NULL; + strq->ss_params.fb.next_spoke.tqe_prev = NULL; + strq->ss_params.fb.rounds = -1; + return; +} + +static void +sctp_ss_fb_add(struct sctp_tcb *stcb, struct sctp_association *asoc, + struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp, + int holds_lock) +{ + if (holds_lock == 0) { + SCTP_TCB_SEND_LOCK(stcb); + } + if ((strq->ss_params.rr.next_spoke.tqe_next == NULL) && + (strq->ss_params.rr.next_spoke.tqe_prev == NULL)) { + if (!TAILQ_EMPTY(&strq->outqueue) && strq->ss_params.fb.rounds < 0) + strq->ss_params.fb.rounds = TAILQ_FIRST(&strq->outqueue)->length; + TAILQ_INSERT_TAIL(&asoc->ss_data.out_wheel, strq, ss_params.rr.next_spoke); + } + if (holds_lock == 0) { + SCTP_TCB_SEND_UNLOCK(stcb); + } + return; +} + +static void +sctp_ss_fb_remove(struct sctp_tcb *stcb, struct sctp_association *asoc, + struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp, + int holds_lock) +{ + /* take off and then setup so we know it is not on the wheel */ + if (holds_lock == 0) { + SCTP_TCB_SEND_LOCK(stcb); + } + if (TAILQ_EMPTY(&strq->outqueue)) { + if (asoc->last_out_stream == strq) { + asoc->last_out_stream = TAILQ_PREV(asoc->last_out_stream, sctpwheel_listhead, + ss_params.fb.next_spoke); + if (asoc->last_out_stream == NULL) { + asoc->last_out_stream = TAILQ_LAST(&asoc->ss_data.out_wheel, + sctpwheel_listhead); + } + if (asoc->last_out_stream == strq) { + asoc->last_out_stream = NULL; + } + } + strq->ss_params.fb.rounds = -1; + TAILQ_REMOVE(&asoc->ss_data.out_wheel, strq, ss_params.fb.next_spoke); + strq->ss_params.fb.next_spoke.tqe_next = NULL; + strq->ss_params.fb.next_spoke.tqe_prev = NULL; + } + if (holds_lock == 0) { + SCTP_TCB_SEND_UNLOCK(stcb); + } + return; +} + +static struct sctp_stream_out * +sctp_ss_fb_select(struct sctp_tcb *stcb, struct sctp_nets *net, + struct sctp_association *asoc) +{ + struct sctp_stream_out *strq = NULL, *strqt; + + if (TAILQ_FIRST(&asoc->ss_data.out_wheel) == TAILQ_LAST(&asoc->ss_data.out_wheel, sctpwheel_listhead)) { + strqt = TAILQ_FIRST(&asoc->ss_data.out_wheel); + } else { + if (asoc->last_out_stream != NULL) { + strqt = TAILQ_NEXT(asoc->last_out_stream, ss_params.fb.next_spoke); + } else { + strqt = TAILQ_FIRST(&asoc->ss_data.out_wheel); + } + } + do { + if ((strqt != NULL) && TAILQ_FIRST(&strqt->outqueue) && + TAILQ_FIRST(&strqt->outqueue)->net != NULL && + ((net == NULL || TAILQ_FIRST(&strqt->outqueue)->net == net) || + (SCTP_BASE_SYSCTL(sctp_cmt_on_off) > 0))) { + if ((strqt->ss_params.fb.rounds >= 0) && (strq == NULL || + strqt->ss_params.fb.rounds < strq->ss_params.fb.rounds)) { + strq = strqt; + } + } + if (strqt != NULL) { + strqt = TAILQ_NEXT(strqt, ss_params.fb.next_spoke); + } else { + strqt = TAILQ_FIRST(&asoc->ss_data.out_wheel); + } + } while (strqt != strq); + return (strq); +} + +static void +sctp_ss_fb_scheduled(struct sctp_tcb *stcb, struct sctp_nets *net, + struct sctp_association *asoc, struct sctp_stream_out *strq, + int moved_how_much) +{ + struct sctp_stream_out *strqt; + int subtract; + + subtract = strq->ss_params.fb.rounds; + TAILQ_FOREACH(strqt, &asoc->ss_data.out_wheel, ss_params.fb.next_spoke) { + strqt->ss_params.fb.rounds -= subtract; + if (strqt->ss_params.fb.rounds < 0) + strqt->ss_params.fb.rounds = 0; + } + if (TAILQ_FIRST(&strq->outqueue)) { + strq->ss_params.fb.rounds = TAILQ_FIRST(&strq->outqueue)->length; + } else { + strq->ss_params.fb.rounds = -1; + } + asoc->last_out_stream = strq; + return; +} + +/* + * First-come, first-serve algorithm. + * Maintains the order provided by the application. + */ +static void +sctp_ss_fcfs_init(struct sctp_tcb *stcb, struct sctp_association *asoc, + int holds_lock) +{ + int x, element = 0, add_more = 1; + struct sctp_stream_queue_pending *sp; + uint16_t i; + + TAILQ_INIT(&asoc->ss_data.out_list); + while (add_more) { + add_more = 0; + for (i = 0; i < stcb->asoc.streamoutcnt; i++) { + sp = TAILQ_FIRST(&asoc->ss_data.out_list); + x = element; + while (sp != NULL && x > 0) { + sp = TAILQ_NEXT(sp, next); + } + if (sp != NULL) { + sctp_ss_default_add(stcb, &stcb->asoc, &stcb->asoc.strmout[i], NULL, holds_lock); + add_more = 1; + } + } + element++; + } + return; +} + +static void +sctp_ss_fcfs_clear(struct sctp_tcb *stcb, struct sctp_association *asoc, + int clear_values, int holds_lock) +{ + if (clear_values) { + while (!TAILQ_EMPTY(&asoc->ss_data.out_list)) { + TAILQ_REMOVE(&asoc->ss_data.out_list, TAILQ_FIRST(&asoc->ss_data.out_list), next); + } + } + return; +} + +static void +sctp_ss_fcfs_init_stream(struct sctp_stream_out *strq) +{ + /* Nothing to be done here */ + return; +} + +static void +sctp_ss_fcfs_add(struct sctp_tcb *stcb, struct sctp_association *asoc, + struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp, + int holds_lock) +{ + if (holds_lock == 0) { + SCTP_TCB_SEND_LOCK(stcb); + } + if (sp && (sp->next.tqe_next == NULL) && + (sp->next.tqe_prev == NULL)) { + TAILQ_INSERT_TAIL(&asoc->ss_data.out_list, sp, next); + } + if (holds_lock == 0) { + SCTP_TCB_SEND_UNLOCK(stcb); + } + return; +} + +static int +sctp_ss_fcfs_is_empty(struct sctp_tcb *stcb, struct sctp_association *asoc) +{ + if (TAILQ_EMPTY(&asoc->ss_data.out_list)) { + return (1); + } else { + return (0); + } +} + +static void +sctp_ss_fcfs_remove(struct sctp_tcb *stcb, struct sctp_association *asoc, + struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp, + int holds_lock) +{ + if (holds_lock == 0) { + SCTP_TCB_SEND_LOCK(stcb); + } + if (sp && + ((sp->next.tqe_next != NULL) || + (sp->next.tqe_prev != NULL))) { + TAILQ_REMOVE(&asoc->ss_data.out_list, sp, next); + } + if (holds_lock == 0) { + SCTP_TCB_SEND_UNLOCK(stcb); + } + return; +} + + +static struct sctp_stream_out * +sctp_ss_fcfs_select(struct sctp_tcb *stcb, struct sctp_nets *net, + struct sctp_association *asoc) +{ + struct sctp_stream_out *strq; + struct sctp_stream_queue_pending *sp; + + sp = TAILQ_FIRST(&asoc->ss_data.out_list); +default_again: + if (sp != NULL) { + strq = &asoc->strmout[sp->stream]; + } else { + strq = NULL; + } + + /* + * If CMT is off, we must validate that the stream in question has + * the first item pointed towards are network destionation requested + * by the caller. Note that if we turn out to be locked to a stream + * (assigning TSN's then we must stop, since we cannot look for + * another stream with data to send to that destination). In CMT's + * case, by skipping this check, we will send one data packet + * towards the requested net. + */ + if (net != NULL && strq != NULL && + SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) { + if (TAILQ_FIRST(&strq->outqueue) && + TAILQ_FIRST(&strq->outqueue)->net != NULL && + TAILQ_FIRST(&strq->outqueue)->net != net) { + sp = TAILQ_NEXT(sp, next); + goto default_again; + } + } + return (strq); +} + +struct sctp_ss_functions sctp_ss_functions[] = { +/* SCTP_SS_DEFAULT */ + { + .sctp_ss_init = sctp_ss_default_init, + .sctp_ss_clear = sctp_ss_default_clear, + .sctp_ss_init_stream = sctp_ss_default_init_stream, + .sctp_ss_add_to_stream = sctp_ss_default_add, + .sctp_ss_is_empty = sctp_ss_default_is_empty, + .sctp_ss_remove_from_stream = sctp_ss_default_remove, + .sctp_ss_select_stream = sctp_ss_default_select, + .sctp_ss_scheduled = sctp_ss_default_scheduled, + .sctp_ss_packet_done = sctp_ss_default_packet_done, + .sctp_ss_get_value = sctp_ss_default_get_value, + .sctp_ss_set_value = sctp_ss_default_set_value + }, +/* SCTP_SS_ROUND_ROBIN */ + { + .sctp_ss_init = sctp_ss_default_init, + .sctp_ss_clear = sctp_ss_default_clear, + .sctp_ss_init_stream = sctp_ss_default_init_stream, + .sctp_ss_add_to_stream = sctp_ss_rr_add, + .sctp_ss_is_empty = sctp_ss_default_is_empty, + .sctp_ss_remove_from_stream = sctp_ss_default_remove, + .sctp_ss_select_stream = sctp_ss_default_select, + .sctp_ss_scheduled = sctp_ss_default_scheduled, + .sctp_ss_packet_done = sctp_ss_default_packet_done, + .sctp_ss_get_value = sctp_ss_default_get_value, + .sctp_ss_set_value = sctp_ss_default_set_value + }, +/* SCTP_SS_ROUND_ROBIN_PACKET */ + { + .sctp_ss_init = sctp_ss_default_init, + .sctp_ss_clear = sctp_ss_default_clear, + .sctp_ss_init_stream = sctp_ss_default_init_stream, + .sctp_ss_add_to_stream = sctp_ss_rrp_add, + .sctp_ss_is_empty = sctp_ss_default_is_empty, + .sctp_ss_remove_from_stream = sctp_ss_default_remove, + .sctp_ss_select_stream = sctp_ss_rrp_select, + .sctp_ss_scheduled = sctp_ss_default_scheduled, + .sctp_ss_packet_done = sctp_ss_rrp_packet_done, + .sctp_ss_get_value = sctp_ss_default_get_value, + .sctp_ss_set_value = sctp_ss_default_set_value + }, +/* SCTP_SS_PRIORITY */ + { + .sctp_ss_init = sctp_ss_default_init, + .sctp_ss_clear = sctp_ss_prio_clear, + .sctp_ss_init_stream = sctp_ss_prio_init_stream, + .sctp_ss_add_to_stream = sctp_ss_prio_add, + .sctp_ss_is_empty = sctp_ss_default_is_empty, + .sctp_ss_remove_from_stream = sctp_ss_prio_remove, + .sctp_ss_select_stream = sctp_ss_prio_select, + .sctp_ss_scheduled = sctp_ss_default_scheduled, + .sctp_ss_packet_done = sctp_ss_default_packet_done, + .sctp_ss_get_value = sctp_ss_prio_get_value, + .sctp_ss_set_value = sctp_ss_prio_set_value + }, +/* SCTP_SS_FAIR_BANDWITH */ + { + .sctp_ss_init = sctp_ss_default_init, + .sctp_ss_clear = sctp_ss_fb_clear, + .sctp_ss_init_stream = sctp_ss_fb_init_stream, + .sctp_ss_add_to_stream = sctp_ss_fb_add, + .sctp_ss_is_empty = sctp_ss_default_is_empty, + .sctp_ss_remove_from_stream = sctp_ss_fb_remove, + .sctp_ss_select_stream = sctp_ss_fb_select, + .sctp_ss_scheduled = sctp_ss_fb_scheduled, + .sctp_ss_packet_done = sctp_ss_default_packet_done, + .sctp_ss_get_value = sctp_ss_default_get_value, + .sctp_ss_set_value = sctp_ss_default_set_value + }, +/* SCTP_SS_FIRST_COME */ + { + .sctp_ss_init = sctp_ss_fcfs_init, + .sctp_ss_clear = sctp_ss_fcfs_clear, + .sctp_ss_init_stream = sctp_ss_fcfs_init_stream, + .sctp_ss_add_to_stream = sctp_ss_fcfs_add, + .sctp_ss_is_empty = sctp_ss_fcfs_is_empty, + .sctp_ss_remove_from_stream = sctp_ss_fcfs_remove, + .sctp_ss_select_stream = sctp_ss_fcfs_select, + .sctp_ss_scheduled = sctp_ss_default_scheduled, + .sctp_ss_packet_done = sctp_ss_default_packet_done, + .sctp_ss_get_value = sctp_ss_default_get_value, + .sctp_ss_set_value = sctp_ss_default_set_value + } +}; diff --git a/sys/netinet/sctp_structs.h b/sys/netinet/sctp_structs.h index 94d0395cc3ab..639a6cea94ff 100644 --- a/sys/netinet/sctp_structs.h +++ b/sys/netinet/sctp_structs.h @@ -476,11 +476,54 @@ struct sctp_stream_in { uint8_t delivery_started; }; -/* This struct is used to track the traffic on outbound streams */ TAILQ_HEAD(sctpwheel_listhead, sctp_stream_out); +TAILQ_HEAD(sctplist_listhead, sctp_stream_queue_pending); + +/* Round-robin schedulers */ +struct ss_rr { + /* next link in wheel */ + TAILQ_ENTRY(sctp_stream_out) next_spoke; +}; + +/* Priority scheduler */ +struct ss_prio { + /* next link in wheel */ + TAILQ_ENTRY(sctp_stream_out) next_spoke; + /* priority id */ + uint16_t priority; +}; + +/* Fair Bandwidth scheduler */ +struct ss_fb { + /* next link in wheel */ + TAILQ_ENTRY(sctp_stream_out) next_spoke; + /* stores message size */ + int32_t rounds; +}; + +/* + * This union holds all data necessary for + * different stream schedulers. + */ +union scheduling_data { + struct sctpwheel_listhead out_wheel; + struct sctplist_listhead out_list; +}; + +/* + * This union holds all parameters per stream + * necessary for different stream schedulers. + */ +union scheduling_parameters { + struct ss_rr rr; + struct ss_prio prio; + struct ss_fb fb; +}; + +/* This struct is used to track the traffic on outbound streams */ struct sctp_stream_out { struct sctp_streamhead outqueue; - TAILQ_ENTRY(sctp_stream_out) next_spoke; /* next link in wheel */ + union scheduling_parameters ss_params; uint16_t stream_no; uint16_t next_sequence_sent; /* next one I expect to send out */ uint8_t last_msg_incomplete; @@ -571,6 +614,33 @@ struct sctp_cc_functions { struct sctp_tcb *stcb, struct sctp_nets *net); }; +/* + * RS - Structure to hold function pointers to the functions responsible + * for stream scheduling. + */ +struct sctp_ss_functions { + void (*sctp_ss_init) (struct sctp_tcb *stcb, struct sctp_association *asoc, + int holds_lock); + void (*sctp_ss_clear) (struct sctp_tcb *stcb, struct sctp_association *asoc, + int clear_values, int holds_lock); + void (*sctp_ss_init_stream) (struct sctp_stream_out *strq); + void (*sctp_ss_add_to_stream) (struct sctp_tcb *stcb, struct sctp_association *asoc, + struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp, int holds_lock); + int (*sctp_ss_is_empty) (struct sctp_tcb *stcb, struct sctp_association *asoc); + void (*sctp_ss_remove_from_stream) (struct sctp_tcb *stcb, struct sctp_association *asoc, + struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp, int holds_lock); + struct sctp_stream_out *(*sctp_ss_select_stream) (struct sctp_tcb *stcb, + struct sctp_nets *net, struct sctp_association *asoc); + void (*sctp_ss_scheduled) (struct sctp_tcb *stcb, struct sctp_nets *net, + struct sctp_association *asoc, struct sctp_stream_out *strq, int moved_how_much); + void (*sctp_ss_packet_done) (struct sctp_tcb *stcb, struct sctp_nets *net, + struct sctp_association *asoc); + int (*sctp_ss_get_value) (struct sctp_tcb *stcb, struct sctp_association *asoc, + struct sctp_stream_out *strq, uint16_t * value); + int (*sctp_ss_set_value) (struct sctp_tcb *stcb, struct sctp_association *asoc, + struct sctp_stream_out *strq, uint16_t value); +}; + /* used to save ASCONF chunks for retransmission */ TAILQ_HEAD(sctp_asconf_head, sctp_asconf); struct sctp_asconf { @@ -658,7 +728,7 @@ struct sctp_association { * set our rwnd to the space in the socket minus also the * size_on_delivery_queue. */ - struct sctpwheel_listhead out_wheel; + union scheduling_data ss_data; /* * This pointer will be set to NULL most of the time. But when we @@ -717,6 +787,10 @@ struct sctp_association { * module */ uint32_t congestion_control_module; + /* RS - the stream scheduling functions are in this struct */ + struct sctp_ss_functions ss_functions; + /* RS - value to store the currently loaded stream scheduling module */ + uint32_t stream_scheduling_module; uint32_t vrf_id; diff --git a/sys/netinet/sctp_sysctl.c b/sys/netinet/sctp_sysctl.c index 94b1dd8d9f55..255d95d80eda 100644 --- a/sys/netinet/sctp_sysctl.c +++ b/sys/netinet/sctp_sysctl.c @@ -105,6 +105,8 @@ sctp_init_sysctls() SCTP_BASE_SYSCTL(sctp_logging_level) = SCTPCTL_LOGGING_LEVEL_DEFAULT; /* JRS - Variable for default congestion control module */ SCTP_BASE_SYSCTL(sctp_default_cc_module) = SCTPCTL_DEFAULT_CC_MODULE_DEFAULT; + /* RS - Variable for default stream scheduling module */ + SCTP_BASE_SYSCTL(sctp_default_ss_module) = SCTPCTL_DEFAULT_SS_MODULE_DEFAULT; SCTP_BASE_SYSCTL(sctp_default_frag_interleave) = SCTPCTL_DEFAULT_FRAG_INTERLEAVE_DEFAULT; SCTP_BASE_SYSCTL(sctp_mobility_base) = SCTPCTL_MOBILITY_BASE_DEFAULT; SCTP_BASE_SYSCTL(sctp_mobility_fasthandoff) = SCTPCTL_MOBILITY_FASTHANDOFF_DEFAULT; @@ -620,6 +622,7 @@ sysctl_sctp_check(SYSCTL_HANDLER_ARGS) RANGECHK(SCTP_BASE_SYSCTL(sctp_max_retran_chunk), SCTPCTL_MAX_RETRAN_CHUNK_MIN, SCTPCTL_MAX_RETRAN_CHUNK_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_logging_level), SCTPCTL_LOGGING_LEVEL_MIN, SCTPCTL_LOGGING_LEVEL_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_default_cc_module), SCTPCTL_DEFAULT_CC_MODULE_MIN, SCTPCTL_DEFAULT_CC_MODULE_MAX); + RANGECHK(SCTP_BASE_SYSCTL(sctp_default_ss_module), SCTPCTL_DEFAULT_SS_MODULE_MIN, SCTPCTL_DEFAULT_SS_MODULE_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_default_frag_interleave), SCTPCTL_DEFAULT_FRAG_INTERLEAVE_MIN, SCTPCTL_DEFAULT_FRAG_INTERLEAVE_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_vtag_time_wait), SCTPCTL_TIME_WAIT_MIN, SCTPCTL_TIME_WAIT_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_buffer_splitting), SCTPCTL_BUFFER_SPLITTING_MIN, SCTPCTL_BUFFER_SPLITTING_MAX); @@ -1025,6 +1028,10 @@ SYSCTL_PROC(_net_inet_sctp, OID_AUTO, default_cc_module, CTLTYPE_UINT | CTLFLAG_ &SCTP_BASE_SYSCTL(sctp_default_cc_module), 0, sysctl_sctp_check, "IU", SCTPCTL_DEFAULT_CC_MODULE_DESC); +SYSCTL_PROC(_net_inet_sctp, OID_AUTO, default_ss_module, CTLTYPE_UINT | CTLFLAG_RW, + &SCTP_BASE_SYSCTL(sctp_default_ss_module), 0, sysctl_sctp_check, "IU", + SCTPCTL_DEFAULT_SS_MODULE_DESC); + SYSCTL_PROC(_net_inet_sctp, OID_AUTO, default_frag_interleave, CTLTYPE_UINT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_default_frag_interleave), 0, sysctl_sctp_check, "IU", SCTPCTL_DEFAULT_FRAG_INTERLEAVE_DESC); diff --git a/sys/netinet/sctp_sysctl.h b/sys/netinet/sctp_sysctl.h index aa8688764ed5..ea17af1b4256 100644 --- a/sys/netinet/sctp_sysctl.h +++ b/sys/netinet/sctp_sysctl.h @@ -96,6 +96,8 @@ struct sctp_sysctl { uint32_t sctp_logging_level; /* JRS - Variable for default congestion control module */ uint32_t sctp_default_cc_module; + /* RS - Variable for default stream scheduling module */ + uint32_t sctp_default_ss_module; uint32_t sctp_default_frag_interleave; uint32_t sctp_mobility_base; uint32_t sctp_mobility_fasthandoff; @@ -444,6 +446,12 @@ struct sctp_sysctl { #define SCTPCTL_DEFAULT_CC_MODULE_MAX 2 #define SCTPCTL_DEFAULT_CC_MODULE_DEFAULT 0 +/* RS - default stream scheduling module sysctl */ +#define SCTPCTL_DEFAULT_SS_MODULE_DESC "Default stream scheduling module" +#define SCTPCTL_DEFAULT_SS_MODULE_MIN 0 +#define SCTPCTL_DEFAULT_SS_MODULE_MAX 5 +#define SCTPCTL_DEFAULT_SS_MODULE_DEFAULT 0 + /* RRS - default fragment interleave */ #define SCTPCTL_DEFAULT_FRAG_INTERLEAVE_DESC "Default fragment interleave level" #define SCTPCTL_DEFAULT_FRAG_INTERLEAVE_MIN 0 diff --git a/sys/netinet/sctp_timer.c b/sys/netinet/sctp_timer.c index 6ecc1786bf4f..dcf8fd739369 100644 --- a/sys/netinet/sctp_timer.c +++ b/sys/netinet/sctp_timer.c @@ -1513,9 +1513,8 @@ static void sctp_audit_stream_queues_for_size(struct sctp_inpcb *inp, struct sctp_tcb *stcb) { - struct sctp_stream_out *outs; struct sctp_stream_queue_pending *sp; - unsigned int chks_in_queue = 0; + unsigned int i, chks_in_queue = 0; int being_filled = 0; /* @@ -1530,13 +1529,17 @@ sctp_audit_stream_queues_for_size(struct sctp_inpcb *inp, stcb->asoc.sent_queue_retran_cnt = 0; } SCTP_TCB_SEND_LOCK(stcb); - if (TAILQ_EMPTY(&stcb->asoc.out_wheel)) { - int i, cnt = 0; + if (stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, &stcb->asoc)) { + int cnt = 0; /* Check to see if a spoke fell off the wheel */ for (i = 0; i < stcb->asoc.streamoutcnt; i++) { if (!TAILQ_EMPTY(&stcb->asoc.strmout[i].outqueue)) { - sctp_insert_on_wheel(stcb, &stcb->asoc, &stcb->asoc.strmout[i], 1); + stcb->asoc.ss_functions.sctp_ss_add_to_stream(stcb, + &stcb->asoc, + &stcb->asoc.strmout[i], + NULL, + 1); cnt++; } } @@ -1552,9 +1555,9 @@ sctp_audit_stream_queues_for_size(struct sctp_inpcb *inp, } SCTP_TCB_SEND_UNLOCK(stcb); /* Check to see if some data queued, if so report it */ - TAILQ_FOREACH(outs, &stcb->asoc.out_wheel, next_spoke) { - if (!TAILQ_EMPTY(&outs->outqueue)) { - TAILQ_FOREACH(sp, &outs->outqueue, next) { + for (i = 0; i < stcb->asoc.streamoutcnt; i++) { + if (!TAILQ_EMPTY(&stcb->asoc.strmout[i].outqueue)) { + TAILQ_FOREACH(sp, &stcb->asoc.strmout[i].outqueue, next) { if (sp->msg_is_complete) being_filled++; chks_in_queue++; diff --git a/sys/netinet/sctp_uio.h b/sys/netinet/sctp_uio.h index 6f59f034c17b..77b86badaec9 100644 --- a/sys/netinet/sctp_uio.h +++ b/sys/netinet/sctp_uio.h @@ -531,6 +531,12 @@ struct sctp_assoc_value { uint32_t assoc_value; }; +struct sctp_stream_value { + sctp_assoc_t assoc_id; + uint16_t stream_id; + uint16_t stream_value; +}; + struct sctp_assoc_ids { uint32_t gaids_number_of_ids; sctp_assoc_t gaids_assoc_id[]; diff --git a/sys/netinet/sctp_usrreq.c b/sys/netinet/sctp_usrreq.c index e65fe47e446a..d5d50e4b7f70 100644 --- a/sys/netinet/sctp_usrreq.c +++ b/sys/netinet/sctp_usrreq.c @@ -53,6 +53,7 @@ __FBSDID("$FreeBSD$"); extern struct sctp_cc_functions sctp_cc_functions[]; +extern struct sctp_ss_functions sctp_ss_functions[]; void sctp_init(void) @@ -1757,6 +1758,47 @@ sctp_getopt(struct socket *so, int optname, void *optval, size_t *optsize, *optsize = sizeof(*av); } break; + /* RS - Get socket option for pluggable stream scheduling */ + case SCTP_PLUGGABLE_SS: + { + struct sctp_assoc_value *av; + + SCTP_CHECK_AND_CAST(av, optval, struct sctp_assoc_value, *optsize); + SCTP_FIND_STCB(inp, stcb, av->assoc_id); + if (stcb) { + av->assoc_value = stcb->asoc.stream_scheduling_module; + SCTP_TCB_UNLOCK(stcb); + } else { + av->assoc_value = inp->sctp_ep.sctp_default_ss_module; + } + *optsize = sizeof(*av); + } + break; + case SCTP_SS_VALUE: + { + struct sctp_stream_value *av; + + SCTP_CHECK_AND_CAST(av, optval, struct sctp_stream_value, *optsize); + SCTP_FIND_STCB(inp, stcb, av->assoc_id); + if (stcb) { + if (stcb->asoc.ss_functions.sctp_ss_get_value(stcb, &stcb->asoc, &stcb->asoc.strmout[av->stream_id], + &av->stream_value) < 0) { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); + error = EINVAL; + } else { + *optsize = sizeof(*av); + } + SCTP_TCB_UNLOCK(stcb); + } else { + /* + * Can't get stream value without + * association + */ + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); + error = EINVAL; + } + } + break; case SCTP_GET_ADDR_LEN: { struct sctp_assoc_value *av; @@ -2910,7 +2952,76 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); error = EINVAL; break; - }; + } + } + } + break; + /* RS - Set socket option for pluggable stream scheduling */ + case SCTP_PLUGGABLE_SS: + { + struct sctp_assoc_value *av; + + SCTP_CHECK_AND_CAST(av, optval, struct sctp_assoc_value, optsize); + SCTP_FIND_STCB(inp, stcb, av->assoc_id); + if (stcb) { + switch (av->assoc_value) { + case SCTP_SS_DEFAULT: + case SCTP_SS_ROUND_ROBIN: + case SCTP_SS_ROUND_ROBIN_PACKET: + case SCTP_SS_PRIORITY: + case SCTP_SS_FAIR_BANDWITH: + case SCTP_SS_FIRST_COME: + stcb->asoc.ss_functions.sctp_ss_clear(stcb, &stcb->asoc, 1, 1); + stcb->asoc.ss_functions = sctp_ss_functions[av->assoc_value]; + stcb->asoc.stream_scheduling_module = av->assoc_value; + stcb->asoc.ss_functions.sctp_ss_init(stcb, &stcb->asoc, 1); + break; + default: + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); + error = EINVAL; + break; + } + SCTP_TCB_UNLOCK(stcb); + } else { + switch (av->assoc_value) { + case SCTP_SS_DEFAULT: + case SCTP_SS_ROUND_ROBIN: + case SCTP_SS_ROUND_ROBIN_PACKET: + case SCTP_SS_PRIORITY: + case SCTP_SS_FAIR_BANDWITH: + case SCTP_SS_FIRST_COME: + SCTP_INP_WLOCK(inp); + inp->sctp_ep.sctp_default_ss_module = av->assoc_value; + SCTP_INP_WUNLOCK(inp); + break; + default: + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); + error = EINVAL; + break; + } + } + } + break; + case SCTP_SS_VALUE: + { + struct sctp_stream_value *av; + + SCTP_CHECK_AND_CAST(av, optval, struct sctp_stream_value, optsize); + SCTP_FIND_STCB(inp, stcb, av->assoc_id); + if (stcb) { + if (stcb->asoc.ss_functions.sctp_ss_set_value(stcb, &stcb->asoc, &stcb->asoc.strmout[av->stream_id], + av->stream_value) < 0) { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); + error = EINVAL; + } + SCTP_TCB_UNLOCK(stcb); + } else { + /* + * Can't set stream value without + * association + */ + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); + error = EINVAL; } } break; @@ -3306,7 +3417,6 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, struct sctp_stream_out *oldstream; struct sctp_stream_queue_pending *sp, *nsp; - int removed; oldstream = stcb->asoc.strmout; /* get some more */ @@ -3324,22 +3434,13 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, * initializing the new stuff. */ SCTP_TCB_SEND_LOCK(stcb); + stcb->asoc.ss_functions.sctp_ss_clear(stcb, &stcb->asoc, 0, 1); for (i = 0; i < stcb->asoc.streamoutcnt; i++) { TAILQ_INIT(&stcb->asoc.strmout[i].outqueue); stcb->asoc.strmout[i].next_sequence_sent = oldstream[i].next_sequence_sent; stcb->asoc.strmout[i].last_msg_incomplete = oldstream[i].last_msg_incomplete; stcb->asoc.strmout[i].stream_no = i; - if (oldstream[i].next_spoke.tqe_next) { - sctp_remove_from_wheel(stcb, &stcb->asoc, &oldstream[i], 1); - stcb->asoc.strmout[i].next_spoke.tqe_next = NULL; - stcb->asoc.strmout[i].next_spoke.tqe_prev = NULL; - removed = 1; - } else { - /* not on out wheel */ - stcb->asoc.strmout[i].next_spoke.tqe_next = NULL; - stcb->asoc.strmout[i].next_spoke.tqe_prev = NULL; - removed = 0; - } + stcb->asoc.ss_functions.sctp_ss_init_stream(&oldstream[i]); /* * now anything on those * queues? @@ -3348,13 +3449,6 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, TAILQ_REMOVE(&oldstream[i].outqueue, sp, next); TAILQ_INSERT_TAIL(&stcb->asoc.strmout[i].outqueue, sp, next); } - /* Did we disrupt the wheel? */ - if (removed) { - sctp_insert_on_wheel(stcb, - &stcb->asoc, - &stcb->asoc.strmout[i], - 1); - } /* * Now move assoc pointers * too @@ -3367,13 +3461,13 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, } } /* now the new streams */ + stcb->asoc.ss_functions.sctp_ss_init(stcb, &stcb->asoc, 1); for (i = stcb->asoc.streamoutcnt; i < (stcb->asoc.streamoutcnt + addstrmcnt); i++) { stcb->asoc.strmout[i].next_sequence_sent = 0x0; TAILQ_INIT(&stcb->asoc.strmout[i].outqueue); stcb->asoc.strmout[i].stream_no = i; stcb->asoc.strmout[i].last_msg_incomplete = 0; - stcb->asoc.strmout[i].next_spoke.tqe_next = NULL; - stcb->asoc.strmout[i].next_spoke.tqe_prev = NULL; + stcb->asoc.ss_functions.sctp_ss_init_stream(&stcb->asoc.strmout[i]); } stcb->asoc.strm_realoutsize = stcb->asoc.streamoutcnt + addstrmcnt; SCTP_FREE(oldstream, SCTP_M_STRMO); diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c index 3ef22972bcdb..6e01627fb345 100644 --- a/sys/netinet/sctputil.c +++ b/sys/netinet/sctputil.c @@ -55,6 +55,7 @@ __FBSDID("$FreeBSD$"); #endif extern struct sctp_cc_functions sctp_cc_functions[]; +extern struct sctp_ss_functions sctp_ss_functions[]; void sctp_sblog(struct sockbuf *sb, @@ -1048,6 +1049,9 @@ sctp_init_asoc(struct sctp_inpcb *m, struct sctp_tcb *stcb, stcb->asoc.congestion_control_module = m->sctp_ep.sctp_default_cc_module; stcb->asoc.cc_functions = sctp_cc_functions[m->sctp_ep.sctp_default_cc_module]; + stcb->asoc.stream_scheduling_module = m->sctp_ep.sctp_default_ss_module; + stcb->asoc.ss_functions = sctp_ss_functions[m->sctp_ep.sctp_default_ss_module]; + /* * Now the stream parameters, here we allocate space for all streams * that we request by default. @@ -1075,9 +1079,10 @@ sctp_init_asoc(struct sctp_inpcb *m, struct sctp_tcb *stcb, TAILQ_INIT(&asoc->strmout[i].outqueue); asoc->strmout[i].stream_no = i; asoc->strmout[i].last_msg_incomplete = 0; - asoc->strmout[i].next_spoke.tqe_next = 0; - asoc->strmout[i].next_spoke.tqe_prev = 0; + asoc->ss_functions.sctp_ss_init_stream(&asoc->strmout[i]); } + asoc->ss_functions.sctp_ss_init(stcb, asoc, 0); + /* Now the mapping array */ asoc->mapping_array_size = SCTP_INITIAL_MAPPING_ARRAY; SCTP_MALLOC(asoc->mapping_array, uint8_t *, asoc->mapping_array_size, @@ -1100,7 +1105,6 @@ sctp_init_asoc(struct sctp_inpcb *m, struct sctp_tcb *stcb, /* Now the init of the other outqueues */ TAILQ_INIT(&asoc->free_chunks); - TAILQ_INIT(&asoc->out_wheel); TAILQ_INIT(&asoc->control_send_queue); TAILQ_INIT(&asoc->asconf_send_queue); TAILQ_INIT(&asoc->send_queue);