xref: /freebsd-src/sys/netinet/sctp_ss_functions.c (revision c3179e6660e1365111b89cb6c05c3a4c47375e73)
1f7a77f6fSMichael Tuexen /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3b7d2b5d5SMichael Tuexen  *
4807aad63SMichael Tuexen  * Copyright (c) 2010-2012, by Michael Tuexen. All rights reserved.
5807aad63SMichael Tuexen  * Copyright (c) 2010-2012, by Randall Stewart. All rights reserved.
6807aad63SMichael Tuexen  * Copyright (c) 2010-2012, by Robin Seggelmann. All rights reserved.
7f7a77f6fSMichael Tuexen  *
8f7a77f6fSMichael Tuexen  * Redistribution and use in source and binary forms, with or without
9f7a77f6fSMichael Tuexen  * modification, are permitted provided that the following conditions are met:
10f7a77f6fSMichael Tuexen  *
11f7a77f6fSMichael Tuexen  * a) Redistributions of source code must retain the above copyright notice,
12f7a77f6fSMichael Tuexen  *    this list of conditions and the following disclaimer.
13f7a77f6fSMichael Tuexen  *
14f7a77f6fSMichael Tuexen  * b) Redistributions in binary form must reproduce the above copyright
15f7a77f6fSMichael Tuexen  *    notice, this list of conditions and the following disclaimer in
16f7a77f6fSMichael Tuexen  *    the documentation and/or other materials provided with the distribution.
17f7a77f6fSMichael Tuexen  *
18f7a77f6fSMichael Tuexen  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19f7a77f6fSMichael Tuexen  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20f7a77f6fSMichael Tuexen  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21f7a77f6fSMichael Tuexen  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22f7a77f6fSMichael Tuexen  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23f7a77f6fSMichael Tuexen  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24f7a77f6fSMichael Tuexen  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25f7a77f6fSMichael Tuexen  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26f7a77f6fSMichael Tuexen  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27f7a77f6fSMichael Tuexen  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28f7a77f6fSMichael Tuexen  * THE POSSIBILITY OF SUCH DAMAGE.
29f7a77f6fSMichael Tuexen  */
30f7a77f6fSMichael Tuexen 
31*c3179e66SMichael Tuexen #include <netinet/sctp_os.h>
32f7a77f6fSMichael Tuexen #include <netinet/sctp_pcb.h>
33f7a77f6fSMichael Tuexen 
34f7a77f6fSMichael Tuexen /*
35f7a77f6fSMichael Tuexen  * Default simple round-robin algorithm.
36e7e65008SMichael Tuexen  * Just iterates the streams in the order they appear.
37f7a77f6fSMichael Tuexen  */
38f7a77f6fSMichael Tuexen 
39f7a77f6fSMichael Tuexen static void
40f7a77f6fSMichael Tuexen sctp_ss_default_add(struct sctp_tcb *, struct sctp_association *,
41f7a77f6fSMichael Tuexen     struct sctp_stream_out *,
42762ae0ecSMichael Tuexen     struct sctp_stream_queue_pending *);
43f7a77f6fSMichael Tuexen 
44f7a77f6fSMichael Tuexen static void
45f7a77f6fSMichael Tuexen sctp_ss_default_remove(struct sctp_tcb *, struct sctp_association *,
46f7a77f6fSMichael Tuexen     struct sctp_stream_out *,
47762ae0ecSMichael Tuexen     struct sctp_stream_queue_pending *);
48f7a77f6fSMichael Tuexen 
49f7a77f6fSMichael Tuexen static void
sctp_ss_default_init(struct sctp_tcb * stcb,struct sctp_association * asoc)50762ae0ecSMichael Tuexen sctp_ss_default_init(struct sctp_tcb *stcb, struct sctp_association *asoc)
51f7a77f6fSMichael Tuexen {
52f7a77f6fSMichael Tuexen 	uint16_t i;
53f7a77f6fSMichael Tuexen 
545ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
55414499b3SMichael Tuexen 
56d1ea5fa9SMichael Tuexen 	asoc->ss_data.locked_on_sending = NULL;
57d1ea5fa9SMichael Tuexen 	asoc->ss_data.last_out_stream = NULL;
58d1ea5fa9SMichael Tuexen 	TAILQ_INIT(&asoc->ss_data.out.wheel);
59252f7f93SMichael Tuexen 	/*
60252f7f93SMichael Tuexen 	 * If there is data in the stream queues already, the scheduler of
61252f7f93SMichael Tuexen 	 * an existing association has been changed. We need to add all
62252f7f93SMichael Tuexen 	 * stream queues to the wheel.
63252f7f93SMichael Tuexen 	 */
64fa947a36SMichael Tuexen 	for (i = 0; i < asoc->streamoutcnt; i++) {
65fa947a36SMichael Tuexen 		stcb->asoc.ss_functions.sctp_ss_add_to_stream(stcb, asoc,
66fa947a36SMichael Tuexen 		    &asoc->strmout[i],
67762ae0ecSMichael Tuexen 		    NULL);
68f7a77f6fSMichael Tuexen 	}
69f7a77f6fSMichael Tuexen 	return;
70f7a77f6fSMichael Tuexen }
71f7a77f6fSMichael Tuexen 
72f7a77f6fSMichael Tuexen static void
sctp_ss_default_clear(struct sctp_tcb * stcb,struct sctp_association * asoc,bool clear_values SCTP_UNUSED)73f7a77f6fSMichael Tuexen sctp_ss_default_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
74414499b3SMichael Tuexen     bool clear_values SCTP_UNUSED)
75f7a77f6fSMichael Tuexen {
765ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
77762ae0ecSMichael Tuexen 
78d1ea5fa9SMichael Tuexen 	while (!TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
79e6dcce69SMichael Tuexen 		struct sctp_stream_out *strq;
80be2a6988SMichael Tuexen 
81e6dcce69SMichael Tuexen 		strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
82fa947a36SMichael Tuexen 		KASSERT(strq->ss_params.scheduled, ("strq %p not scheduled", (void *)strq));
83414499b3SMichael Tuexen 		TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.ss.rr.next_spoke);
84414499b3SMichael Tuexen 		strq->ss_params.scheduled = false;
85be2a6988SMichael Tuexen 	}
86d1ea5fa9SMichael Tuexen 	asoc->ss_data.last_out_stream = NULL;
87f7a77f6fSMichael Tuexen 	return;
88f7a77f6fSMichael Tuexen }
89f7a77f6fSMichael Tuexen 
90f7a77f6fSMichael Tuexen static void
sctp_ss_default_init_stream(struct sctp_tcb * stcb,struct sctp_stream_out * strq,struct sctp_stream_out * with_strq)91d1ea5fa9SMichael Tuexen sctp_ss_default_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
92f7a77f6fSMichael Tuexen {
935ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
945ac91821SMichael Tuexen 
95d1ea5fa9SMichael Tuexen 	if (with_strq != NULL) {
96d1ea5fa9SMichael Tuexen 		if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
97d1ea5fa9SMichael Tuexen 			stcb->asoc.ss_data.locked_on_sending = strq;
98d1ea5fa9SMichael Tuexen 		}
99d1ea5fa9SMichael Tuexen 		if (stcb->asoc.ss_data.last_out_stream == with_strq) {
100d1ea5fa9SMichael Tuexen 			stcb->asoc.ss_data.last_out_stream = strq;
101d1ea5fa9SMichael Tuexen 		}
102d1ea5fa9SMichael Tuexen 	}
103414499b3SMichael Tuexen 	strq->ss_params.scheduled = false;
104f7a77f6fSMichael Tuexen 	return;
105f7a77f6fSMichael Tuexen }
106f7a77f6fSMichael Tuexen 
107f7a77f6fSMichael Tuexen 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 SCTP_UNUSED)108f7a77f6fSMichael Tuexen sctp_ss_default_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
109f7a77f6fSMichael Tuexen     struct sctp_stream_out *strq,
110762ae0ecSMichael Tuexen     struct sctp_stream_queue_pending *sp SCTP_UNUSED)
111f7a77f6fSMichael Tuexen {
1125ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
113762ae0ecSMichael Tuexen 
114be2a6988SMichael Tuexen 	/* Add to wheel if not already on it and stream queue not empty */
115414499b3SMichael Tuexen 	if (!TAILQ_EMPTY(&strq->outqueue) && !strq->ss_params.scheduled) {
116d1ea5fa9SMichael Tuexen 		TAILQ_INSERT_TAIL(&asoc->ss_data.out.wheel,
117414499b3SMichael Tuexen 		    strq, ss_params.ss.rr.next_spoke);
118414499b3SMichael Tuexen 		strq->ss_params.scheduled = true;
119f7a77f6fSMichael Tuexen 	}
120f7a77f6fSMichael Tuexen 	return;
121f7a77f6fSMichael Tuexen }
122f7a77f6fSMichael Tuexen 
123414499b3SMichael Tuexen static bool
sctp_ss_default_is_empty(struct sctp_tcb * stcb SCTP_UNUSED,struct sctp_association * asoc)1247215cc1bSMichael Tuexen sctp_ss_default_is_empty(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc)
125f7a77f6fSMichael Tuexen {
1265ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
1275ac91821SMichael Tuexen 
128414499b3SMichael Tuexen 	return (TAILQ_EMPTY(&asoc->ss_data.out.wheel));
129f7a77f6fSMichael Tuexen }
130f7a77f6fSMichael Tuexen 
131f7a77f6fSMichael Tuexen 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 SCTP_UNUSED)132f7a77f6fSMichael Tuexen sctp_ss_default_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
133f7a77f6fSMichael Tuexen     struct sctp_stream_out *strq,
134762ae0ecSMichael Tuexen     struct sctp_stream_queue_pending *sp SCTP_UNUSED)
135f7a77f6fSMichael Tuexen {
1365ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
137762ae0ecSMichael Tuexen 
138b7b84c0eSMichael Tuexen 	/*
139b7b84c0eSMichael Tuexen 	 * Remove from wheel if stream queue is empty and actually is on the
140b7b84c0eSMichael Tuexen 	 * wheel
141b7b84c0eSMichael Tuexen 	 */
142414499b3SMichael Tuexen 	if (TAILQ_EMPTY(&strq->outqueue) && strq->ss_params.scheduled) {
143d1ea5fa9SMichael Tuexen 		if (asoc->ss_data.last_out_stream == strq) {
144d1ea5fa9SMichael Tuexen 			asoc->ss_data.last_out_stream = TAILQ_PREV(asoc->ss_data.last_out_stream,
145f7a77f6fSMichael Tuexen 			    sctpwheel_listhead,
146414499b3SMichael Tuexen 			    ss_params.ss.rr.next_spoke);
147d1ea5fa9SMichael Tuexen 			if (asoc->ss_data.last_out_stream == NULL) {
148d1ea5fa9SMichael Tuexen 				asoc->ss_data.last_out_stream = TAILQ_LAST(&asoc->ss_data.out.wheel,
149f7a77f6fSMichael Tuexen 				    sctpwheel_listhead);
150f7a77f6fSMichael Tuexen 			}
151d1ea5fa9SMichael Tuexen 			if (asoc->ss_data.last_out_stream == strq) {
152d1ea5fa9SMichael Tuexen 				asoc->ss_data.last_out_stream = NULL;
153f7a77f6fSMichael Tuexen 			}
154f7a77f6fSMichael Tuexen 		}
1553ff37339SMichael Tuexen 		if (asoc->ss_data.locked_on_sending == strq) {
1563ff37339SMichael Tuexen 			asoc->ss_data.locked_on_sending = NULL;
1573ff37339SMichael Tuexen 		}
158414499b3SMichael Tuexen 		TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.ss.rr.next_spoke);
159414499b3SMichael Tuexen 		strq->ss_params.scheduled = false;
160f7a77f6fSMichael Tuexen 	}
161f7a77f6fSMichael Tuexen 	return;
162f7a77f6fSMichael Tuexen }
163f7a77f6fSMichael Tuexen 
164f7a77f6fSMichael Tuexen static struct sctp_stream_out *
sctp_ss_default_select(struct sctp_tcb * stcb SCTP_UNUSED,struct sctp_nets * net,struct sctp_association * asoc)1657215cc1bSMichael Tuexen sctp_ss_default_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
166f7a77f6fSMichael Tuexen     struct sctp_association *asoc)
167f7a77f6fSMichael Tuexen {
168f7a77f6fSMichael Tuexen 	struct sctp_stream_out *strq, *strqt;
169f7a77f6fSMichael Tuexen 
1705ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
1715ac91821SMichael Tuexen 
172bd19202cSMichael Tuexen 	if (asoc->ss_data.locked_on_sending != NULL) {
173fa947a36SMichael Tuexen 		KASSERT(asoc->ss_data.locked_on_sending->ss_params.scheduled,
174bd19202cSMichael Tuexen 		    ("locked_on_sending %p not scheduled",
175fa947a36SMichael Tuexen 		    (void *)asoc->ss_data.locked_on_sending));
176d1ea5fa9SMichael Tuexen 		return (asoc->ss_data.locked_on_sending);
177d1ea5fa9SMichael Tuexen 	}
178d1ea5fa9SMichael Tuexen 	strqt = asoc->ss_data.last_out_stream;
179bd19202cSMichael Tuexen 	KASSERT(strqt == NULL || strqt->ss_params.scheduled,
180bd19202cSMichael Tuexen 	    ("last_out_stream %p not scheduled", (void *)strqt));
181f7a77f6fSMichael Tuexen default_again:
182f7a77f6fSMichael Tuexen 	/* Find the next stream to use */
183f7a77f6fSMichael Tuexen 	if (strqt == NULL) {
184d1ea5fa9SMichael Tuexen 		strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
185f7a77f6fSMichael Tuexen 	} else {
186414499b3SMichael Tuexen 		strq = TAILQ_NEXT(strqt, ss_params.ss.rr.next_spoke);
187f7a77f6fSMichael Tuexen 		if (strq == NULL) {
188d1ea5fa9SMichael Tuexen 			strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
189f7a77f6fSMichael Tuexen 		}
190f7a77f6fSMichael Tuexen 	}
191fa947a36SMichael Tuexen 	KASSERT(strq == NULL || strq->ss_params.scheduled,
192fa947a36SMichael Tuexen 	    ("strq %p not scheduled", (void *)strq));
193f7a77f6fSMichael Tuexen 
194f7a77f6fSMichael Tuexen 	/*
195f7a77f6fSMichael Tuexen 	 * If CMT is off, we must validate that the stream in question has
1965d40cf5dSRandall Stewart 	 * the first item pointed towards are network destination requested
197f7a77f6fSMichael Tuexen 	 * by the caller. Note that if we turn out to be locked to a stream
198f7a77f6fSMichael Tuexen 	 * (assigning TSN's then we must stop, since we cannot look for
199f7a77f6fSMichael Tuexen 	 * another stream with data to send to that destination). In CMT's
200f7a77f6fSMichael Tuexen 	 * case, by skipping this check, we will send one data packet
201f7a77f6fSMichael Tuexen 	 * towards the requested net.
202f7a77f6fSMichael Tuexen 	 */
203f7a77f6fSMichael Tuexen 	if (net != NULL && strq != NULL &&
204f7a77f6fSMichael Tuexen 	    SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
205f7a77f6fSMichael Tuexen 		if (TAILQ_FIRST(&strq->outqueue) &&
206f7a77f6fSMichael Tuexen 		    TAILQ_FIRST(&strq->outqueue)->net != NULL &&
207f7a77f6fSMichael Tuexen 		    TAILQ_FIRST(&strq->outqueue)->net != net) {
208d1ea5fa9SMichael Tuexen 			if (strq == asoc->ss_data.last_out_stream) {
209f7a77f6fSMichael Tuexen 				return (NULL);
210f7a77f6fSMichael Tuexen 			} else {
211f7a77f6fSMichael Tuexen 				strqt = strq;
212f7a77f6fSMichael Tuexen 				goto default_again;
213f7a77f6fSMichael Tuexen 			}
214f7a77f6fSMichael Tuexen 		}
215f7a77f6fSMichael Tuexen 	}
216f7a77f6fSMichael Tuexen 	return (strq);
217f7a77f6fSMichael Tuexen }
218f7a77f6fSMichael Tuexen 
219f7a77f6fSMichael Tuexen static void
sctp_ss_default_scheduled(struct sctp_tcb * stcb,struct sctp_nets * net SCTP_UNUSED,struct sctp_association * asoc,struct sctp_stream_out * strq,int moved_how_much SCTP_UNUSED)220bfe7e932SMichael Tuexen sctp_ss_default_scheduled(struct sctp_tcb *stcb,
221bfe7e932SMichael Tuexen     struct sctp_nets *net SCTP_UNUSED,
222d1ea5fa9SMichael Tuexen     struct sctp_association *asoc,
223bfe7e932SMichael Tuexen     struct sctp_stream_out *strq,
224bfe7e932SMichael Tuexen     int moved_how_much SCTP_UNUSED)
225f7a77f6fSMichael Tuexen {
226d1ea5fa9SMichael Tuexen 	struct sctp_stream_queue_pending *sp;
227d1ea5fa9SMichael Tuexen 
228fa947a36SMichael Tuexen 	KASSERT(strq != NULL, ("strq is NULL"));
229fa947a36SMichael Tuexen 	KASSERT(strq->ss_params.scheduled, ("strq %p is not scheduled", (void *)strq));
2305ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
2315ac91821SMichael Tuexen 
232d1ea5fa9SMichael Tuexen 	asoc->ss_data.last_out_stream = strq;
233fa947a36SMichael Tuexen 	if (asoc->idata_supported == 0) {
234d1ea5fa9SMichael Tuexen 		sp = TAILQ_FIRST(&strq->outqueue);
235d1ea5fa9SMichael Tuexen 		if ((sp != NULL) && (sp->some_taken == 1)) {
236fa947a36SMichael Tuexen 			asoc->ss_data.locked_on_sending = strq;
237d1ea5fa9SMichael Tuexen 		} else {
238fa947a36SMichael Tuexen 			asoc->ss_data.locked_on_sending = NULL;
239d1ea5fa9SMichael Tuexen 		}
240d1ea5fa9SMichael Tuexen 	} else {
241fa947a36SMichael Tuexen 		asoc->ss_data.locked_on_sending = NULL;
242d1ea5fa9SMichael Tuexen 	}
243f7a77f6fSMichael Tuexen 	return;
244f7a77f6fSMichael Tuexen }
245f7a77f6fSMichael Tuexen 
246f7a77f6fSMichael Tuexen static void
sctp_ss_default_packet_done(struct sctp_tcb * stcb SCTP_UNUSED,struct sctp_nets * net SCTP_UNUSED,struct sctp_association * asoc SCTP_UNUSED)2477215cc1bSMichael Tuexen sctp_ss_default_packet_done(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net SCTP_UNUSED,
2487215cc1bSMichael Tuexen     struct sctp_association *asoc SCTP_UNUSED)
249f7a77f6fSMichael Tuexen {
2505ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
2515ac91821SMichael Tuexen 
252f7a77f6fSMichael Tuexen 	/* Nothing to be done here */
253f7a77f6fSMichael Tuexen 	return;
254f7a77f6fSMichael Tuexen }
255f7a77f6fSMichael Tuexen 
256f7a77f6fSMichael Tuexen static int
sctp_ss_default_get_value(struct sctp_tcb * stcb SCTP_UNUSED,struct sctp_association * asoc SCTP_UNUSED,struct sctp_stream_out * strq SCTP_UNUSED,uint16_t * value SCTP_UNUSED)2577215cc1bSMichael Tuexen sctp_ss_default_get_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED,
2587215cc1bSMichael Tuexen     struct sctp_stream_out *strq SCTP_UNUSED, uint16_t *value SCTP_UNUSED)
259f7a77f6fSMichael Tuexen {
2605ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
2615ac91821SMichael Tuexen 
262f7a77f6fSMichael Tuexen 	/* Nothing to be done here */
263f7a77f6fSMichael Tuexen 	return (-1);
264f7a77f6fSMichael Tuexen }
265f7a77f6fSMichael Tuexen 
266f7a77f6fSMichael Tuexen static int
sctp_ss_default_set_value(struct sctp_tcb * stcb SCTP_UNUSED,struct sctp_association * asoc SCTP_UNUSED,struct sctp_stream_out * strq SCTP_UNUSED,uint16_t value SCTP_UNUSED)2677215cc1bSMichael Tuexen sctp_ss_default_set_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED,
2687215cc1bSMichael Tuexen     struct sctp_stream_out *strq SCTP_UNUSED, uint16_t value SCTP_UNUSED)
269f7a77f6fSMichael Tuexen {
2705ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
2715ac91821SMichael Tuexen 
272f7a77f6fSMichael Tuexen 	/* Nothing to be done here */
273f7a77f6fSMichael Tuexen 	return (-1);
274f7a77f6fSMichael Tuexen }
275f7a77f6fSMichael Tuexen 
276414499b3SMichael Tuexen static bool
sctp_ss_default_is_user_msgs_incomplete(struct sctp_tcb * stcb SCTP_UNUSED,struct sctp_association * asoc)277bbc9dfbcSMichael Tuexen sctp_ss_default_is_user_msgs_incomplete(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc)
278d1ea5fa9SMichael Tuexen {
279bbc9dfbcSMichael Tuexen 	struct sctp_stream_out *strq;
280bbc9dfbcSMichael Tuexen 	struct sctp_stream_queue_pending *sp;
281bbc9dfbcSMichael Tuexen 
2825ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
2835ac91821SMichael Tuexen 
284bbc9dfbcSMichael Tuexen 	if (asoc->stream_queue_cnt != 1) {
285414499b3SMichael Tuexen 		return (false);
286d1ea5fa9SMichael Tuexen 	}
287bbc9dfbcSMichael Tuexen 	strq = asoc->ss_data.locked_on_sending;
288bbc9dfbcSMichael Tuexen 	if (strq == NULL) {
289414499b3SMichael Tuexen 		return (false);
290bbc9dfbcSMichael Tuexen 	}
291bbc9dfbcSMichael Tuexen 	sp = TAILQ_FIRST(&strq->outqueue);
292bbc9dfbcSMichael Tuexen 	if (sp == NULL) {
293414499b3SMichael Tuexen 		return (false);
294bbc9dfbcSMichael Tuexen 	}
295414499b3SMichael Tuexen 	return (sp->msg_is_complete == 0);
296bbc9dfbcSMichael Tuexen }
297d1ea5fa9SMichael Tuexen 
298f7a77f6fSMichael Tuexen /*
299f7a77f6fSMichael Tuexen  * Real round-robin algorithm.
300e7e65008SMichael Tuexen  * Always iterates the streams in ascending order.
301f7a77f6fSMichael Tuexen  */
302f7a77f6fSMichael Tuexen 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 SCTP_UNUSED)303f7a77f6fSMichael Tuexen sctp_ss_rr_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
304f7a77f6fSMichael Tuexen     struct sctp_stream_out *strq,
305762ae0ecSMichael Tuexen     struct sctp_stream_queue_pending *sp SCTP_UNUSED)
306f7a77f6fSMichael Tuexen {
307f7a77f6fSMichael Tuexen 	struct sctp_stream_out *strqt;
308f7a77f6fSMichael Tuexen 
3095ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
310762ae0ecSMichael Tuexen 
311414499b3SMichael Tuexen 	if (!TAILQ_EMPTY(&strq->outqueue) && !strq->ss_params.scheduled) {
312d1ea5fa9SMichael Tuexen 		if (TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
313414499b3SMichael Tuexen 			TAILQ_INSERT_HEAD(&asoc->ss_data.out.wheel, strq, ss_params.ss.rr.next_spoke);
314f7a77f6fSMichael Tuexen 		} else {
315d1ea5fa9SMichael Tuexen 			strqt = TAILQ_FIRST(&asoc->ss_data.out.wheel);
31649656eefSMichael Tuexen 			while (strqt != NULL && (strqt->sid < strq->sid)) {
317414499b3SMichael Tuexen 				strqt = TAILQ_NEXT(strqt, ss_params.ss.rr.next_spoke);
318f7a77f6fSMichael Tuexen 			}
319f7a77f6fSMichael Tuexen 			if (strqt != NULL) {
320414499b3SMichael Tuexen 				TAILQ_INSERT_BEFORE(strqt, strq, ss_params.ss.rr.next_spoke);
321f7a77f6fSMichael Tuexen 			} else {
322414499b3SMichael Tuexen 				TAILQ_INSERT_TAIL(&asoc->ss_data.out.wheel, strq, ss_params.ss.rr.next_spoke);
323f7a77f6fSMichael Tuexen 			}
324f7a77f6fSMichael Tuexen 		}
325414499b3SMichael Tuexen 		strq->ss_params.scheduled = true;
326f7a77f6fSMichael Tuexen 	}
327f7a77f6fSMichael Tuexen 	return;
328f7a77f6fSMichael Tuexen }
329f7a77f6fSMichael Tuexen 
330f7a77f6fSMichael Tuexen /*
331f7a77f6fSMichael Tuexen  * Real round-robin per packet algorithm.
332e7e65008SMichael Tuexen  * Always iterates the streams in ascending order and
333f7a77f6fSMichael Tuexen  * only fills messages of the same stream in a packet.
334f7a77f6fSMichael Tuexen  */
335f7a77f6fSMichael Tuexen static struct sctp_stream_out *
sctp_ss_rrp_select(struct sctp_tcb * stcb SCTP_UNUSED,struct sctp_nets * net SCTP_UNUSED,struct sctp_association * asoc)3367215cc1bSMichael Tuexen sctp_ss_rrp_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net SCTP_UNUSED,
337f7a77f6fSMichael Tuexen     struct sctp_association *asoc)
338f7a77f6fSMichael Tuexen {
3395ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
3405ac91821SMichael Tuexen 
341d1ea5fa9SMichael Tuexen 	return (asoc->ss_data.last_out_stream);
342be2a6988SMichael Tuexen }
343be2a6988SMichael Tuexen 
344be2a6988SMichael Tuexen static void
sctp_ss_rrp_packet_done(struct sctp_tcb * stcb SCTP_UNUSED,struct sctp_nets * net,struct sctp_association * asoc)3457215cc1bSMichael Tuexen sctp_ss_rrp_packet_done(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
346be2a6988SMichael Tuexen     struct sctp_association *asoc)
347be2a6988SMichael Tuexen {
348f7a77f6fSMichael Tuexen 	struct sctp_stream_out *strq, *strqt;
349f7a77f6fSMichael Tuexen 
3505ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
3515ac91821SMichael Tuexen 
352d1ea5fa9SMichael Tuexen 	strqt = asoc->ss_data.last_out_stream;
353bd19202cSMichael Tuexen 	KASSERT(strqt == NULL || strqt->ss_params.scheduled,
354bd19202cSMichael Tuexen 	    ("last_out_stream %p not scheduled", (void *)strqt));
355f7a77f6fSMichael Tuexen rrp_again:
356f7a77f6fSMichael Tuexen 	/* Find the next stream to use */
357f7a77f6fSMichael Tuexen 	if (strqt == NULL) {
358d1ea5fa9SMichael Tuexen 		strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
359f7a77f6fSMichael Tuexen 	} else {
360414499b3SMichael Tuexen 		strq = TAILQ_NEXT(strqt, ss_params.ss.rr.next_spoke);
361f7a77f6fSMichael Tuexen 		if (strq == NULL) {
362d1ea5fa9SMichael Tuexen 			strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
363f7a77f6fSMichael Tuexen 		}
364f7a77f6fSMichael Tuexen 	}
365fa947a36SMichael Tuexen 	KASSERT(strq == NULL || strq->ss_params.scheduled,
366fa947a36SMichael Tuexen 	    ("strq %p not scheduled", (void *)strq));
367f7a77f6fSMichael Tuexen 
368f7a77f6fSMichael Tuexen 	/*
369f7a77f6fSMichael Tuexen 	 * If CMT is off, we must validate that the stream in question has
3705d40cf5dSRandall Stewart 	 * the first item pointed towards are network destination requested
371f7a77f6fSMichael Tuexen 	 * by the caller. Note that if we turn out to be locked to a stream
372f7a77f6fSMichael Tuexen 	 * (assigning TSN's then we must stop, since we cannot look for
373f7a77f6fSMichael Tuexen 	 * another stream with data to send to that destination). In CMT's
374f7a77f6fSMichael Tuexen 	 * case, by skipping this check, we will send one data packet
375f7a77f6fSMichael Tuexen 	 * towards the requested net.
376f7a77f6fSMichael Tuexen 	 */
377f7a77f6fSMichael Tuexen 	if (net != NULL && strq != NULL &&
378f7a77f6fSMichael Tuexen 	    SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
379f7a77f6fSMichael Tuexen 		if (TAILQ_FIRST(&strq->outqueue) &&
380f7a77f6fSMichael Tuexen 		    TAILQ_FIRST(&strq->outqueue)->net != NULL &&
381f7a77f6fSMichael Tuexen 		    TAILQ_FIRST(&strq->outqueue)->net != net) {
382d1ea5fa9SMichael Tuexen 			if (strq == asoc->ss_data.last_out_stream) {
383be2a6988SMichael Tuexen 				strq = NULL;
384f7a77f6fSMichael Tuexen 			} else {
385f7a77f6fSMichael Tuexen 				strqt = strq;
386f7a77f6fSMichael Tuexen 				goto rrp_again;
387f7a77f6fSMichael Tuexen 			}
388f7a77f6fSMichael Tuexen 		}
389f7a77f6fSMichael Tuexen 	}
390d1ea5fa9SMichael Tuexen 	asoc->ss_data.last_out_stream = strq;
391f7a77f6fSMichael Tuexen 	return;
392f7a77f6fSMichael Tuexen }
393f7a77f6fSMichael Tuexen 
394f7a77f6fSMichael Tuexen /*
395f7a77f6fSMichael Tuexen  * Priority algorithm.
396f7a77f6fSMichael Tuexen  * Always prefers streams based on their priority id.
397f7a77f6fSMichael Tuexen  */
398f7a77f6fSMichael Tuexen static void
sctp_ss_prio_clear(struct sctp_tcb * stcb,struct sctp_association * asoc,bool clear_values)399f7a77f6fSMichael Tuexen sctp_ss_prio_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
400414499b3SMichael Tuexen     bool clear_values)
401f7a77f6fSMichael Tuexen {
4025ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
403762ae0ecSMichael Tuexen 
404d1ea5fa9SMichael Tuexen 	while (!TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
405a458a6e6SMichael Tuexen 		struct sctp_stream_out *strq;
406be2a6988SMichael Tuexen 
407a458a6e6SMichael Tuexen 		strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
408fa947a36SMichael Tuexen 		KASSERT(strq->ss_params.scheduled, ("strq %p not scheduled", (void *)strq));
409be2a6988SMichael Tuexen 		if (clear_values) {
410414499b3SMichael Tuexen 			strq->ss_params.ss.prio.priority = 0;
411be2a6988SMichael Tuexen 		}
412414499b3SMichael Tuexen 		TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.ss.prio.next_spoke);
413414499b3SMichael Tuexen 		strq->ss_params.scheduled = false;
414be2a6988SMichael Tuexen 	}
415d1ea5fa9SMichael Tuexen 	asoc->ss_data.last_out_stream = NULL;
416f7a77f6fSMichael Tuexen 	return;
417f7a77f6fSMichael Tuexen }
418f7a77f6fSMichael Tuexen 
419f7a77f6fSMichael Tuexen static void
sctp_ss_prio_init_stream(struct sctp_tcb * stcb,struct sctp_stream_out * strq,struct sctp_stream_out * with_strq)420d1ea5fa9SMichael Tuexen sctp_ss_prio_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
421f7a77f6fSMichael Tuexen {
4225ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
4235ac91821SMichael Tuexen 
424d1ea5fa9SMichael Tuexen 	if (with_strq != NULL) {
425d1ea5fa9SMichael Tuexen 		if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
426d1ea5fa9SMichael Tuexen 			stcb->asoc.ss_data.locked_on_sending = strq;
427d1ea5fa9SMichael Tuexen 		}
428d1ea5fa9SMichael Tuexen 		if (stcb->asoc.ss_data.last_out_stream == with_strq) {
429d1ea5fa9SMichael Tuexen 			stcb->asoc.ss_data.last_out_stream = strq;
430d1ea5fa9SMichael Tuexen 		}
431d1ea5fa9SMichael Tuexen 	}
432414499b3SMichael Tuexen 	strq->ss_params.scheduled = false;
433252f7f93SMichael Tuexen 	if (with_strq != NULL) {
434414499b3SMichael Tuexen 		strq->ss_params.ss.prio.priority = with_strq->ss_params.ss.prio.priority;
435252f7f93SMichael Tuexen 	} else {
436414499b3SMichael Tuexen 		strq->ss_params.ss.prio.priority = 0;
437252f7f93SMichael Tuexen 	}
438f7a77f6fSMichael Tuexen 	return;
439f7a77f6fSMichael Tuexen }
440f7a77f6fSMichael Tuexen 
441f7a77f6fSMichael Tuexen 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 SCTP_UNUSED)442f7a77f6fSMichael Tuexen sctp_ss_prio_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
443762ae0ecSMichael Tuexen     struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED)
444f7a77f6fSMichael Tuexen {
445f7a77f6fSMichael Tuexen 	struct sctp_stream_out *strqt;
446f7a77f6fSMichael Tuexen 
4475ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
448762ae0ecSMichael Tuexen 
449be2a6988SMichael Tuexen 	/* Add to wheel if not already on it and stream queue not empty */
450414499b3SMichael Tuexen 	if (!TAILQ_EMPTY(&strq->outqueue) && !strq->ss_params.scheduled) {
451d1ea5fa9SMichael Tuexen 		if (TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
452414499b3SMichael Tuexen 			TAILQ_INSERT_HEAD(&asoc->ss_data.out.wheel, strq, ss_params.ss.prio.next_spoke);
453f7a77f6fSMichael Tuexen 		} else {
454d1ea5fa9SMichael Tuexen 			strqt = TAILQ_FIRST(&asoc->ss_data.out.wheel);
455414499b3SMichael Tuexen 			while (strqt != NULL && strqt->ss_params.ss.prio.priority < strq->ss_params.ss.prio.priority) {
456414499b3SMichael Tuexen 				strqt = TAILQ_NEXT(strqt, ss_params.ss.prio.next_spoke);
457f7a77f6fSMichael Tuexen 			}
458f7a77f6fSMichael Tuexen 			if (strqt != NULL) {
459414499b3SMichael Tuexen 				TAILQ_INSERT_BEFORE(strqt, strq, ss_params.ss.prio.next_spoke);
460f7a77f6fSMichael Tuexen 			} else {
461414499b3SMichael Tuexen 				TAILQ_INSERT_TAIL(&asoc->ss_data.out.wheel, strq, ss_params.ss.prio.next_spoke);
462f7a77f6fSMichael Tuexen 			}
463f7a77f6fSMichael Tuexen 		}
464414499b3SMichael Tuexen 		strq->ss_params.scheduled = true;
465f7a77f6fSMichael Tuexen 	}
466f7a77f6fSMichael Tuexen 	return;
467f7a77f6fSMichael Tuexen }
468f7a77f6fSMichael Tuexen 
469f7a77f6fSMichael Tuexen 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 SCTP_UNUSED)470f7a77f6fSMichael Tuexen sctp_ss_prio_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
471762ae0ecSMichael Tuexen     struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED)
472f7a77f6fSMichael Tuexen {
4735ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
474762ae0ecSMichael Tuexen 
475b7b84c0eSMichael Tuexen 	/*
476b7b84c0eSMichael Tuexen 	 * Remove from wheel if stream queue is empty and actually is on the
477b7b84c0eSMichael Tuexen 	 * wheel
478b7b84c0eSMichael Tuexen 	 */
479414499b3SMichael Tuexen 	if (TAILQ_EMPTY(&strq->outqueue) && strq->ss_params.scheduled) {
480d1ea5fa9SMichael Tuexen 		if (asoc->ss_data.last_out_stream == strq) {
481414499b3SMichael Tuexen 			asoc->ss_data.last_out_stream = TAILQ_PREV(asoc->ss_data.last_out_stream,
482414499b3SMichael Tuexen 			    sctpwheel_listhead,
483414499b3SMichael Tuexen 			    ss_params.ss.prio.next_spoke);
484d1ea5fa9SMichael Tuexen 			if (asoc->ss_data.last_out_stream == NULL) {
485d1ea5fa9SMichael Tuexen 				asoc->ss_data.last_out_stream = TAILQ_LAST(&asoc->ss_data.out.wheel,
486f7a77f6fSMichael Tuexen 				    sctpwheel_listhead);
487f7a77f6fSMichael Tuexen 			}
488d1ea5fa9SMichael Tuexen 			if (asoc->ss_data.last_out_stream == strq) {
489d1ea5fa9SMichael Tuexen 				asoc->ss_data.last_out_stream = NULL;
490f7a77f6fSMichael Tuexen 			}
491f7a77f6fSMichael Tuexen 		}
4923ff37339SMichael Tuexen 		if (asoc->ss_data.locked_on_sending == strq) {
4933ff37339SMichael Tuexen 			asoc->ss_data.locked_on_sending = NULL;
4943ff37339SMichael Tuexen 		}
495414499b3SMichael Tuexen 		TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.ss.prio.next_spoke);
496414499b3SMichael Tuexen 		strq->ss_params.scheduled = false;
497f7a77f6fSMichael Tuexen 	}
498f7a77f6fSMichael Tuexen 	return;
499f7a77f6fSMichael Tuexen }
500f7a77f6fSMichael Tuexen 
501f7a77f6fSMichael Tuexen static struct sctp_stream_out *
sctp_ss_prio_select(struct sctp_tcb * stcb SCTP_UNUSED,struct sctp_nets * net,struct sctp_association * asoc)5027215cc1bSMichael Tuexen sctp_ss_prio_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
503f7a77f6fSMichael Tuexen     struct sctp_association *asoc)
504f7a77f6fSMichael Tuexen {
505f7a77f6fSMichael Tuexen 	struct sctp_stream_out *strq, *strqt, *strqn;
506f7a77f6fSMichael Tuexen 
5075ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
5085ac91821SMichael Tuexen 
509bd19202cSMichael Tuexen 	if (asoc->ss_data.locked_on_sending != NULL) {
510fa947a36SMichael Tuexen 		KASSERT(asoc->ss_data.locked_on_sending->ss_params.scheduled,
511bd19202cSMichael Tuexen 		    ("locked_on_sending %p not scheduled",
512fa947a36SMichael Tuexen 		    (void *)asoc->ss_data.locked_on_sending));
513a610bb21SMichael Tuexen 		return (asoc->ss_data.locked_on_sending);
514a610bb21SMichael Tuexen 	}
515d1ea5fa9SMichael Tuexen 	strqt = asoc->ss_data.last_out_stream;
516bd19202cSMichael Tuexen 	KASSERT(strqt == NULL || strqt->ss_params.scheduled,
517bd19202cSMichael Tuexen 	    ("last_out_stream %p not scheduled", (void *)strqt));
518f7a77f6fSMichael Tuexen prio_again:
519f7a77f6fSMichael Tuexen 	/* Find the next stream to use */
520f7a77f6fSMichael Tuexen 	if (strqt == NULL) {
521d1ea5fa9SMichael Tuexen 		strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
522f7a77f6fSMichael Tuexen 	} else {
523414499b3SMichael Tuexen 		strqn = TAILQ_NEXT(strqt, ss_params.ss.prio.next_spoke);
524f7a77f6fSMichael Tuexen 		if (strqn != NULL &&
525414499b3SMichael Tuexen 		    strqn->ss_params.ss.prio.priority == strqt->ss_params.ss.prio.priority) {
526be2a6988SMichael Tuexen 			strq = strqn;
527f7a77f6fSMichael Tuexen 		} else {
528d1ea5fa9SMichael Tuexen 			strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
529f7a77f6fSMichael Tuexen 		}
530f7a77f6fSMichael Tuexen 	}
531fa947a36SMichael Tuexen 	KASSERT(strq == NULL || strq->ss_params.scheduled,
532fa947a36SMichael Tuexen 	    ("strq %p not scheduled", (void *)strq));
533f7a77f6fSMichael Tuexen 
534f7a77f6fSMichael Tuexen 	/*
535f7a77f6fSMichael Tuexen 	 * If CMT is off, we must validate that the stream in question has
5365d40cf5dSRandall Stewart 	 * the first item pointed towards are network destination requested
537f7a77f6fSMichael Tuexen 	 * by the caller. Note that if we turn out to be locked to a stream
538f7a77f6fSMichael Tuexen 	 * (assigning TSN's then we must stop, since we cannot look for
539f7a77f6fSMichael Tuexen 	 * another stream with data to send to that destination). In CMT's
540f7a77f6fSMichael Tuexen 	 * case, by skipping this check, we will send one data packet
541f7a77f6fSMichael Tuexen 	 * towards the requested net.
542f7a77f6fSMichael Tuexen 	 */
543f7a77f6fSMichael Tuexen 	if (net != NULL && strq != NULL &&
544f7a77f6fSMichael Tuexen 	    SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
545f7a77f6fSMichael Tuexen 		if (TAILQ_FIRST(&strq->outqueue) &&
546f7a77f6fSMichael Tuexen 		    TAILQ_FIRST(&strq->outqueue)->net != NULL &&
547f7a77f6fSMichael Tuexen 		    TAILQ_FIRST(&strq->outqueue)->net != net) {
548d1ea5fa9SMichael Tuexen 			if (strq == asoc->ss_data.last_out_stream) {
549f7a77f6fSMichael Tuexen 				return (NULL);
550f7a77f6fSMichael Tuexen 			} else {
551f7a77f6fSMichael Tuexen 				strqt = strq;
552f7a77f6fSMichael Tuexen 				goto prio_again;
553f7a77f6fSMichael Tuexen 			}
554f7a77f6fSMichael Tuexen 		}
555f7a77f6fSMichael Tuexen 	}
556f7a77f6fSMichael Tuexen 	return (strq);
557f7a77f6fSMichael Tuexen }
558f7a77f6fSMichael Tuexen 
559f7a77f6fSMichael Tuexen static int
sctp_ss_prio_get_value(struct sctp_tcb * stcb SCTP_UNUSED,struct sctp_association * asoc SCTP_UNUSED,struct sctp_stream_out * strq,uint16_t * value)5607215cc1bSMichael Tuexen sctp_ss_prio_get_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED,
561f7a77f6fSMichael Tuexen     struct sctp_stream_out *strq, uint16_t *value)
562f7a77f6fSMichael Tuexen {
5635ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
5645ac91821SMichael Tuexen 
565f7a77f6fSMichael Tuexen 	if (strq == NULL) {
566f7a77f6fSMichael Tuexen 		return (-1);
567f7a77f6fSMichael Tuexen 	}
568414499b3SMichael Tuexen 	*value = strq->ss_params.ss.prio.priority;
569f7a77f6fSMichael Tuexen 	return (1);
570f7a77f6fSMichael Tuexen }
571f7a77f6fSMichael Tuexen 
572f7a77f6fSMichael Tuexen static int
sctp_ss_prio_set_value(struct sctp_tcb * stcb,struct sctp_association * asoc,struct sctp_stream_out * strq,uint16_t value)573f7a77f6fSMichael Tuexen sctp_ss_prio_set_value(struct sctp_tcb *stcb, struct sctp_association *asoc,
574f7a77f6fSMichael Tuexen     struct sctp_stream_out *strq, uint16_t value)
575f7a77f6fSMichael Tuexen {
5765ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
5775ac91821SMichael Tuexen 
578f7a77f6fSMichael Tuexen 	if (strq == NULL) {
579f7a77f6fSMichael Tuexen 		return (-1);
580f7a77f6fSMichael Tuexen 	}
581414499b3SMichael Tuexen 	strq->ss_params.ss.prio.priority = value;
582762ae0ecSMichael Tuexen 	sctp_ss_prio_remove(stcb, asoc, strq, NULL);
583762ae0ecSMichael Tuexen 	sctp_ss_prio_add(stcb, asoc, strq, NULL);
584f7a77f6fSMichael Tuexen 	return (1);
585f7a77f6fSMichael Tuexen }
586f7a77f6fSMichael Tuexen 
587f7a77f6fSMichael Tuexen /*
588f7a77f6fSMichael Tuexen  * Fair bandwidth algorithm.
589183502d1SGordon Bergling  * Maintains an equal throughput per stream.
590f7a77f6fSMichael Tuexen  */
591f7a77f6fSMichael Tuexen static void
sctp_ss_fb_clear(struct sctp_tcb * stcb,struct sctp_association * asoc,bool clear_values)592f7a77f6fSMichael Tuexen sctp_ss_fb_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
593414499b3SMichael Tuexen     bool clear_values)
594f7a77f6fSMichael Tuexen {
5955ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
596762ae0ecSMichael Tuexen 
597d1ea5fa9SMichael Tuexen 	while (!TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
598a458a6e6SMichael Tuexen 		struct sctp_stream_out *strq;
599f7a77f6fSMichael Tuexen 
600a458a6e6SMichael Tuexen 		strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
601fa947a36SMichael Tuexen 		KASSERT(strq->ss_params.scheduled, ("strq %p not scheduled", (void *)strq));
602f7a77f6fSMichael Tuexen 		if (clear_values) {
603414499b3SMichael Tuexen 			strq->ss_params.ss.fb.rounds = -1;
604f7a77f6fSMichael Tuexen 		}
605414499b3SMichael Tuexen 		TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.ss.fb.next_spoke);
606414499b3SMichael Tuexen 		strq->ss_params.scheduled = false;
607f7a77f6fSMichael Tuexen 	}
608d1ea5fa9SMichael Tuexen 	asoc->ss_data.last_out_stream = NULL;
609f7a77f6fSMichael Tuexen 	return;
610f7a77f6fSMichael Tuexen }
611f7a77f6fSMichael Tuexen 
612f7a77f6fSMichael Tuexen static void
sctp_ss_fb_init_stream(struct sctp_tcb * stcb,struct sctp_stream_out * strq,struct sctp_stream_out * with_strq)613d1ea5fa9SMichael Tuexen sctp_ss_fb_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
614f7a77f6fSMichael Tuexen {
6155ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
6165ac91821SMichael Tuexen 
617d1ea5fa9SMichael Tuexen 	if (with_strq != NULL) {
618d1ea5fa9SMichael Tuexen 		if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
619d1ea5fa9SMichael Tuexen 			stcb->asoc.ss_data.locked_on_sending = strq;
620d1ea5fa9SMichael Tuexen 		}
621d1ea5fa9SMichael Tuexen 		if (stcb->asoc.ss_data.last_out_stream == with_strq) {
622d1ea5fa9SMichael Tuexen 			stcb->asoc.ss_data.last_out_stream = strq;
623d1ea5fa9SMichael Tuexen 		}
624d1ea5fa9SMichael Tuexen 	}
625414499b3SMichael Tuexen 	strq->ss_params.scheduled = false;
626252f7f93SMichael Tuexen 	if (with_strq != NULL) {
627414499b3SMichael Tuexen 		strq->ss_params.ss.fb.rounds = with_strq->ss_params.ss.fb.rounds;
628252f7f93SMichael Tuexen 	} else {
629414499b3SMichael Tuexen 		strq->ss_params.ss.fb.rounds = -1;
630252f7f93SMichael Tuexen 	}
631f7a77f6fSMichael Tuexen 	return;
632f7a77f6fSMichael Tuexen }
633f7a77f6fSMichael Tuexen 
634f7a77f6fSMichael Tuexen 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 SCTP_UNUSED)635f7a77f6fSMichael Tuexen sctp_ss_fb_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
636762ae0ecSMichael Tuexen     struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED)
637f7a77f6fSMichael Tuexen {
6385ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
639762ae0ecSMichael Tuexen 
640414499b3SMichael Tuexen 	if (!TAILQ_EMPTY(&strq->outqueue) && !strq->ss_params.scheduled) {
641414499b3SMichael Tuexen 		if (strq->ss_params.ss.fb.rounds < 0)
642414499b3SMichael Tuexen 			strq->ss_params.ss.fb.rounds = TAILQ_FIRST(&strq->outqueue)->length;
643414499b3SMichael Tuexen 		TAILQ_INSERT_TAIL(&asoc->ss_data.out.wheel, strq, ss_params.ss.fb.next_spoke);
644414499b3SMichael Tuexen 		strq->ss_params.scheduled = true;
645f7a77f6fSMichael Tuexen 	}
646f7a77f6fSMichael Tuexen 	return;
647f7a77f6fSMichael Tuexen }
648f7a77f6fSMichael Tuexen 
649f7a77f6fSMichael Tuexen 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 SCTP_UNUSED)650f7a77f6fSMichael Tuexen sctp_ss_fb_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
651762ae0ecSMichael Tuexen     struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED)
652f7a77f6fSMichael Tuexen {
6535ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
654762ae0ecSMichael Tuexen 
655b7b84c0eSMichael Tuexen 	/*
656b7b84c0eSMichael Tuexen 	 * Remove from wheel if stream queue is empty and actually is on the
657b7b84c0eSMichael Tuexen 	 * wheel
658b7b84c0eSMichael Tuexen 	 */
659414499b3SMichael Tuexen 	if (TAILQ_EMPTY(&strq->outqueue) && strq->ss_params.scheduled) {
660d1ea5fa9SMichael Tuexen 		if (asoc->ss_data.last_out_stream == strq) {
661414499b3SMichael Tuexen 			asoc->ss_data.last_out_stream = TAILQ_PREV(asoc->ss_data.last_out_stream,
662414499b3SMichael Tuexen 			    sctpwheel_listhead,
663414499b3SMichael Tuexen 			    ss_params.ss.fb.next_spoke);
664d1ea5fa9SMichael Tuexen 			if (asoc->ss_data.last_out_stream == NULL) {
665d1ea5fa9SMichael Tuexen 				asoc->ss_data.last_out_stream = TAILQ_LAST(&asoc->ss_data.out.wheel,
666f7a77f6fSMichael Tuexen 				    sctpwheel_listhead);
667f7a77f6fSMichael Tuexen 			}
668d1ea5fa9SMichael Tuexen 			if (asoc->ss_data.last_out_stream == strq) {
669d1ea5fa9SMichael Tuexen 				asoc->ss_data.last_out_stream = NULL;
670f7a77f6fSMichael Tuexen 			}
671f7a77f6fSMichael Tuexen 		}
6723ff37339SMichael Tuexen 		if (asoc->ss_data.locked_on_sending == strq) {
6733ff37339SMichael Tuexen 			asoc->ss_data.locked_on_sending = NULL;
6743ff37339SMichael Tuexen 		}
675414499b3SMichael Tuexen 		TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.ss.fb.next_spoke);
676414499b3SMichael Tuexen 		strq->ss_params.scheduled = false;
677f7a77f6fSMichael Tuexen 	}
678f7a77f6fSMichael Tuexen 	return;
679f7a77f6fSMichael Tuexen }
680f7a77f6fSMichael Tuexen 
681f7a77f6fSMichael Tuexen static struct sctp_stream_out *
sctp_ss_fb_select(struct sctp_tcb * stcb SCTP_UNUSED,struct sctp_nets * net,struct sctp_association * asoc)6827215cc1bSMichael Tuexen sctp_ss_fb_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
683f7a77f6fSMichael Tuexen     struct sctp_association *asoc)
684f7a77f6fSMichael Tuexen {
685f7a77f6fSMichael Tuexen 	struct sctp_stream_out *strq = NULL, *strqt;
686f7a77f6fSMichael Tuexen 
6875ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
6885ac91821SMichael Tuexen 
689bd19202cSMichael Tuexen 	if (asoc->ss_data.locked_on_sending != NULL) {
690fa947a36SMichael Tuexen 		KASSERT(asoc->ss_data.locked_on_sending->ss_params.scheduled,
691bd19202cSMichael Tuexen 		    ("locked_on_sending %p not scheduled",
692fa947a36SMichael Tuexen 		    (void *)asoc->ss_data.locked_on_sending));
693a610bb21SMichael Tuexen 		return (asoc->ss_data.locked_on_sending);
694a610bb21SMichael Tuexen 	}
695d1ea5fa9SMichael Tuexen 	if (asoc->ss_data.last_out_stream == NULL ||
696d1ea5fa9SMichael Tuexen 	    TAILQ_FIRST(&asoc->ss_data.out.wheel) == TAILQ_LAST(&asoc->ss_data.out.wheel, sctpwheel_listhead)) {
697d1ea5fa9SMichael Tuexen 		strqt = TAILQ_FIRST(&asoc->ss_data.out.wheel);
698f7a77f6fSMichael Tuexen 	} else {
699414499b3SMichael Tuexen 		strqt = TAILQ_NEXT(asoc->ss_data.last_out_stream, ss_params.ss.fb.next_spoke);
700f7a77f6fSMichael Tuexen 	}
701f7a77f6fSMichael Tuexen 	do {
702be2a6988SMichael Tuexen 		if ((strqt != NULL) &&
703be2a6988SMichael Tuexen 		    ((SCTP_BASE_SYSCTL(sctp_cmt_on_off) > 0) ||
704be2a6988SMichael Tuexen 		    (SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0 &&
705be2a6988SMichael Tuexen 		    (net == NULL || (TAILQ_FIRST(&strqt->outqueue) && TAILQ_FIRST(&strqt->outqueue)->net == NULL) ||
706be2a6988SMichael Tuexen 		    (net != NULL && TAILQ_FIRST(&strqt->outqueue) && TAILQ_FIRST(&strqt->outqueue)->net != NULL &&
707be2a6988SMichael Tuexen 		    TAILQ_FIRST(&strqt->outqueue)->net == net))))) {
708414499b3SMichael Tuexen 			if ((strqt->ss_params.ss.fb.rounds >= 0) &&
709414499b3SMichael Tuexen 			    ((strq == NULL) ||
710414499b3SMichael Tuexen 			    (strqt->ss_params.ss.fb.rounds < strq->ss_params.ss.fb.rounds))) {
711f7a77f6fSMichael Tuexen 				strq = strqt;
712f7a77f6fSMichael Tuexen 			}
713f7a77f6fSMichael Tuexen 		}
714f7a77f6fSMichael Tuexen 		if (strqt != NULL) {
715414499b3SMichael Tuexen 			strqt = TAILQ_NEXT(strqt, ss_params.ss.fb.next_spoke);
716f7a77f6fSMichael Tuexen 		} else {
717d1ea5fa9SMichael Tuexen 			strqt = TAILQ_FIRST(&asoc->ss_data.out.wheel);
718f7a77f6fSMichael Tuexen 		}
719f7a77f6fSMichael Tuexen 	} while (strqt != strq);
720f7a77f6fSMichael Tuexen 	return (strq);
721f7a77f6fSMichael Tuexen }
722f7a77f6fSMichael Tuexen 
723f7a77f6fSMichael Tuexen static void
sctp_ss_fb_scheduled(struct sctp_tcb * stcb,struct sctp_nets * net SCTP_UNUSED,struct sctp_association * asoc,struct sctp_stream_out * strq,int moved_how_much SCTP_UNUSED)724d1ea5fa9SMichael Tuexen sctp_ss_fb_scheduled(struct sctp_tcb *stcb, struct sctp_nets *net SCTP_UNUSED,
725f7a77f6fSMichael Tuexen     struct sctp_association *asoc, struct sctp_stream_out *strq,
7267215cc1bSMichael Tuexen     int moved_how_much SCTP_UNUSED)
727f7a77f6fSMichael Tuexen {
728d1ea5fa9SMichael Tuexen 	struct sctp_stream_queue_pending *sp;
729f7a77f6fSMichael Tuexen 	struct sctp_stream_out *strqt;
730f7a77f6fSMichael Tuexen 	int subtract;
731f7a77f6fSMichael Tuexen 
7325ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
7335ac91821SMichael Tuexen 
734fa947a36SMichael Tuexen 	if (asoc->idata_supported == 0) {
735d1ea5fa9SMichael Tuexen 		sp = TAILQ_FIRST(&strq->outqueue);
736d1ea5fa9SMichael Tuexen 		if ((sp != NULL) && (sp->some_taken == 1)) {
737fa947a36SMichael Tuexen 			asoc->ss_data.locked_on_sending = strq;
738d1ea5fa9SMichael Tuexen 		} else {
739fa947a36SMichael Tuexen 			asoc->ss_data.locked_on_sending = NULL;
740d1ea5fa9SMichael Tuexen 		}
741d1ea5fa9SMichael Tuexen 	} else {
742fa947a36SMichael Tuexen 		asoc->ss_data.locked_on_sending = NULL;
743d1ea5fa9SMichael Tuexen 	}
744414499b3SMichael Tuexen 	subtract = strq->ss_params.ss.fb.rounds;
745414499b3SMichael Tuexen 	TAILQ_FOREACH(strqt, &asoc->ss_data.out.wheel, ss_params.ss.fb.next_spoke) {
746414499b3SMichael Tuexen 		strqt->ss_params.ss.fb.rounds -= subtract;
747414499b3SMichael Tuexen 		if (strqt->ss_params.ss.fb.rounds < 0)
748414499b3SMichael Tuexen 			strqt->ss_params.ss.fb.rounds = 0;
749f7a77f6fSMichael Tuexen 	}
750f7a77f6fSMichael Tuexen 	if (TAILQ_FIRST(&strq->outqueue)) {
751414499b3SMichael Tuexen 		strq->ss_params.ss.fb.rounds = TAILQ_FIRST(&strq->outqueue)->length;
752f7a77f6fSMichael Tuexen 	} else {
753414499b3SMichael Tuexen 		strq->ss_params.ss.fb.rounds = -1;
754f7a77f6fSMichael Tuexen 	}
755d1ea5fa9SMichael Tuexen 	asoc->ss_data.last_out_stream = strq;
756f7a77f6fSMichael Tuexen 	return;
757f7a77f6fSMichael Tuexen }
758f7a77f6fSMichael Tuexen 
759f7a77f6fSMichael Tuexen /*
760f7a77f6fSMichael Tuexen  * First-come, first-serve algorithm.
761f7a77f6fSMichael Tuexen  * Maintains the order provided by the application.
762f7a77f6fSMichael Tuexen  */
763f7a77f6fSMichael Tuexen static void
764252f7f93SMichael Tuexen sctp_ss_fcfs_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
76544710431SMichael Tuexen     struct sctp_stream_out *strq SCTP_UNUSED,
766762ae0ecSMichael Tuexen     struct sctp_stream_queue_pending *sp);
767252f7f93SMichael Tuexen 
768252f7f93SMichael Tuexen static void
sctp_ss_fcfs_init(struct sctp_tcb * stcb,struct sctp_association * asoc)769762ae0ecSMichael Tuexen sctp_ss_fcfs_init(struct sctp_tcb *stcb, struct sctp_association *asoc)
770f7a77f6fSMichael Tuexen {
771252f7f93SMichael Tuexen 	uint32_t x, n = 0, add_more = 1;
772f7a77f6fSMichael Tuexen 	struct sctp_stream_queue_pending *sp;
773f7a77f6fSMichael Tuexen 	uint16_t i;
774f7a77f6fSMichael Tuexen 
7755ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
776762ae0ecSMichael Tuexen 
777d1ea5fa9SMichael Tuexen 	TAILQ_INIT(&asoc->ss_data.out.list);
778252f7f93SMichael Tuexen 	/*
779252f7f93SMichael Tuexen 	 * If there is data in the stream queues already, the scheduler of
780252f7f93SMichael Tuexen 	 * an existing association has been changed. We can only cycle
781252f7f93SMichael Tuexen 	 * through the stream queues and add everything to the FCFS queue.
782252f7f93SMichael Tuexen 	 */
783f7a77f6fSMichael Tuexen 	while (add_more) {
784f7a77f6fSMichael Tuexen 		add_more = 0;
785fa947a36SMichael Tuexen 		for (i = 0; i < asoc->streamoutcnt; i++) {
786fa947a36SMichael Tuexen 			sp = TAILQ_FIRST(&asoc->strmout[i].outqueue);
787252f7f93SMichael Tuexen 			x = 0;
788252f7f93SMichael Tuexen 			/* Find n. message in current stream queue */
789252f7f93SMichael Tuexen 			while (sp != NULL && x < n) {
790f7a77f6fSMichael Tuexen 				sp = TAILQ_NEXT(sp, next);
791252f7f93SMichael Tuexen 				x++;
792f7a77f6fSMichael Tuexen 			}
793f7a77f6fSMichael Tuexen 			if (sp != NULL) {
794fa947a36SMichael Tuexen 				sctp_ss_fcfs_add(stcb, asoc, &asoc->strmout[i], sp);
795f7a77f6fSMichael Tuexen 				add_more = 1;
796f7a77f6fSMichael Tuexen 			}
797f7a77f6fSMichael Tuexen 		}
798252f7f93SMichael Tuexen 		n++;
799f7a77f6fSMichael Tuexen 	}
800f7a77f6fSMichael Tuexen 	return;
801f7a77f6fSMichael Tuexen }
802f7a77f6fSMichael Tuexen 
803f7a77f6fSMichael Tuexen static void
sctp_ss_fcfs_clear(struct sctp_tcb * stcb,struct sctp_association * asoc,bool clear_values SCTP_UNUSED)804f7a77f6fSMichael Tuexen sctp_ss_fcfs_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
805414499b3SMichael Tuexen     bool clear_values SCTP_UNUSED)
806f7a77f6fSMichael Tuexen {
807e6dcce69SMichael Tuexen 	struct sctp_stream_queue_pending *sp;
808e6dcce69SMichael Tuexen 
8095ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
810762ae0ecSMichael Tuexen 
811d1ea5fa9SMichael Tuexen 	while (!TAILQ_EMPTY(&asoc->ss_data.out.list)) {
812e6dcce69SMichael Tuexen 		sp = TAILQ_FIRST(&asoc->ss_data.out.list);
813fa947a36SMichael Tuexen 		KASSERT(sp->scheduled, ("sp %p not scheduled", (void *)sp));
814e6dcce69SMichael Tuexen 		TAILQ_REMOVE(&asoc->ss_data.out.list, sp, ss_next);
815414499b3SMichael Tuexen 		sp->scheduled = false;
816f7a77f6fSMichael Tuexen 	}
817e19d93b1SMichael Tuexen 	asoc->ss_data.last_out_stream = NULL;
818f7a77f6fSMichael Tuexen 	return;
819f7a77f6fSMichael Tuexen }
820f7a77f6fSMichael Tuexen 
821f7a77f6fSMichael Tuexen static void
sctp_ss_fcfs_init_stream(struct sctp_tcb * stcb,struct sctp_stream_out * strq,struct sctp_stream_out * with_strq)822d1ea5fa9SMichael Tuexen sctp_ss_fcfs_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
823f7a77f6fSMichael Tuexen {
8245ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
8255ac91821SMichael Tuexen 
826d1ea5fa9SMichael Tuexen 	if (with_strq != NULL) {
827d1ea5fa9SMichael Tuexen 		if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
828d1ea5fa9SMichael Tuexen 			stcb->asoc.ss_data.locked_on_sending = strq;
829d1ea5fa9SMichael Tuexen 		}
830d1ea5fa9SMichael Tuexen 		if (stcb->asoc.ss_data.last_out_stream == with_strq) {
831d1ea5fa9SMichael Tuexen 			stcb->asoc.ss_data.last_out_stream = strq;
832d1ea5fa9SMichael Tuexen 		}
833d1ea5fa9SMichael Tuexen 	}
834414499b3SMichael Tuexen 	strq->ss_params.scheduled = false;
835f7a77f6fSMichael Tuexen 	return;
836f7a77f6fSMichael Tuexen }
837f7a77f6fSMichael Tuexen 
838f7a77f6fSMichael Tuexen static void
sctp_ss_fcfs_add(struct sctp_tcb * stcb,struct sctp_association * asoc,struct sctp_stream_out * strq SCTP_UNUSED,struct sctp_stream_queue_pending * sp)839f7a77f6fSMichael Tuexen sctp_ss_fcfs_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
840762ae0ecSMichael Tuexen     struct sctp_stream_out *strq SCTP_UNUSED, struct sctp_stream_queue_pending *sp)
841f7a77f6fSMichael Tuexen {
8425ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
843762ae0ecSMichael Tuexen 
844414499b3SMichael Tuexen 	if (!sp->scheduled) {
845d1ea5fa9SMichael Tuexen 		TAILQ_INSERT_TAIL(&asoc->ss_data.out.list, sp, ss_next);
846414499b3SMichael Tuexen 		sp->scheduled = true;
847f7a77f6fSMichael Tuexen 	}
848f7a77f6fSMichael Tuexen 	return;
849f7a77f6fSMichael Tuexen }
850f7a77f6fSMichael Tuexen 
851414499b3SMichael Tuexen static bool
sctp_ss_fcfs_is_empty(struct sctp_tcb * stcb SCTP_UNUSED,struct sctp_association * asoc)8527215cc1bSMichael Tuexen sctp_ss_fcfs_is_empty(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc)
853f7a77f6fSMichael Tuexen {
8545ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
8555ac91821SMichael Tuexen 
856414499b3SMichael Tuexen 	return (TAILQ_EMPTY(&asoc->ss_data.out.list));
857f7a77f6fSMichael Tuexen }
858f7a77f6fSMichael Tuexen 
859f7a77f6fSMichael Tuexen static void
sctp_ss_fcfs_remove(struct sctp_tcb * stcb,struct sctp_association * asoc,struct sctp_stream_out * strq SCTP_UNUSED,struct sctp_stream_queue_pending * sp)860f7a77f6fSMichael Tuexen sctp_ss_fcfs_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
861762ae0ecSMichael Tuexen     struct sctp_stream_out *strq SCTP_UNUSED, struct sctp_stream_queue_pending *sp)
862f7a77f6fSMichael Tuexen {
8635ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
864762ae0ecSMichael Tuexen 
865414499b3SMichael Tuexen 	if (sp->scheduled) {
866d1ea5fa9SMichael Tuexen 		TAILQ_REMOVE(&asoc->ss_data.out.list, sp, ss_next);
867414499b3SMichael Tuexen 		sp->scheduled = false;
868f7a77f6fSMichael Tuexen 	}
869f7a77f6fSMichael Tuexen 	return;
870f7a77f6fSMichael Tuexen }
871f7a77f6fSMichael Tuexen 
872f7a77f6fSMichael Tuexen static struct sctp_stream_out *
sctp_ss_fcfs_select(struct sctp_tcb * stcb SCTP_UNUSED,struct sctp_nets * net,struct sctp_association * asoc)8737215cc1bSMichael Tuexen sctp_ss_fcfs_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
874f7a77f6fSMichael Tuexen     struct sctp_association *asoc)
875f7a77f6fSMichael Tuexen {
876f7a77f6fSMichael Tuexen 	struct sctp_stream_out *strq;
877f7a77f6fSMichael Tuexen 	struct sctp_stream_queue_pending *sp;
878f7a77f6fSMichael Tuexen 
8795ac91821SMichael Tuexen 	SCTP_TCB_LOCK_ASSERT(stcb);
8805ac91821SMichael Tuexen 
881a610bb21SMichael Tuexen 	if (asoc->ss_data.locked_on_sending) {
882a610bb21SMichael Tuexen 		return (asoc->ss_data.locked_on_sending);
883a610bb21SMichael Tuexen 	}
884d1ea5fa9SMichael Tuexen 	sp = TAILQ_FIRST(&asoc->ss_data.out.list);
885f7a77f6fSMichael Tuexen default_again:
886f7a77f6fSMichael Tuexen 	if (sp != NULL) {
88749656eefSMichael Tuexen 		strq = &asoc->strmout[sp->sid];
888f7a77f6fSMichael Tuexen 	} else {
889f7a77f6fSMichael Tuexen 		strq = NULL;
890f7a77f6fSMichael Tuexen 	}
891f7a77f6fSMichael Tuexen 
892f7a77f6fSMichael Tuexen 	/*
893f7a77f6fSMichael Tuexen 	 * If CMT is off, we must validate that the stream in question has
8945d40cf5dSRandall Stewart 	 * the first item pointed towards are network destination requested
895f7a77f6fSMichael Tuexen 	 * by the caller. Note that if we turn out to be locked to a stream
896f7a77f6fSMichael Tuexen 	 * (assigning TSN's then we must stop, since we cannot look for
897f7a77f6fSMichael Tuexen 	 * another stream with data to send to that destination). In CMT's
898f7a77f6fSMichael Tuexen 	 * case, by skipping this check, we will send one data packet
899f7a77f6fSMichael Tuexen 	 * towards the requested net.
900f7a77f6fSMichael Tuexen 	 */
901f7a77f6fSMichael Tuexen 	if (net != NULL && strq != NULL &&
902f7a77f6fSMichael Tuexen 	    SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
903f7a77f6fSMichael Tuexen 		if (TAILQ_FIRST(&strq->outqueue) &&
904f7a77f6fSMichael Tuexen 		    TAILQ_FIRST(&strq->outqueue)->net != NULL &&
905f7a77f6fSMichael Tuexen 		    TAILQ_FIRST(&strq->outqueue)->net != net) {
906252f7f93SMichael Tuexen 			sp = TAILQ_NEXT(sp, ss_next);
907f7a77f6fSMichael Tuexen 			goto default_again;
908f7a77f6fSMichael Tuexen 		}
909f7a77f6fSMichael Tuexen 	}
910f7a77f6fSMichael Tuexen 	return (strq);
911f7a77f6fSMichael Tuexen }
912f7a77f6fSMichael Tuexen 
91328ea9470SMichael Tuexen static void
sctp_ss_fcfs_scheduled(struct sctp_tcb * stcb,struct sctp_nets * net SCTP_UNUSED,struct sctp_association * asoc,struct sctp_stream_out * strq,int moved_how_much SCTP_UNUSED)91428ea9470SMichael Tuexen sctp_ss_fcfs_scheduled(struct sctp_tcb *stcb,
91528ea9470SMichael Tuexen     struct sctp_nets *net SCTP_UNUSED,
91628ea9470SMichael Tuexen     struct sctp_association *asoc,
91728ea9470SMichael Tuexen     struct sctp_stream_out *strq,
91828ea9470SMichael Tuexen     int moved_how_much SCTP_UNUSED)
91928ea9470SMichael Tuexen {
92028ea9470SMichael Tuexen 	struct sctp_stream_queue_pending *sp;
92128ea9470SMichael Tuexen 
92228ea9470SMichael Tuexen 	KASSERT(strq != NULL, ("strq is NULL"));
92328ea9470SMichael Tuexen 	asoc->ss_data.last_out_stream = strq;
92428ea9470SMichael Tuexen 	if (asoc->idata_supported == 0) {
92528ea9470SMichael Tuexen 		sp = TAILQ_FIRST(&strq->outqueue);
92628ea9470SMichael Tuexen 		if ((sp != NULL) && (sp->some_taken == 1)) {
92728ea9470SMichael Tuexen 			asoc->ss_data.locked_on_sending = strq;
92828ea9470SMichael Tuexen 		} else {
92928ea9470SMichael Tuexen 			asoc->ss_data.locked_on_sending = NULL;
93028ea9470SMichael Tuexen 		}
93128ea9470SMichael Tuexen 	} else {
93228ea9470SMichael Tuexen 		asoc->ss_data.locked_on_sending = NULL;
93328ea9470SMichael Tuexen 	}
93428ea9470SMichael Tuexen 	return;
93528ea9470SMichael Tuexen }
93628ea9470SMichael Tuexen 
937ed654363SMichael Tuexen const struct sctp_ss_functions sctp_ss_functions[] = {
938f7a77f6fSMichael Tuexen /* SCTP_SS_DEFAULT */
939f7a77f6fSMichael Tuexen 	{
940f7a77f6fSMichael Tuexen 		.sctp_ss_init = sctp_ss_default_init,
941f7a77f6fSMichael Tuexen 		.sctp_ss_clear = sctp_ss_default_clear,
942f7a77f6fSMichael Tuexen 		.sctp_ss_init_stream = sctp_ss_default_init_stream,
943f7a77f6fSMichael Tuexen 		.sctp_ss_add_to_stream = sctp_ss_default_add,
944f7a77f6fSMichael Tuexen 		.sctp_ss_is_empty = sctp_ss_default_is_empty,
945f7a77f6fSMichael Tuexen 		.sctp_ss_remove_from_stream = sctp_ss_default_remove,
946f7a77f6fSMichael Tuexen 		.sctp_ss_select_stream = sctp_ss_default_select,
947f7a77f6fSMichael Tuexen 		.sctp_ss_scheduled = sctp_ss_default_scheduled,
948f7a77f6fSMichael Tuexen 		.sctp_ss_packet_done = sctp_ss_default_packet_done,
949f7a77f6fSMichael Tuexen 		.sctp_ss_get_value = sctp_ss_default_get_value,
950d1ea5fa9SMichael Tuexen 		.sctp_ss_set_value = sctp_ss_default_set_value,
951d1ea5fa9SMichael Tuexen 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
952f7a77f6fSMichael Tuexen 	},
95366d6fd53SMichael Tuexen /* SCTP_SS_RR */
954f7a77f6fSMichael Tuexen 	{
955f7a77f6fSMichael Tuexen 		.sctp_ss_init = sctp_ss_default_init,
956f7a77f6fSMichael Tuexen 		.sctp_ss_clear = sctp_ss_default_clear,
957f7a77f6fSMichael Tuexen 		.sctp_ss_init_stream = sctp_ss_default_init_stream,
958f7a77f6fSMichael Tuexen 		.sctp_ss_add_to_stream = sctp_ss_rr_add,
959f7a77f6fSMichael Tuexen 		.sctp_ss_is_empty = sctp_ss_default_is_empty,
960f7a77f6fSMichael Tuexen 		.sctp_ss_remove_from_stream = sctp_ss_default_remove,
961f7a77f6fSMichael Tuexen 		.sctp_ss_select_stream = sctp_ss_default_select,
962f7a77f6fSMichael Tuexen 		.sctp_ss_scheduled = sctp_ss_default_scheduled,
963f7a77f6fSMichael Tuexen 		.sctp_ss_packet_done = sctp_ss_default_packet_done,
964f7a77f6fSMichael Tuexen 		.sctp_ss_get_value = sctp_ss_default_get_value,
965d1ea5fa9SMichael Tuexen 		.sctp_ss_set_value = sctp_ss_default_set_value,
966d1ea5fa9SMichael Tuexen 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
967f7a77f6fSMichael Tuexen 	},
96866d6fd53SMichael Tuexen /* SCTP_SS_RR_PKT */
969f7a77f6fSMichael Tuexen 	{
970f7a77f6fSMichael Tuexen 		.sctp_ss_init = sctp_ss_default_init,
971f7a77f6fSMichael Tuexen 		.sctp_ss_clear = sctp_ss_default_clear,
972f7a77f6fSMichael Tuexen 		.sctp_ss_init_stream = sctp_ss_default_init_stream,
973be2a6988SMichael Tuexen 		.sctp_ss_add_to_stream = sctp_ss_rr_add,
974f7a77f6fSMichael Tuexen 		.sctp_ss_is_empty = sctp_ss_default_is_empty,
975f7a77f6fSMichael Tuexen 		.sctp_ss_remove_from_stream = sctp_ss_default_remove,
976f7a77f6fSMichael Tuexen 		.sctp_ss_select_stream = sctp_ss_rrp_select,
977f7a77f6fSMichael Tuexen 		.sctp_ss_scheduled = sctp_ss_default_scheduled,
978f7a77f6fSMichael Tuexen 		.sctp_ss_packet_done = sctp_ss_rrp_packet_done,
979f7a77f6fSMichael Tuexen 		.sctp_ss_get_value = sctp_ss_default_get_value,
980d1ea5fa9SMichael Tuexen 		.sctp_ss_set_value = sctp_ss_default_set_value,
981d1ea5fa9SMichael Tuexen 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
982f7a77f6fSMichael Tuexen 	},
98366d6fd53SMichael Tuexen /* SCTP_SS_PRIO */
984f7a77f6fSMichael Tuexen 	{
985f7a77f6fSMichael Tuexen 		.sctp_ss_init = sctp_ss_default_init,
986f7a77f6fSMichael Tuexen 		.sctp_ss_clear = sctp_ss_prio_clear,
987f7a77f6fSMichael Tuexen 		.sctp_ss_init_stream = sctp_ss_prio_init_stream,
988f7a77f6fSMichael Tuexen 		.sctp_ss_add_to_stream = sctp_ss_prio_add,
989f7a77f6fSMichael Tuexen 		.sctp_ss_is_empty = sctp_ss_default_is_empty,
990f7a77f6fSMichael Tuexen 		.sctp_ss_remove_from_stream = sctp_ss_prio_remove,
991f7a77f6fSMichael Tuexen 		.sctp_ss_select_stream = sctp_ss_prio_select,
992f7a77f6fSMichael Tuexen 		.sctp_ss_scheduled = sctp_ss_default_scheduled,
993f7a77f6fSMichael Tuexen 		.sctp_ss_packet_done = sctp_ss_default_packet_done,
994f7a77f6fSMichael Tuexen 		.sctp_ss_get_value = sctp_ss_prio_get_value,
995d1ea5fa9SMichael Tuexen 		.sctp_ss_set_value = sctp_ss_prio_set_value,
996d1ea5fa9SMichael Tuexen 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
997f7a77f6fSMichael Tuexen 	},
99866d6fd53SMichael Tuexen /* SCTP_SS_FB */
999f7a77f6fSMichael Tuexen 	{
1000f7a77f6fSMichael Tuexen 		.sctp_ss_init = sctp_ss_default_init,
1001f7a77f6fSMichael Tuexen 		.sctp_ss_clear = sctp_ss_fb_clear,
1002f7a77f6fSMichael Tuexen 		.sctp_ss_init_stream = sctp_ss_fb_init_stream,
1003f7a77f6fSMichael Tuexen 		.sctp_ss_add_to_stream = sctp_ss_fb_add,
1004f7a77f6fSMichael Tuexen 		.sctp_ss_is_empty = sctp_ss_default_is_empty,
1005f7a77f6fSMichael Tuexen 		.sctp_ss_remove_from_stream = sctp_ss_fb_remove,
1006f7a77f6fSMichael Tuexen 		.sctp_ss_select_stream = sctp_ss_fb_select,
1007f7a77f6fSMichael Tuexen 		.sctp_ss_scheduled = sctp_ss_fb_scheduled,
1008f7a77f6fSMichael Tuexen 		.sctp_ss_packet_done = sctp_ss_default_packet_done,
1009f7a77f6fSMichael Tuexen 		.sctp_ss_get_value = sctp_ss_default_get_value,
1010d1ea5fa9SMichael Tuexen 		.sctp_ss_set_value = sctp_ss_default_set_value,
1011d1ea5fa9SMichael Tuexen 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
1012f7a77f6fSMichael Tuexen 	},
101366d6fd53SMichael Tuexen /* SCTP_SS_FCFS */
1014f7a77f6fSMichael Tuexen 	{
1015f7a77f6fSMichael Tuexen 		.sctp_ss_init = sctp_ss_fcfs_init,
1016f7a77f6fSMichael Tuexen 		.sctp_ss_clear = sctp_ss_fcfs_clear,
1017f7a77f6fSMichael Tuexen 		.sctp_ss_init_stream = sctp_ss_fcfs_init_stream,
1018f7a77f6fSMichael Tuexen 		.sctp_ss_add_to_stream = sctp_ss_fcfs_add,
1019f7a77f6fSMichael Tuexen 		.sctp_ss_is_empty = sctp_ss_fcfs_is_empty,
1020f7a77f6fSMichael Tuexen 		.sctp_ss_remove_from_stream = sctp_ss_fcfs_remove,
1021f7a77f6fSMichael Tuexen 		.sctp_ss_select_stream = sctp_ss_fcfs_select,
102228ea9470SMichael Tuexen 		.sctp_ss_scheduled = sctp_ss_fcfs_scheduled,
1023f7a77f6fSMichael Tuexen 		.sctp_ss_packet_done = sctp_ss_default_packet_done,
1024f7a77f6fSMichael Tuexen 		.sctp_ss_get_value = sctp_ss_default_get_value,
1025d1ea5fa9SMichael Tuexen 		.sctp_ss_set_value = sctp_ss_default_set_value,
1026d1ea5fa9SMichael Tuexen 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
1027f7a77f6fSMichael Tuexen 	}
1028f7a77f6fSMichael Tuexen };
1029