Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 23 Jan 2011 19:36:28 +0000 (UTC)
From:      Michael Tuexen <tuexen@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r217760 - in head/sys: conf netinet
Message-ID:  <201101231936.p0NJaSHF065284@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: tuexen
Date: Sun Jan 23 19:36:28 2011
New Revision: 217760
URL: http://svn.freebsd.org/changeset/base/217760

Log:
  Add stream scheduling support.
  This work is based on a patch received from Robin Seggelmann.
  
  MFC after: 3 months.

Added:
  head/sys/netinet/sctp_ss_functions.c   (contents, props changed)
Modified:
  head/sys/conf/files
  head/sys/netinet/sctp.h
  head/sys/netinet/sctp_input.c
  head/sys/netinet/sctp_output.c
  head/sys/netinet/sctp_output.h
  head/sys/netinet/sctp_pcb.c
  head/sys/netinet/sctp_pcb.h
  head/sys/netinet/sctp_structs.h
  head/sys/netinet/sctp_sysctl.c
  head/sys/netinet/sctp_sysctl.h
  head/sys/netinet/sctp_timer.c
  head/sys/netinet/sctp_uio.h
  head/sys/netinet/sctp_usrreq.c
  head/sys/netinet/sctputil.c

Modified: head/sys/conf/files
==============================================================================
--- head/sys/conf/files	Sun Jan 23 18:43:16 2011	(r217759)
+++ head/sys/conf/files	Sun Jan 23 19:36:28 2011	(r217760)
@@ -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

Modified: head/sys/netinet/sctp.h
==============================================================================
--- head/sys/netinet/sctp.h	Sun Jan 23 18:43:16 2011	(r217759)
+++ head/sys/netinet/sctp.h	Sun Jan 23 19:36:28 2011	(r217760)
@@ -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

Modified: head/sys/netinet/sctp_input.c
==============================================================================
--- head/sys/netinet/sctp_input.c	Sun Jan 23 18:43:16 2011	(r217759)
+++ head/sys/netinet/sctp_input.c	Sun Jan 23 19:36:28 2011	(r217760)
@@ -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_tc
 	 */
 	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_tc
 					    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_shu
 	/* 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 sct
 		/* 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);
 		}
 	}

Modified: head/sys/netinet/sctp_output.c
==============================================================================
--- head/sys/netinet/sctp_output.c	Sun Jan 23 18:43:16 2011	(r217759)
+++ head/sys/netinet/sctp_output.c	Sun Jan 23 19:36:28 2011	(r217760)
@@ -5616,52 +5616,6 @@ do_a_abort:
 }
 
 
-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 @@ one_more_time:
 			}
 			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 @@ dont_do_it:
 			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 @@ out_of:
 }
 
 
-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 
 	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 
 		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 @@ skip_preblock:
 				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);

Modified: head/sys/netinet/sctp_output.h
==============================================================================
--- head/sys/netinet/sctp_output.h	Sun Jan 23 18:43:16 2011	(r217759)
+++ head/sys/netinet/sctp_output.h	Sun Jan 23 19:36:28 2011	(r217760)
@@ -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)

Modified: head/sys/netinet/sctp_pcb.c
==============================================================================
--- head/sys/netinet/sctp_pcb.c	Sun Jan 23 18:43:16 2011	(r217759)
+++ head/sys/netinet/sctp_pcb.c	Sun Jan 23 19:36:28 2011	(r217760)
@@ -2517,6 +2517,7 @@ sctp_inpcb_alloc(struct socket *so, uint
 	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);
 

Modified: head/sys/netinet/sctp_pcb.h
==============================================================================
--- head/sys/netinet/sctp_pcb.h	Sun Jan 23 18:43:16 2011	(r217759)
+++ head/sys/netinet/sctp_pcb.h	Sun Jan 23 19:36:28 2011	(r217760)
@@ -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;

Added: head/sys/netinet/sctp_ss_functions.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/netinet/sctp_ss_functions.c	Sun Jan 23 19:36:28 2011	(r217760)
@@ -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,

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201101231936.p0NJaSHF065284>