xref: /freebsd/sys/netinet/sctp_ss_functions.c (revision 076ad2f836d5f49dc1375f1677335a48fe0d4b82)
1 /*-
2  * Copyright (c) 2010-2012, by Michael Tuexen. All rights reserved.
3  * Copyright (c) 2010-2012, by Randall Stewart. All rights reserved.
4  * Copyright (c) 2010-2012, by Robin Seggelmann. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * a) Redistributions of source code must retain the above copyright notice,
10  *    this list of conditions and the following disclaimer.
11  *
12  * b) Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in
14  *    the documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
18  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
26  * THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <netinet/sctp_pcb.h>
33 
34 /*
35  * Default simple round-robin algorithm.
36  * Just interates the streams in the order they appear.
37  */
38 
39 static void
40 sctp_ss_default_add(struct sctp_tcb *, struct sctp_association *,
41     struct sctp_stream_out *,
42     struct sctp_stream_queue_pending *, int);
43 
44 static void
45 sctp_ss_default_remove(struct sctp_tcb *, struct sctp_association *,
46     struct sctp_stream_out *,
47     struct sctp_stream_queue_pending *, int);
48 
49 static void
50 sctp_ss_default_init(struct sctp_tcb *stcb, struct sctp_association *asoc,
51     int holds_lock)
52 {
53 	uint16_t i;
54 
55 	asoc->ss_data.locked_on_sending = NULL;
56 	asoc->ss_data.last_out_stream = NULL;
57 	TAILQ_INIT(&asoc->ss_data.out.wheel);
58 	/*
59 	 * If there is data in the stream queues already, the scheduler of
60 	 * an existing association has been changed. We need to add all
61 	 * stream queues to the wheel.
62 	 */
63 	for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
64 		stcb->asoc.ss_functions.sctp_ss_add_to_stream(stcb, &stcb->asoc,
65 		    &stcb->asoc.strmout[i],
66 		    NULL, holds_lock);
67 	}
68 	return;
69 }
70 
71 static void
72 sctp_ss_default_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
73     int clear_values SCTP_UNUSED, int holds_lock)
74 {
75 	if (holds_lock == 0) {
76 		SCTP_TCB_SEND_LOCK(stcb);
77 	}
78 	while (!TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
79 		struct sctp_stream_out *strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
80 
81 		TAILQ_REMOVE(&asoc->ss_data.out.wheel, TAILQ_FIRST(&asoc->ss_data.out.wheel), ss_params.rr.next_spoke);
82 		strq->ss_params.rr.next_spoke.tqe_next = NULL;
83 		strq->ss_params.rr.next_spoke.tqe_prev = NULL;
84 	}
85 	asoc->ss_data.last_out_stream = NULL;
86 	if (holds_lock == 0) {
87 		SCTP_TCB_SEND_UNLOCK(stcb);
88 	}
89 	return;
90 }
91 
92 static void
93 sctp_ss_default_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
94 {
95 	if (with_strq != NULL) {
96 		if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
97 			stcb->asoc.ss_data.locked_on_sending = strq;
98 		}
99 		if (stcb->asoc.ss_data.last_out_stream == with_strq) {
100 			stcb->asoc.ss_data.last_out_stream = strq;
101 		}
102 	}
103 	strq->ss_params.rr.next_spoke.tqe_next = NULL;
104 	strq->ss_params.rr.next_spoke.tqe_prev = NULL;
105 	return;
106 }
107 
108 static void
109 sctp_ss_default_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
110     struct sctp_stream_out *strq,
111     struct sctp_stream_queue_pending *sp SCTP_UNUSED, int holds_lock)
112 {
113 	if (holds_lock == 0) {
114 		SCTP_TCB_SEND_LOCK(stcb);
115 	}
116 	/* Add to wheel if not already on it and stream queue not empty */
117 	if (!TAILQ_EMPTY(&strq->outqueue) &&
118 	    (strq->ss_params.rr.next_spoke.tqe_next == NULL) &&
119 	    (strq->ss_params.rr.next_spoke.tqe_prev == NULL)) {
120 		TAILQ_INSERT_TAIL(&asoc->ss_data.out.wheel,
121 		    strq, ss_params.rr.next_spoke);
122 	}
123 	if (holds_lock == 0) {
124 		SCTP_TCB_SEND_UNLOCK(stcb);
125 	}
126 	return;
127 }
128 
129 static int
130 sctp_ss_default_is_empty(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc)
131 {
132 	if (TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
133 		return (1);
134 	} else {
135 		return (0);
136 	}
137 }
138 
139 static void
140 sctp_ss_default_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
141     struct sctp_stream_out *strq,
142     struct sctp_stream_queue_pending *sp SCTP_UNUSED, int holds_lock)
143 {
144 	if (holds_lock == 0) {
145 		SCTP_TCB_SEND_LOCK(stcb);
146 	}
147 	/*
148 	 * Remove from wheel if stream queue is empty and actually is on the
149 	 * wheel
150 	 */
151 	if (TAILQ_EMPTY(&strq->outqueue) &&
152 	    (strq->ss_params.rr.next_spoke.tqe_next != NULL ||
153 	    strq->ss_params.rr.next_spoke.tqe_prev != NULL)) {
154 		if (asoc->ss_data.last_out_stream == strq) {
155 			asoc->ss_data.last_out_stream = TAILQ_PREV(asoc->ss_data.last_out_stream,
156 			    sctpwheel_listhead,
157 			    ss_params.rr.next_spoke);
158 			if (asoc->ss_data.last_out_stream == NULL) {
159 				asoc->ss_data.last_out_stream = TAILQ_LAST(&asoc->ss_data.out.wheel,
160 				    sctpwheel_listhead);
161 			}
162 			if (asoc->ss_data.last_out_stream == strq) {
163 				asoc->ss_data.last_out_stream = NULL;
164 			}
165 		}
166 		TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.rr.next_spoke);
167 		strq->ss_params.rr.next_spoke.tqe_next = NULL;
168 		strq->ss_params.rr.next_spoke.tqe_prev = NULL;
169 	}
170 	if (holds_lock == 0) {
171 		SCTP_TCB_SEND_UNLOCK(stcb);
172 	}
173 	return;
174 }
175 
176 
177 static struct sctp_stream_out *
178 sctp_ss_default_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
179     struct sctp_association *asoc)
180 {
181 	struct sctp_stream_out *strq, *strqt;
182 
183 	if (asoc->ss_data.locked_on_sending) {
184 		return (asoc->ss_data.locked_on_sending);
185 	}
186 	strqt = asoc->ss_data.last_out_stream;
187 default_again:
188 	/* Find the next stream to use */
189 	if (strqt == NULL) {
190 		strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
191 	} else {
192 		strq = TAILQ_NEXT(strqt, ss_params.rr.next_spoke);
193 		if (strq == NULL) {
194 			strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
195 		}
196 	}
197 
198 	/*
199 	 * If CMT is off, we must validate that the stream in question has
200 	 * the first item pointed towards are network destination requested
201 	 * by the caller. Note that if we turn out to be locked to a stream
202 	 * (assigning TSN's then we must stop, since we cannot look for
203 	 * another stream with data to send to that destination). In CMT's
204 	 * case, by skipping this check, we will send one data packet
205 	 * towards the requested net.
206 	 */
207 	if (net != NULL && strq != NULL &&
208 	    SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
209 		if (TAILQ_FIRST(&strq->outqueue) &&
210 		    TAILQ_FIRST(&strq->outqueue)->net != NULL &&
211 		    TAILQ_FIRST(&strq->outqueue)->net != net) {
212 			if (strq == asoc->ss_data.last_out_stream) {
213 				return (NULL);
214 			} else {
215 				strqt = strq;
216 				goto default_again;
217 			}
218 		}
219 	}
220 	return (strq);
221 }
222 
223 static void
224 sctp_ss_default_scheduled(struct sctp_tcb *stcb,
225     struct sctp_nets *net SCTP_UNUSED,
226     struct sctp_association *asoc,
227     struct sctp_stream_out *strq,
228     int moved_how_much SCTP_UNUSED)
229 {
230 	struct sctp_stream_queue_pending *sp;
231 
232 	asoc->ss_data.last_out_stream = strq;
233 	if (stcb->asoc.idata_supported == 0) {
234 		sp = TAILQ_FIRST(&strq->outqueue);
235 		if ((sp != NULL) && (sp->some_taken == 1)) {
236 			stcb->asoc.ss_data.locked_on_sending = strq;
237 		} else {
238 			stcb->asoc.ss_data.locked_on_sending = NULL;
239 		}
240 	} else {
241 		stcb->asoc.ss_data.locked_on_sending = NULL;
242 	}
243 	return;
244 }
245 
246 static void
247 sctp_ss_default_packet_done(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net SCTP_UNUSED,
248     struct sctp_association *asoc SCTP_UNUSED)
249 {
250 	/* Nothing to be done here */
251 	return;
252 }
253 
254 static int
255 sctp_ss_default_get_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED,
256     struct sctp_stream_out *strq SCTP_UNUSED, uint16_t *value SCTP_UNUSED)
257 {
258 	/* Nothing to be done here */
259 	return (-1);
260 }
261 
262 static int
263 sctp_ss_default_set_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED,
264     struct sctp_stream_out *strq SCTP_UNUSED, uint16_t value SCTP_UNUSED)
265 {
266 	/* Nothing to be done here */
267 	return (-1);
268 }
269 
270 static int
271 sctp_ss_default_is_user_msgs_incomplete(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED)
272 {
273 	return (0);
274 }
275 
276 /*
277  * Real round-robin algorithm.
278  * Always interates the streams in ascending order.
279  */
280 static void
281 sctp_ss_rr_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
282     struct sctp_stream_out *strq,
283     struct sctp_stream_queue_pending *sp SCTP_UNUSED, int holds_lock)
284 {
285 	struct sctp_stream_out *strqt;
286 
287 	if (holds_lock == 0) {
288 		SCTP_TCB_SEND_LOCK(stcb);
289 	}
290 	if (!TAILQ_EMPTY(&strq->outqueue) &&
291 	    (strq->ss_params.rr.next_spoke.tqe_next == NULL) &&
292 	    (strq->ss_params.rr.next_spoke.tqe_prev == NULL)) {
293 		if (TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
294 			TAILQ_INSERT_HEAD(&asoc->ss_data.out.wheel, strq, ss_params.rr.next_spoke);
295 		} else {
296 			strqt = TAILQ_FIRST(&asoc->ss_data.out.wheel);
297 			while (strqt != NULL && (strqt->sid < strq->sid)) {
298 				strqt = TAILQ_NEXT(strqt, ss_params.rr.next_spoke);
299 			}
300 			if (strqt != NULL) {
301 				TAILQ_INSERT_BEFORE(strqt, strq, ss_params.rr.next_spoke);
302 			} else {
303 				TAILQ_INSERT_TAIL(&asoc->ss_data.out.wheel, strq, ss_params.rr.next_spoke);
304 			}
305 		}
306 	}
307 	if (holds_lock == 0) {
308 		SCTP_TCB_SEND_UNLOCK(stcb);
309 	}
310 	return;
311 }
312 
313 /*
314  * Real round-robin per packet algorithm.
315  * Always interates the streams in ascending order and
316  * only fills messages of the same stream in a packet.
317  */
318 static struct sctp_stream_out *
319 sctp_ss_rrp_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net SCTP_UNUSED,
320     struct sctp_association *asoc)
321 {
322 	return (asoc->ss_data.last_out_stream);
323 }
324 
325 static void
326 sctp_ss_rrp_packet_done(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
327     struct sctp_association *asoc)
328 {
329 	struct sctp_stream_out *strq, *strqt;
330 
331 	strqt = asoc->ss_data.last_out_stream;
332 rrp_again:
333 	/* Find the next stream to use */
334 	if (strqt == NULL) {
335 		strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
336 	} else {
337 		strq = TAILQ_NEXT(strqt, ss_params.rr.next_spoke);
338 		if (strq == NULL) {
339 			strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
340 		}
341 	}
342 
343 	/*
344 	 * If CMT is off, we must validate that the stream in question has
345 	 * the first item pointed towards are network destination requested
346 	 * by the caller. Note that if we turn out to be locked to a stream
347 	 * (assigning TSN's then we must stop, since we cannot look for
348 	 * another stream with data to send to that destination). In CMT's
349 	 * case, by skipping this check, we will send one data packet
350 	 * towards the requested net.
351 	 */
352 	if (net != NULL && strq != NULL &&
353 	    SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
354 		if (TAILQ_FIRST(&strq->outqueue) &&
355 		    TAILQ_FIRST(&strq->outqueue)->net != NULL &&
356 		    TAILQ_FIRST(&strq->outqueue)->net != net) {
357 			if (strq == asoc->ss_data.last_out_stream) {
358 				strq = NULL;
359 			} else {
360 				strqt = strq;
361 				goto rrp_again;
362 			}
363 		}
364 	}
365 	asoc->ss_data.last_out_stream = strq;
366 	return;
367 }
368 
369 
370 /*
371  * Priority algorithm.
372  * Always prefers streams based on their priority id.
373  */
374 static void
375 sctp_ss_prio_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
376     int clear_values, int holds_lock)
377 {
378 	if (holds_lock == 0) {
379 		SCTP_TCB_SEND_LOCK(stcb);
380 	}
381 	while (!TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
382 		struct sctp_stream_out *strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
383 
384 		if (clear_values) {
385 			strq->ss_params.prio.priority = 0;
386 		}
387 		TAILQ_REMOVE(&asoc->ss_data.out.wheel, TAILQ_FIRST(&asoc->ss_data.out.wheel), ss_params.prio.next_spoke);
388 		strq->ss_params.prio.next_spoke.tqe_next = NULL;
389 		strq->ss_params.prio.next_spoke.tqe_prev = NULL;
390 
391 	}
392 	asoc->ss_data.last_out_stream = NULL;
393 	if (holds_lock == 0) {
394 		SCTP_TCB_SEND_UNLOCK(stcb);
395 	}
396 	return;
397 }
398 
399 static void
400 sctp_ss_prio_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
401 {
402 	if (with_strq != NULL) {
403 		if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
404 			stcb->asoc.ss_data.locked_on_sending = strq;
405 		}
406 		if (stcb->asoc.ss_data.last_out_stream == with_strq) {
407 			stcb->asoc.ss_data.last_out_stream = strq;
408 		}
409 	}
410 	strq->ss_params.prio.next_spoke.tqe_next = NULL;
411 	strq->ss_params.prio.next_spoke.tqe_prev = NULL;
412 	if (with_strq != NULL) {
413 		strq->ss_params.prio.priority = with_strq->ss_params.prio.priority;
414 	} else {
415 		strq->ss_params.prio.priority = 0;
416 	}
417 	return;
418 }
419 
420 static void
421 sctp_ss_prio_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
422     struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED,
423     int holds_lock)
424 {
425 	struct sctp_stream_out *strqt;
426 
427 	if (holds_lock == 0) {
428 		SCTP_TCB_SEND_LOCK(stcb);
429 	}
430 	/* Add to wheel if not already on it and stream queue not empty */
431 	if (!TAILQ_EMPTY(&strq->outqueue) &&
432 	    (strq->ss_params.prio.next_spoke.tqe_next == NULL) &&
433 	    (strq->ss_params.prio.next_spoke.tqe_prev == NULL)) {
434 		if (TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
435 			TAILQ_INSERT_HEAD(&asoc->ss_data.out.wheel, strq, ss_params.prio.next_spoke);
436 		} else {
437 			strqt = TAILQ_FIRST(&asoc->ss_data.out.wheel);
438 			while (strqt != NULL && strqt->ss_params.prio.priority < strq->ss_params.prio.priority) {
439 				strqt = TAILQ_NEXT(strqt, ss_params.prio.next_spoke);
440 			}
441 			if (strqt != NULL) {
442 				TAILQ_INSERT_BEFORE(strqt, strq, ss_params.prio.next_spoke);
443 			} else {
444 				TAILQ_INSERT_TAIL(&asoc->ss_data.out.wheel, strq, ss_params.prio.next_spoke);
445 			}
446 		}
447 	}
448 	if (holds_lock == 0) {
449 		SCTP_TCB_SEND_UNLOCK(stcb);
450 	}
451 	return;
452 }
453 
454 static void
455 sctp_ss_prio_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
456     struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED,
457     int holds_lock)
458 {
459 	if (holds_lock == 0) {
460 		SCTP_TCB_SEND_LOCK(stcb);
461 	}
462 	/*
463 	 * Remove from wheel if stream queue is empty and actually is on the
464 	 * wheel
465 	 */
466 	if (TAILQ_EMPTY(&strq->outqueue) &&
467 	    (strq->ss_params.prio.next_spoke.tqe_next != NULL ||
468 	    strq->ss_params.prio.next_spoke.tqe_prev != NULL)) {
469 		if (asoc->ss_data.last_out_stream == strq) {
470 			asoc->ss_data.last_out_stream = TAILQ_PREV(asoc->ss_data.last_out_stream, sctpwheel_listhead,
471 			    ss_params.prio.next_spoke);
472 			if (asoc->ss_data.last_out_stream == NULL) {
473 				asoc->ss_data.last_out_stream = TAILQ_LAST(&asoc->ss_data.out.wheel,
474 				    sctpwheel_listhead);
475 			}
476 			if (asoc->ss_data.last_out_stream == strq) {
477 				asoc->ss_data.last_out_stream = NULL;
478 			}
479 		}
480 		TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.prio.next_spoke);
481 		strq->ss_params.prio.next_spoke.tqe_next = NULL;
482 		strq->ss_params.prio.next_spoke.tqe_prev = NULL;
483 	}
484 	if (holds_lock == 0) {
485 		SCTP_TCB_SEND_UNLOCK(stcb);
486 	}
487 	return;
488 }
489 
490 static struct sctp_stream_out *
491 sctp_ss_prio_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
492     struct sctp_association *asoc)
493 {
494 	struct sctp_stream_out *strq, *strqt, *strqn;
495 
496 	strqt = asoc->ss_data.last_out_stream;
497 prio_again:
498 	/* Find the next stream to use */
499 	if (strqt == NULL) {
500 		strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
501 	} else {
502 		strqn = TAILQ_NEXT(strqt, ss_params.prio.next_spoke);
503 		if (strqn != NULL &&
504 		    strqn->ss_params.prio.priority == strqt->ss_params.prio.priority) {
505 			strq = strqn;
506 		} else {
507 			strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
508 		}
509 	}
510 
511 	/*
512 	 * If CMT is off, we must validate that the stream in question has
513 	 * the first item pointed towards are network destination requested
514 	 * by the caller. Note that if we turn out to be locked to a stream
515 	 * (assigning TSN's then we must stop, since we cannot look for
516 	 * another stream with data to send to that destination). In CMT's
517 	 * case, by skipping this check, we will send one data packet
518 	 * towards the requested net.
519 	 */
520 	if (net != NULL && strq != NULL &&
521 	    SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
522 		if (TAILQ_FIRST(&strq->outqueue) &&
523 		    TAILQ_FIRST(&strq->outqueue)->net != NULL &&
524 		    TAILQ_FIRST(&strq->outqueue)->net != net) {
525 			if (strq == asoc->ss_data.last_out_stream) {
526 				return (NULL);
527 			} else {
528 				strqt = strq;
529 				goto prio_again;
530 			}
531 		}
532 	}
533 	return (strq);
534 }
535 
536 static int
537 sctp_ss_prio_get_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED,
538     struct sctp_stream_out *strq, uint16_t *value)
539 {
540 	if (strq == NULL) {
541 		return (-1);
542 	}
543 	*value = strq->ss_params.prio.priority;
544 	return (1);
545 }
546 
547 static int
548 sctp_ss_prio_set_value(struct sctp_tcb *stcb, struct sctp_association *asoc,
549     struct sctp_stream_out *strq, uint16_t value)
550 {
551 	if (strq == NULL) {
552 		return (-1);
553 	}
554 	strq->ss_params.prio.priority = value;
555 	sctp_ss_prio_remove(stcb, asoc, strq, NULL, 1);
556 	sctp_ss_prio_add(stcb, asoc, strq, NULL, 1);
557 	return (1);
558 }
559 
560 /*
561  * Fair bandwidth algorithm.
562  * Maintains an equal troughput per stream.
563  */
564 static void
565 sctp_ss_fb_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
566     int clear_values, int holds_lock)
567 {
568 	if (holds_lock == 0) {
569 		SCTP_TCB_SEND_LOCK(stcb);
570 	}
571 	while (!TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
572 		struct sctp_stream_out *strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
573 
574 		if (clear_values) {
575 			strq->ss_params.fb.rounds = -1;
576 		}
577 		TAILQ_REMOVE(&asoc->ss_data.out.wheel, TAILQ_FIRST(&asoc->ss_data.out.wheel), ss_params.fb.next_spoke);
578 		strq->ss_params.fb.next_spoke.tqe_next = NULL;
579 		strq->ss_params.fb.next_spoke.tqe_prev = NULL;
580 	}
581 	asoc->ss_data.last_out_stream = NULL;
582 	if (holds_lock == 0) {
583 		SCTP_TCB_SEND_UNLOCK(stcb);
584 	}
585 	return;
586 }
587 
588 static void
589 sctp_ss_fb_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
590 {
591 	if (with_strq != NULL) {
592 		if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
593 			stcb->asoc.ss_data.locked_on_sending = strq;
594 		}
595 		if (stcb->asoc.ss_data.last_out_stream == with_strq) {
596 			stcb->asoc.ss_data.last_out_stream = strq;
597 		}
598 	}
599 	strq->ss_params.fb.next_spoke.tqe_next = NULL;
600 	strq->ss_params.fb.next_spoke.tqe_prev = NULL;
601 	if (with_strq != NULL) {
602 		strq->ss_params.fb.rounds = with_strq->ss_params.fb.rounds;
603 	} else {
604 		strq->ss_params.fb.rounds = -1;
605 	}
606 	return;
607 }
608 
609 static void
610 sctp_ss_fb_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
611     struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED,
612     int holds_lock)
613 {
614 	if (holds_lock == 0) {
615 		SCTP_TCB_SEND_LOCK(stcb);
616 	}
617 	if (!TAILQ_EMPTY(&strq->outqueue) &&
618 	    (strq->ss_params.fb.next_spoke.tqe_next == NULL) &&
619 	    (strq->ss_params.fb.next_spoke.tqe_prev == NULL)) {
620 		if (strq->ss_params.fb.rounds < 0)
621 			strq->ss_params.fb.rounds = TAILQ_FIRST(&strq->outqueue)->length;
622 		TAILQ_INSERT_TAIL(&asoc->ss_data.out.wheel, strq, ss_params.fb.next_spoke);
623 	}
624 	if (holds_lock == 0) {
625 		SCTP_TCB_SEND_UNLOCK(stcb);
626 	}
627 	return;
628 }
629 
630 static void
631 sctp_ss_fb_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
632     struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED,
633     int holds_lock)
634 {
635 	if (holds_lock == 0) {
636 		SCTP_TCB_SEND_LOCK(stcb);
637 	}
638 	/*
639 	 * Remove from wheel if stream queue is empty and actually is on the
640 	 * wheel
641 	 */
642 	if (TAILQ_EMPTY(&strq->outqueue) &&
643 	    (strq->ss_params.fb.next_spoke.tqe_next != NULL ||
644 	    strq->ss_params.fb.next_spoke.tqe_prev != NULL)) {
645 		if (asoc->ss_data.last_out_stream == strq) {
646 			asoc->ss_data.last_out_stream = TAILQ_PREV(asoc->ss_data.last_out_stream, sctpwheel_listhead,
647 			    ss_params.fb.next_spoke);
648 			if (asoc->ss_data.last_out_stream == NULL) {
649 				asoc->ss_data.last_out_stream = TAILQ_LAST(&asoc->ss_data.out.wheel,
650 				    sctpwheel_listhead);
651 			}
652 			if (asoc->ss_data.last_out_stream == strq) {
653 				asoc->ss_data.last_out_stream = NULL;
654 			}
655 		}
656 		TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.fb.next_spoke);
657 		strq->ss_params.fb.next_spoke.tqe_next = NULL;
658 		strq->ss_params.fb.next_spoke.tqe_prev = NULL;
659 	}
660 	if (holds_lock == 0) {
661 		SCTP_TCB_SEND_UNLOCK(stcb);
662 	}
663 	return;
664 }
665 
666 static struct sctp_stream_out *
667 sctp_ss_fb_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
668     struct sctp_association *asoc)
669 {
670 	struct sctp_stream_out *strq = NULL, *strqt;
671 
672 	if (asoc->ss_data.last_out_stream == NULL ||
673 	    TAILQ_FIRST(&asoc->ss_data.out.wheel) == TAILQ_LAST(&asoc->ss_data.out.wheel, sctpwheel_listhead)) {
674 		strqt = TAILQ_FIRST(&asoc->ss_data.out.wheel);
675 	} else {
676 		strqt = TAILQ_NEXT(asoc->ss_data.last_out_stream, ss_params.fb.next_spoke);
677 	}
678 	do {
679 		if ((strqt != NULL) &&
680 		    ((SCTP_BASE_SYSCTL(sctp_cmt_on_off) > 0) ||
681 		    (SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0 &&
682 		    (net == NULL || (TAILQ_FIRST(&strqt->outqueue) && TAILQ_FIRST(&strqt->outqueue)->net == NULL) ||
683 		    (net != NULL && TAILQ_FIRST(&strqt->outqueue) && TAILQ_FIRST(&strqt->outqueue)->net != NULL &&
684 		    TAILQ_FIRST(&strqt->outqueue)->net == net))))) {
685 			if ((strqt->ss_params.fb.rounds >= 0) && (strq == NULL ||
686 			    strqt->ss_params.fb.rounds < strq->ss_params.fb.rounds)) {
687 				strq = strqt;
688 			}
689 		}
690 		if (strqt != NULL) {
691 			strqt = TAILQ_NEXT(strqt, ss_params.fb.next_spoke);
692 		} else {
693 			strqt = TAILQ_FIRST(&asoc->ss_data.out.wheel);
694 		}
695 	} while (strqt != strq);
696 	return (strq);
697 }
698 
699 static void
700 sctp_ss_fb_scheduled(struct sctp_tcb *stcb, struct sctp_nets *net SCTP_UNUSED,
701     struct sctp_association *asoc, struct sctp_stream_out *strq,
702     int moved_how_much SCTP_UNUSED)
703 {
704 	struct sctp_stream_queue_pending *sp;
705 	struct sctp_stream_out *strqt;
706 	int subtract;
707 
708 	if (stcb->asoc.idata_supported == 0) {
709 		sp = TAILQ_FIRST(&strq->outqueue);
710 		if ((sp != NULL) && (sp->some_taken == 1)) {
711 			stcb->asoc.ss_data.locked_on_sending = strq;
712 		} else {
713 			stcb->asoc.ss_data.locked_on_sending = NULL;
714 		}
715 	} else {
716 		stcb->asoc.ss_data.locked_on_sending = NULL;
717 	}
718 	subtract = strq->ss_params.fb.rounds;
719 	TAILQ_FOREACH(strqt, &asoc->ss_data.out.wheel, ss_params.fb.next_spoke) {
720 		strqt->ss_params.fb.rounds -= subtract;
721 		if (strqt->ss_params.fb.rounds < 0)
722 			strqt->ss_params.fb.rounds = 0;
723 	}
724 	if (TAILQ_FIRST(&strq->outqueue)) {
725 		strq->ss_params.fb.rounds = TAILQ_FIRST(&strq->outqueue)->length;
726 	} else {
727 		strq->ss_params.fb.rounds = -1;
728 	}
729 	asoc->ss_data.last_out_stream = strq;
730 	return;
731 }
732 
733 /*
734  * First-come, first-serve algorithm.
735  * Maintains the order provided by the application.
736  */
737 static void
738 sctp_ss_fcfs_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
739     struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp,
740     int holds_lock);
741 
742 static void
743 sctp_ss_fcfs_init(struct sctp_tcb *stcb, struct sctp_association *asoc,
744     int holds_lock)
745 {
746 	uint32_t x, n = 0, add_more = 1;
747 	struct sctp_stream_queue_pending *sp;
748 	uint16_t i;
749 
750 	TAILQ_INIT(&asoc->ss_data.out.list);
751 	/*
752 	 * If there is data in the stream queues already, the scheduler of
753 	 * an existing association has been changed. We can only cycle
754 	 * through the stream queues and add everything to the FCFS queue.
755 	 */
756 	while (add_more) {
757 		add_more = 0;
758 		for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
759 			sp = TAILQ_FIRST(&stcb->asoc.strmout[i].outqueue);
760 			x = 0;
761 			/* Find n. message in current stream queue */
762 			while (sp != NULL && x < n) {
763 				sp = TAILQ_NEXT(sp, next);
764 				x++;
765 			}
766 			if (sp != NULL) {
767 				sctp_ss_fcfs_add(stcb, &stcb->asoc, &stcb->asoc.strmout[i], sp, holds_lock);
768 				add_more = 1;
769 			}
770 		}
771 		n++;
772 	}
773 	return;
774 }
775 
776 static void
777 sctp_ss_fcfs_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
778     int clear_values, int holds_lock)
779 {
780 	if (clear_values) {
781 		if (holds_lock == 0) {
782 			SCTP_TCB_SEND_LOCK(stcb);
783 		}
784 		while (!TAILQ_EMPTY(&asoc->ss_data.out.list)) {
785 			TAILQ_REMOVE(&asoc->ss_data.out.list, TAILQ_FIRST(&asoc->ss_data.out.list), ss_next);
786 		}
787 		if (holds_lock == 0) {
788 			SCTP_TCB_SEND_UNLOCK(stcb);
789 		}
790 	}
791 	return;
792 }
793 
794 static void
795 sctp_ss_fcfs_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
796 {
797 	if (with_strq != NULL) {
798 		if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
799 			stcb->asoc.ss_data.locked_on_sending = strq;
800 		}
801 		if (stcb->asoc.ss_data.last_out_stream == with_strq) {
802 			stcb->asoc.ss_data.last_out_stream = strq;
803 		}
804 	}
805 	return;
806 }
807 
808 static void
809 sctp_ss_fcfs_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
810     struct sctp_stream_out *strq SCTP_UNUSED, struct sctp_stream_queue_pending *sp,
811     int holds_lock)
812 {
813 	if (holds_lock == 0) {
814 		SCTP_TCB_SEND_LOCK(stcb);
815 	}
816 	if (sp && (sp->ss_next.tqe_next == NULL) &&
817 	    (sp->ss_next.tqe_prev == NULL)) {
818 		TAILQ_INSERT_TAIL(&asoc->ss_data.out.list, sp, ss_next);
819 	}
820 	if (holds_lock == 0) {
821 		SCTP_TCB_SEND_UNLOCK(stcb);
822 	}
823 	return;
824 }
825 
826 static int
827 sctp_ss_fcfs_is_empty(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc)
828 {
829 	if (TAILQ_EMPTY(&asoc->ss_data.out.list)) {
830 		return (1);
831 	} else {
832 		return (0);
833 	}
834 }
835 
836 static void
837 sctp_ss_fcfs_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
838     struct sctp_stream_out *strq SCTP_UNUSED, struct sctp_stream_queue_pending *sp,
839     int holds_lock)
840 {
841 	if (holds_lock == 0) {
842 		SCTP_TCB_SEND_LOCK(stcb);
843 	}
844 	if (sp &&
845 	    ((sp->ss_next.tqe_next != NULL) ||
846 	    (sp->ss_next.tqe_prev != NULL))) {
847 		TAILQ_REMOVE(&asoc->ss_data.out.list, sp, ss_next);
848 	}
849 	if (holds_lock == 0) {
850 		SCTP_TCB_SEND_UNLOCK(stcb);
851 	}
852 	return;
853 }
854 
855 
856 static struct sctp_stream_out *
857 sctp_ss_fcfs_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
858     struct sctp_association *asoc)
859 {
860 	struct sctp_stream_out *strq;
861 	struct sctp_stream_queue_pending *sp;
862 
863 	sp = TAILQ_FIRST(&asoc->ss_data.out.list);
864 default_again:
865 	if (sp != NULL) {
866 		strq = &asoc->strmout[sp->sid];
867 	} else {
868 		strq = NULL;
869 	}
870 
871 	/*
872 	 * If CMT is off, we must validate that the stream in question has
873 	 * the first item pointed towards are network destination requested
874 	 * by the caller. Note that if we turn out to be locked to a stream
875 	 * (assigning TSN's then we must stop, since we cannot look for
876 	 * another stream with data to send to that destination). In CMT's
877 	 * case, by skipping this check, we will send one data packet
878 	 * towards the requested net.
879 	 */
880 	if (net != NULL && strq != NULL &&
881 	    SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
882 		if (TAILQ_FIRST(&strq->outqueue) &&
883 		    TAILQ_FIRST(&strq->outqueue)->net != NULL &&
884 		    TAILQ_FIRST(&strq->outqueue)->net != net) {
885 			sp = TAILQ_NEXT(sp, ss_next);
886 			goto default_again;
887 		}
888 	}
889 	return (strq);
890 }
891 
892 const struct sctp_ss_functions sctp_ss_functions[] = {
893 /* SCTP_SS_DEFAULT */
894 	{
895 		.sctp_ss_init = sctp_ss_default_init,
896 		.sctp_ss_clear = sctp_ss_default_clear,
897 		.sctp_ss_init_stream = sctp_ss_default_init_stream,
898 		.sctp_ss_add_to_stream = sctp_ss_default_add,
899 		.sctp_ss_is_empty = sctp_ss_default_is_empty,
900 		.sctp_ss_remove_from_stream = sctp_ss_default_remove,
901 		.sctp_ss_select_stream = sctp_ss_default_select,
902 		.sctp_ss_scheduled = sctp_ss_default_scheduled,
903 		.sctp_ss_packet_done = sctp_ss_default_packet_done,
904 		.sctp_ss_get_value = sctp_ss_default_get_value,
905 		.sctp_ss_set_value = sctp_ss_default_set_value,
906 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
907 	},
908 /* SCTP_SS_ROUND_ROBIN */
909 	{
910 		.sctp_ss_init = sctp_ss_default_init,
911 		.sctp_ss_clear = sctp_ss_default_clear,
912 		.sctp_ss_init_stream = sctp_ss_default_init_stream,
913 		.sctp_ss_add_to_stream = sctp_ss_rr_add,
914 		.sctp_ss_is_empty = sctp_ss_default_is_empty,
915 		.sctp_ss_remove_from_stream = sctp_ss_default_remove,
916 		.sctp_ss_select_stream = sctp_ss_default_select,
917 		.sctp_ss_scheduled = sctp_ss_default_scheduled,
918 		.sctp_ss_packet_done = sctp_ss_default_packet_done,
919 		.sctp_ss_get_value = sctp_ss_default_get_value,
920 		.sctp_ss_set_value = sctp_ss_default_set_value,
921 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
922 	},
923 /* SCTP_SS_ROUND_ROBIN_PACKET */
924 	{
925 		.sctp_ss_init = sctp_ss_default_init,
926 		.sctp_ss_clear = sctp_ss_default_clear,
927 		.sctp_ss_init_stream = sctp_ss_default_init_stream,
928 		.sctp_ss_add_to_stream = sctp_ss_rr_add,
929 		.sctp_ss_is_empty = sctp_ss_default_is_empty,
930 		.sctp_ss_remove_from_stream = sctp_ss_default_remove,
931 		.sctp_ss_select_stream = sctp_ss_rrp_select,
932 		.sctp_ss_scheduled = sctp_ss_default_scheduled,
933 		.sctp_ss_packet_done = sctp_ss_rrp_packet_done,
934 		.sctp_ss_get_value = sctp_ss_default_get_value,
935 		.sctp_ss_set_value = sctp_ss_default_set_value,
936 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
937 	},
938 /* SCTP_SS_PRIORITY */
939 	{
940 		.sctp_ss_init = sctp_ss_default_init,
941 		.sctp_ss_clear = sctp_ss_prio_clear,
942 		.sctp_ss_init_stream = sctp_ss_prio_init_stream,
943 		.sctp_ss_add_to_stream = sctp_ss_prio_add,
944 		.sctp_ss_is_empty = sctp_ss_default_is_empty,
945 		.sctp_ss_remove_from_stream = sctp_ss_prio_remove,
946 		.sctp_ss_select_stream = sctp_ss_prio_select,
947 		.sctp_ss_scheduled = sctp_ss_default_scheduled,
948 		.sctp_ss_packet_done = sctp_ss_default_packet_done,
949 		.sctp_ss_get_value = sctp_ss_prio_get_value,
950 		.sctp_ss_set_value = sctp_ss_prio_set_value,
951 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
952 	},
953 /* SCTP_SS_FAIR_BANDWITH */
954 	{
955 		.sctp_ss_init = sctp_ss_default_init,
956 		.sctp_ss_clear = sctp_ss_fb_clear,
957 		.sctp_ss_init_stream = sctp_ss_fb_init_stream,
958 		.sctp_ss_add_to_stream = sctp_ss_fb_add,
959 		.sctp_ss_is_empty = sctp_ss_default_is_empty,
960 		.sctp_ss_remove_from_stream = sctp_ss_fb_remove,
961 		.sctp_ss_select_stream = sctp_ss_fb_select,
962 		.sctp_ss_scheduled = sctp_ss_fb_scheduled,
963 		.sctp_ss_packet_done = sctp_ss_default_packet_done,
964 		.sctp_ss_get_value = sctp_ss_default_get_value,
965 		.sctp_ss_set_value = sctp_ss_default_set_value,
966 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
967 	},
968 /* SCTP_SS_FIRST_COME */
969 	{
970 		.sctp_ss_init = sctp_ss_fcfs_init,
971 		.sctp_ss_clear = sctp_ss_fcfs_clear,
972 		.sctp_ss_init_stream = sctp_ss_fcfs_init_stream,
973 		.sctp_ss_add_to_stream = sctp_ss_fcfs_add,
974 		.sctp_ss_is_empty = sctp_ss_fcfs_is_empty,
975 		.sctp_ss_remove_from_stream = sctp_ss_fcfs_remove,
976 		.sctp_ss_select_stream = sctp_ss_fcfs_select,
977 		.sctp_ss_scheduled = sctp_ss_default_scheduled,
978 		.sctp_ss_packet_done = sctp_ss_default_packet_done,
979 		.sctp_ss_get_value = sctp_ss_default_get_value,
980 		.sctp_ss_set_value = sctp_ss_default_set_value,
981 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
982 	}
983 };
984