Add stream scheduling support.
This work is based on a patch received from Robin Seggelmann. MFC after: 3 months.
This commit is contained in:
parent
a0a3479c06
commit
f7a77f6fd3
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
914
sys/netinet/sctp_ss_functions.c
Normal file
914
sys/netinet/sctp_ss_functions.c
Normal file
@ -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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <netinet/sctp_pcb.h>
|
||||
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
};
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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++;
|
||||
|
@ -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[];
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user