xref: /freebsd/sys/netinet/sctp_ss_functions.c (revision e19d93b19dce276bdf178bb6a449728238d1c6f8)
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 throughput 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 SCTP_UNUSED, int holds_lock)
813 {
814 	struct sctp_stream_queue_pending *sp;
815 
816 	if (holds_lock == 0) {
817 		SCTP_TCB_SEND_LOCK(stcb);
818 	}
819 	while (!TAILQ_EMPTY(&asoc->ss_data.out.list)) {
820 		sp = TAILQ_FIRST(&asoc->ss_data.out.list);
821 		TAILQ_REMOVE(&asoc->ss_data.out.list, sp, ss_next);
822 		sp->ss_next.tqe_next = NULL;
823 		sp->ss_next.tqe_prev = NULL;
824 	}
825 	asoc->ss_data.last_out_stream = NULL;
826 	if (holds_lock == 0) {
827 		SCTP_TCB_SEND_UNLOCK(stcb);
828 	}
829 	return;
830 }
831 
832 static void
833 sctp_ss_fcfs_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
834 {
835 	if (with_strq != NULL) {
836 		if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
837 			stcb->asoc.ss_data.locked_on_sending = strq;
838 		}
839 		if (stcb->asoc.ss_data.last_out_stream == with_strq) {
840 			stcb->asoc.ss_data.last_out_stream = strq;
841 		}
842 	}
843 	strq->ss_params.fb.next_spoke.tqe_next = NULL;
844 	strq->ss_params.fb.next_spoke.tqe_prev = NULL;
845 	return;
846 }
847 
848 static void
849 sctp_ss_fcfs_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
850     struct sctp_stream_out *strq SCTP_UNUSED, struct sctp_stream_queue_pending *sp,
851     int holds_lock)
852 {
853 	if (holds_lock == 0) {
854 		SCTP_TCB_SEND_LOCK(stcb);
855 	}
856 	if (sp && (sp->ss_next.tqe_next == NULL) &&
857 	    (sp->ss_next.tqe_prev == NULL)) {
858 		TAILQ_INSERT_TAIL(&asoc->ss_data.out.list, sp, ss_next);
859 	}
860 	if (holds_lock == 0) {
861 		SCTP_TCB_SEND_UNLOCK(stcb);
862 	}
863 	return;
864 }
865 
866 static int
867 sctp_ss_fcfs_is_empty(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc)
868 {
869 	if (TAILQ_EMPTY(&asoc->ss_data.out.list)) {
870 		return (1);
871 	} else {
872 		return (0);
873 	}
874 }
875 
876 static void
877 sctp_ss_fcfs_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
878     struct sctp_stream_out *strq SCTP_UNUSED, struct sctp_stream_queue_pending *sp,
879     int holds_lock)
880 {
881 	if (holds_lock == 0) {
882 		SCTP_TCB_SEND_LOCK(stcb);
883 	}
884 	if (sp &&
885 	    ((sp->ss_next.tqe_next != NULL) ||
886 	    (sp->ss_next.tqe_prev != NULL))) {
887 		TAILQ_REMOVE(&asoc->ss_data.out.list, sp, ss_next);
888 		sp->ss_next.tqe_next = NULL;
889 		sp->ss_next.tqe_prev = NULL;
890 	}
891 	if (holds_lock == 0) {
892 		SCTP_TCB_SEND_UNLOCK(stcb);
893 	}
894 	return;
895 }
896 
897 static struct sctp_stream_out *
898 sctp_ss_fcfs_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
899     struct sctp_association *asoc)
900 {
901 	struct sctp_stream_out *strq;
902 	struct sctp_stream_queue_pending *sp;
903 
904 	if (asoc->ss_data.locked_on_sending) {
905 		return (asoc->ss_data.locked_on_sending);
906 	}
907 	sp = TAILQ_FIRST(&asoc->ss_data.out.list);
908 default_again:
909 	if (sp != NULL) {
910 		strq = &asoc->strmout[sp->sid];
911 	} else {
912 		strq = NULL;
913 	}
914 
915 	/*
916 	 * If CMT is off, we must validate that the stream in question has
917 	 * the first item pointed towards are network destination requested
918 	 * by the caller. Note that if we turn out to be locked to a stream
919 	 * (assigning TSN's then we must stop, since we cannot look for
920 	 * another stream with data to send to that destination). In CMT's
921 	 * case, by skipping this check, we will send one data packet
922 	 * towards the requested net.
923 	 */
924 	if (net != NULL && strq != NULL &&
925 	    SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
926 		if (TAILQ_FIRST(&strq->outqueue) &&
927 		    TAILQ_FIRST(&strq->outqueue)->net != NULL &&
928 		    TAILQ_FIRST(&strq->outqueue)->net != net) {
929 			sp = TAILQ_NEXT(sp, ss_next);
930 			goto default_again;
931 		}
932 	}
933 	return (strq);
934 }
935 
936 const struct sctp_ss_functions sctp_ss_functions[] = {
937 /* SCTP_SS_DEFAULT */
938 	{
939 		.sctp_ss_init = sctp_ss_default_init,
940 		.sctp_ss_clear = sctp_ss_default_clear,
941 		.sctp_ss_init_stream = sctp_ss_default_init_stream,
942 		.sctp_ss_add_to_stream = sctp_ss_default_add,
943 		.sctp_ss_is_empty = sctp_ss_default_is_empty,
944 		.sctp_ss_remove_from_stream = sctp_ss_default_remove,
945 		.sctp_ss_select_stream = sctp_ss_default_select,
946 		.sctp_ss_scheduled = sctp_ss_default_scheduled,
947 		.sctp_ss_packet_done = sctp_ss_default_packet_done,
948 		.sctp_ss_get_value = sctp_ss_default_get_value,
949 		.sctp_ss_set_value = sctp_ss_default_set_value,
950 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
951 	},
952 /* SCTP_SS_ROUND_ROBIN */
953 	{
954 		.sctp_ss_init = sctp_ss_default_init,
955 		.sctp_ss_clear = sctp_ss_default_clear,
956 		.sctp_ss_init_stream = sctp_ss_default_init_stream,
957 		.sctp_ss_add_to_stream = sctp_ss_rr_add,
958 		.sctp_ss_is_empty = sctp_ss_default_is_empty,
959 		.sctp_ss_remove_from_stream = sctp_ss_default_remove,
960 		.sctp_ss_select_stream = sctp_ss_default_select,
961 		.sctp_ss_scheduled = sctp_ss_default_scheduled,
962 		.sctp_ss_packet_done = sctp_ss_default_packet_done,
963 		.sctp_ss_get_value = sctp_ss_default_get_value,
964 		.sctp_ss_set_value = sctp_ss_default_set_value,
965 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
966 	},
967 /* SCTP_SS_ROUND_ROBIN_PACKET */
968 	{
969 		.sctp_ss_init = sctp_ss_default_init,
970 		.sctp_ss_clear = sctp_ss_default_clear,
971 		.sctp_ss_init_stream = sctp_ss_default_init_stream,
972 		.sctp_ss_add_to_stream = sctp_ss_rr_add,
973 		.sctp_ss_is_empty = sctp_ss_default_is_empty,
974 		.sctp_ss_remove_from_stream = sctp_ss_default_remove,
975 		.sctp_ss_select_stream = sctp_ss_rrp_select,
976 		.sctp_ss_scheduled = sctp_ss_default_scheduled,
977 		.sctp_ss_packet_done = sctp_ss_rrp_packet_done,
978 		.sctp_ss_get_value = sctp_ss_default_get_value,
979 		.sctp_ss_set_value = sctp_ss_default_set_value,
980 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
981 	},
982 /* SCTP_SS_PRIORITY */
983 	{
984 		.sctp_ss_init = sctp_ss_default_init,
985 		.sctp_ss_clear = sctp_ss_prio_clear,
986 		.sctp_ss_init_stream = sctp_ss_prio_init_stream,
987 		.sctp_ss_add_to_stream = sctp_ss_prio_add,
988 		.sctp_ss_is_empty = sctp_ss_default_is_empty,
989 		.sctp_ss_remove_from_stream = sctp_ss_prio_remove,
990 		.sctp_ss_select_stream = sctp_ss_prio_select,
991 		.sctp_ss_scheduled = sctp_ss_default_scheduled,
992 		.sctp_ss_packet_done = sctp_ss_default_packet_done,
993 		.sctp_ss_get_value = sctp_ss_prio_get_value,
994 		.sctp_ss_set_value = sctp_ss_prio_set_value,
995 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
996 	},
997 /* SCTP_SS_FAIR_BANDWITH */
998 	{
999 		.sctp_ss_init = sctp_ss_default_init,
1000 		.sctp_ss_clear = sctp_ss_fb_clear,
1001 		.sctp_ss_init_stream = sctp_ss_fb_init_stream,
1002 		.sctp_ss_add_to_stream = sctp_ss_fb_add,
1003 		.sctp_ss_is_empty = sctp_ss_default_is_empty,
1004 		.sctp_ss_remove_from_stream = sctp_ss_fb_remove,
1005 		.sctp_ss_select_stream = sctp_ss_fb_select,
1006 		.sctp_ss_scheduled = sctp_ss_fb_scheduled,
1007 		.sctp_ss_packet_done = sctp_ss_default_packet_done,
1008 		.sctp_ss_get_value = sctp_ss_default_get_value,
1009 		.sctp_ss_set_value = sctp_ss_default_set_value,
1010 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
1011 	},
1012 /* SCTP_SS_FIRST_COME */
1013 	{
1014 		.sctp_ss_init = sctp_ss_fcfs_init,
1015 		.sctp_ss_clear = sctp_ss_fcfs_clear,
1016 		.sctp_ss_init_stream = sctp_ss_fcfs_init_stream,
1017 		.sctp_ss_add_to_stream = sctp_ss_fcfs_add,
1018 		.sctp_ss_is_empty = sctp_ss_fcfs_is_empty,
1019 		.sctp_ss_remove_from_stream = sctp_ss_fcfs_remove,
1020 		.sctp_ss_select_stream = sctp_ss_fcfs_select,
1021 		.sctp_ss_scheduled = sctp_ss_default_scheduled,
1022 		.sctp_ss_packet_done = sctp_ss_default_packet_done,
1023 		.sctp_ss_get_value = sctp_ss_default_get_value,
1024 		.sctp_ss_set_value = sctp_ss_default_set_value,
1025 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
1026 	}
1027 };
1028