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