xref: /freebsd/sys/netinet/sctp_ss_functions.c (revision c697fb7f)
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 
186 static struct sctp_stream_out *
187 sctp_ss_default_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
188     struct sctp_association *asoc)
189 {
190 	struct sctp_stream_out *strq, *strqt;
191 
192 	if (asoc->ss_data.locked_on_sending) {
193 		return (asoc->ss_data.locked_on_sending);
194 	}
195 	strqt = asoc->ss_data.last_out_stream;
196 default_again:
197 	/* Find the next stream to use */
198 	if (strqt == NULL) {
199 		strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
200 	} else {
201 		strq = TAILQ_NEXT(strqt, ss_params.rr.next_spoke);
202 		if (strq == NULL) {
203 			strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
204 		}
205 	}
206 
207 	/*
208 	 * If CMT is off, we must validate that the stream in question has
209 	 * the first item pointed towards are network destination requested
210 	 * by the caller. Note that if we turn out to be locked to a stream
211 	 * (assigning TSN's then we must stop, since we cannot look for
212 	 * another stream with data to send to that destination). In CMT's
213 	 * case, by skipping this check, we will send one data packet
214 	 * towards the requested net.
215 	 */
216 	if (net != NULL && strq != NULL &&
217 	    SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
218 		if (TAILQ_FIRST(&strq->outqueue) &&
219 		    TAILQ_FIRST(&strq->outqueue)->net != NULL &&
220 		    TAILQ_FIRST(&strq->outqueue)->net != net) {
221 			if (strq == asoc->ss_data.last_out_stream) {
222 				return (NULL);
223 			} else {
224 				strqt = strq;
225 				goto default_again;
226 			}
227 		}
228 	}
229 	return (strq);
230 }
231 
232 static void
233 sctp_ss_default_scheduled(struct sctp_tcb *stcb,
234     struct sctp_nets *net SCTP_UNUSED,
235     struct sctp_association *asoc,
236     struct sctp_stream_out *strq,
237     int moved_how_much SCTP_UNUSED)
238 {
239 	struct sctp_stream_queue_pending *sp;
240 
241 	asoc->ss_data.last_out_stream = strq;
242 	if (stcb->asoc.idata_supported == 0) {
243 		sp = TAILQ_FIRST(&strq->outqueue);
244 		if ((sp != NULL) && (sp->some_taken == 1)) {
245 			stcb->asoc.ss_data.locked_on_sending = strq;
246 		} else {
247 			stcb->asoc.ss_data.locked_on_sending = NULL;
248 		}
249 	} else {
250 		stcb->asoc.ss_data.locked_on_sending = NULL;
251 	}
252 	return;
253 }
254 
255 static void
256 sctp_ss_default_packet_done(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net SCTP_UNUSED,
257     struct sctp_association *asoc SCTP_UNUSED)
258 {
259 	/* Nothing to be done here */
260 	return;
261 }
262 
263 static int
264 sctp_ss_default_get_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED,
265     struct sctp_stream_out *strq SCTP_UNUSED, uint16_t *value SCTP_UNUSED)
266 {
267 	/* Nothing to be done here */
268 	return (-1);
269 }
270 
271 static int
272 sctp_ss_default_set_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED,
273     struct sctp_stream_out *strq SCTP_UNUSED, uint16_t value SCTP_UNUSED)
274 {
275 	/* Nothing to be done here */
276 	return (-1);
277 }
278 
279 static int
280 sctp_ss_default_is_user_msgs_incomplete(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc)
281 {
282 	struct sctp_stream_out *strq;
283 	struct sctp_stream_queue_pending *sp;
284 
285 	if (asoc->stream_queue_cnt != 1) {
286 		return (0);
287 	}
288 	strq = asoc->ss_data.locked_on_sending;
289 	if (strq == NULL) {
290 		return (0);
291 	}
292 	sp = TAILQ_FIRST(&strq->outqueue);
293 	if (sp == NULL) {
294 		return (0);
295 	}
296 	return (!sp->msg_is_complete);
297 }
298 
299 /*
300  * Real round-robin algorithm.
301  * Always interates the streams in ascending order.
302  */
303 static void
304 sctp_ss_rr_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
305     struct sctp_stream_out *strq,
306     struct sctp_stream_queue_pending *sp SCTP_UNUSED, int holds_lock)
307 {
308 	struct sctp_stream_out *strqt;
309 
310 	if (holds_lock == 0) {
311 		SCTP_TCB_SEND_LOCK(stcb);
312 	}
313 	if (!TAILQ_EMPTY(&strq->outqueue) &&
314 	    (strq->ss_params.rr.next_spoke.tqe_next == NULL) &&
315 	    (strq->ss_params.rr.next_spoke.tqe_prev == NULL)) {
316 		if (TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
317 			TAILQ_INSERT_HEAD(&asoc->ss_data.out.wheel, strq, ss_params.rr.next_spoke);
318 		} else {
319 			strqt = TAILQ_FIRST(&asoc->ss_data.out.wheel);
320 			while (strqt != NULL && (strqt->sid < strq->sid)) {
321 				strqt = TAILQ_NEXT(strqt, ss_params.rr.next_spoke);
322 			}
323 			if (strqt != NULL) {
324 				TAILQ_INSERT_BEFORE(strqt, strq, ss_params.rr.next_spoke);
325 			} else {
326 				TAILQ_INSERT_TAIL(&asoc->ss_data.out.wheel, strq, ss_params.rr.next_spoke);
327 			}
328 		}
329 	}
330 	if (holds_lock == 0) {
331 		SCTP_TCB_SEND_UNLOCK(stcb);
332 	}
333 	return;
334 }
335 
336 /*
337  * Real round-robin per packet algorithm.
338  * Always interates the streams in ascending order and
339  * only fills messages of the same stream in a packet.
340  */
341 static struct sctp_stream_out *
342 sctp_ss_rrp_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net SCTP_UNUSED,
343     struct sctp_association *asoc)
344 {
345 	return (asoc->ss_data.last_out_stream);
346 }
347 
348 static void
349 sctp_ss_rrp_packet_done(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
350     struct sctp_association *asoc)
351 {
352 	struct sctp_stream_out *strq, *strqt;
353 
354 	strqt = asoc->ss_data.last_out_stream;
355 rrp_again:
356 	/* Find the next stream to use */
357 	if (strqt == NULL) {
358 		strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
359 	} else {
360 		strq = TAILQ_NEXT(strqt, ss_params.rr.next_spoke);
361 		if (strq == NULL) {
362 			strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
363 		}
364 	}
365 
366 	/*
367 	 * If CMT is off, we must validate that the stream in question has
368 	 * the first item pointed towards are network destination requested
369 	 * by the caller. Note that if we turn out to be locked to a stream
370 	 * (assigning TSN's then we must stop, since we cannot look for
371 	 * another stream with data to send to that destination). In CMT's
372 	 * case, by skipping this check, we will send one data packet
373 	 * towards the requested net.
374 	 */
375 	if (net != NULL && strq != NULL &&
376 	    SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
377 		if (TAILQ_FIRST(&strq->outqueue) &&
378 		    TAILQ_FIRST(&strq->outqueue)->net != NULL &&
379 		    TAILQ_FIRST(&strq->outqueue)->net != net) {
380 			if (strq == asoc->ss_data.last_out_stream) {
381 				strq = NULL;
382 			} else {
383 				strqt = strq;
384 				goto rrp_again;
385 			}
386 		}
387 	}
388 	asoc->ss_data.last_out_stream = strq;
389 	return;
390 }
391 
392 
393 /*
394  * Priority algorithm.
395  * Always prefers streams based on their priority id.
396  */
397 static void
398 sctp_ss_prio_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
399     int clear_values, int holds_lock)
400 {
401 	if (holds_lock == 0) {
402 		SCTP_TCB_SEND_LOCK(stcb);
403 	}
404 	while (!TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
405 		struct sctp_stream_out *strq;
406 
407 		strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
408 		if (clear_values) {
409 			strq->ss_params.prio.priority = 0;
410 		}
411 		TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.prio.next_spoke);
412 		strq->ss_params.prio.next_spoke.tqe_next = NULL;
413 		strq->ss_params.prio.next_spoke.tqe_prev = NULL;
414 
415 	}
416 	asoc->ss_data.last_out_stream = NULL;
417 	if (holds_lock == 0) {
418 		SCTP_TCB_SEND_UNLOCK(stcb);
419 	}
420 	return;
421 }
422 
423 static void
424 sctp_ss_prio_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
425 {
426 	if (with_strq != NULL) {
427 		if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
428 			stcb->asoc.ss_data.locked_on_sending = strq;
429 		}
430 		if (stcb->asoc.ss_data.last_out_stream == with_strq) {
431 			stcb->asoc.ss_data.last_out_stream = strq;
432 		}
433 	}
434 	strq->ss_params.prio.next_spoke.tqe_next = NULL;
435 	strq->ss_params.prio.next_spoke.tqe_prev = NULL;
436 	if (with_strq != NULL) {
437 		strq->ss_params.prio.priority = with_strq->ss_params.prio.priority;
438 	} else {
439 		strq->ss_params.prio.priority = 0;
440 	}
441 	return;
442 }
443 
444 static void
445 sctp_ss_prio_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
446     struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED,
447     int holds_lock)
448 {
449 	struct sctp_stream_out *strqt;
450 
451 	if (holds_lock == 0) {
452 		SCTP_TCB_SEND_LOCK(stcb);
453 	}
454 	/* Add to wheel if not already on it and stream queue not empty */
455 	if (!TAILQ_EMPTY(&strq->outqueue) &&
456 	    (strq->ss_params.prio.next_spoke.tqe_next == NULL) &&
457 	    (strq->ss_params.prio.next_spoke.tqe_prev == NULL)) {
458 		if (TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
459 			TAILQ_INSERT_HEAD(&asoc->ss_data.out.wheel, strq, ss_params.prio.next_spoke);
460 		} else {
461 			strqt = TAILQ_FIRST(&asoc->ss_data.out.wheel);
462 			while (strqt != NULL && strqt->ss_params.prio.priority < strq->ss_params.prio.priority) {
463 				strqt = TAILQ_NEXT(strqt, ss_params.prio.next_spoke);
464 			}
465 			if (strqt != NULL) {
466 				TAILQ_INSERT_BEFORE(strqt, strq, ss_params.prio.next_spoke);
467 			} else {
468 				TAILQ_INSERT_TAIL(&asoc->ss_data.out.wheel, strq, ss_params.prio.next_spoke);
469 			}
470 		}
471 	}
472 	if (holds_lock == 0) {
473 		SCTP_TCB_SEND_UNLOCK(stcb);
474 	}
475 	return;
476 }
477 
478 static void
479 sctp_ss_prio_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
480     struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED,
481     int holds_lock)
482 {
483 	if (holds_lock == 0) {
484 		SCTP_TCB_SEND_LOCK(stcb);
485 	}
486 	/*
487 	 * Remove from wheel if stream queue is empty and actually is on the
488 	 * wheel
489 	 */
490 	if (TAILQ_EMPTY(&strq->outqueue) &&
491 	    (strq->ss_params.prio.next_spoke.tqe_next != NULL ||
492 	    strq->ss_params.prio.next_spoke.tqe_prev != NULL)) {
493 		if (asoc->ss_data.last_out_stream == strq) {
494 			asoc->ss_data.last_out_stream = TAILQ_PREV(asoc->ss_data.last_out_stream, sctpwheel_listhead,
495 			    ss_params.prio.next_spoke);
496 			if (asoc->ss_data.last_out_stream == NULL) {
497 				asoc->ss_data.last_out_stream = TAILQ_LAST(&asoc->ss_data.out.wheel,
498 				    sctpwheel_listhead);
499 			}
500 			if (asoc->ss_data.last_out_stream == strq) {
501 				asoc->ss_data.last_out_stream = NULL;
502 			}
503 		}
504 		TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.prio.next_spoke);
505 		strq->ss_params.prio.next_spoke.tqe_next = NULL;
506 		strq->ss_params.prio.next_spoke.tqe_prev = NULL;
507 	}
508 	if (holds_lock == 0) {
509 		SCTP_TCB_SEND_UNLOCK(stcb);
510 	}
511 	return;
512 }
513 
514 static struct sctp_stream_out *
515 sctp_ss_prio_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
516     struct sctp_association *asoc)
517 {
518 	struct sctp_stream_out *strq, *strqt, *strqn;
519 
520 	if (asoc->ss_data.locked_on_sending) {
521 		return (asoc->ss_data.locked_on_sending);
522 	}
523 	strqt = asoc->ss_data.last_out_stream;
524 prio_again:
525 	/* Find the next stream to use */
526 	if (strqt == NULL) {
527 		strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
528 	} else {
529 		strqn = TAILQ_NEXT(strqt, ss_params.prio.next_spoke);
530 		if (strqn != NULL &&
531 		    strqn->ss_params.prio.priority == strqt->ss_params.prio.priority) {
532 			strq = strqn;
533 		} else {
534 			strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
535 		}
536 	}
537 
538 	/*
539 	 * If CMT is off, we must validate that the stream in question has
540 	 * the first item pointed towards are network destination requested
541 	 * by the caller. Note that if we turn out to be locked to a stream
542 	 * (assigning TSN's then we must stop, since we cannot look for
543 	 * another stream with data to send to that destination). In CMT's
544 	 * case, by skipping this check, we will send one data packet
545 	 * towards the requested net.
546 	 */
547 	if (net != NULL && strq != NULL &&
548 	    SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
549 		if (TAILQ_FIRST(&strq->outqueue) &&
550 		    TAILQ_FIRST(&strq->outqueue)->net != NULL &&
551 		    TAILQ_FIRST(&strq->outqueue)->net != net) {
552 			if (strq == asoc->ss_data.last_out_stream) {
553 				return (NULL);
554 			} else {
555 				strqt = strq;
556 				goto prio_again;
557 			}
558 		}
559 	}
560 	return (strq);
561 }
562 
563 static int
564 sctp_ss_prio_get_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED,
565     struct sctp_stream_out *strq, uint16_t *value)
566 {
567 	if (strq == NULL) {
568 		return (-1);
569 	}
570 	*value = strq->ss_params.prio.priority;
571 	return (1);
572 }
573 
574 static int
575 sctp_ss_prio_set_value(struct sctp_tcb *stcb, struct sctp_association *asoc,
576     struct sctp_stream_out *strq, uint16_t value)
577 {
578 	if (strq == NULL) {
579 		return (-1);
580 	}
581 	strq->ss_params.prio.priority = value;
582 	sctp_ss_prio_remove(stcb, asoc, strq, NULL, 1);
583 	sctp_ss_prio_add(stcb, asoc, strq, NULL, 1);
584 	return (1);
585 }
586 
587 /*
588  * Fair bandwidth algorithm.
589  * Maintains an equal troughput per stream.
590  */
591 static void
592 sctp_ss_fb_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
593     int clear_values, int holds_lock)
594 {
595 	if (holds_lock == 0) {
596 		SCTP_TCB_SEND_LOCK(stcb);
597 	}
598 	while (!TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
599 		struct sctp_stream_out *strq;
600 
601 		strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
602 		if (clear_values) {
603 			strq->ss_params.fb.rounds = -1;
604 		}
605 		TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.fb.next_spoke);
606 		strq->ss_params.fb.next_spoke.tqe_next = NULL;
607 		strq->ss_params.fb.next_spoke.tqe_prev = NULL;
608 	}
609 	asoc->ss_data.last_out_stream = NULL;
610 	if (holds_lock == 0) {
611 		SCTP_TCB_SEND_UNLOCK(stcb);
612 	}
613 	return;
614 }
615 
616 static void
617 sctp_ss_fb_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
618 {
619 	if (with_strq != NULL) {
620 		if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
621 			stcb->asoc.ss_data.locked_on_sending = strq;
622 		}
623 		if (stcb->asoc.ss_data.last_out_stream == with_strq) {
624 			stcb->asoc.ss_data.last_out_stream = strq;
625 		}
626 	}
627 	strq->ss_params.fb.next_spoke.tqe_next = NULL;
628 	strq->ss_params.fb.next_spoke.tqe_prev = NULL;
629 	if (with_strq != NULL) {
630 		strq->ss_params.fb.rounds = with_strq->ss_params.fb.rounds;
631 	} else {
632 		strq->ss_params.fb.rounds = -1;
633 	}
634 	return;
635 }
636 
637 static void
638 sctp_ss_fb_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
639     struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED,
640     int holds_lock)
641 {
642 	if (holds_lock == 0) {
643 		SCTP_TCB_SEND_LOCK(stcb);
644 	}
645 	if (!TAILQ_EMPTY(&strq->outqueue) &&
646 	    (strq->ss_params.fb.next_spoke.tqe_next == NULL) &&
647 	    (strq->ss_params.fb.next_spoke.tqe_prev == NULL)) {
648 		if (strq->ss_params.fb.rounds < 0)
649 			strq->ss_params.fb.rounds = TAILQ_FIRST(&strq->outqueue)->length;
650 		TAILQ_INSERT_TAIL(&asoc->ss_data.out.wheel, strq, ss_params.fb.next_spoke);
651 	}
652 	if (holds_lock == 0) {
653 		SCTP_TCB_SEND_UNLOCK(stcb);
654 	}
655 	return;
656 }
657 
658 static void
659 sctp_ss_fb_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
660     struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED,
661     int holds_lock)
662 {
663 	if (holds_lock == 0) {
664 		SCTP_TCB_SEND_LOCK(stcb);
665 	}
666 	/*
667 	 * Remove from wheel if stream queue is empty and actually is on the
668 	 * wheel
669 	 */
670 	if (TAILQ_EMPTY(&strq->outqueue) &&
671 	    (strq->ss_params.fb.next_spoke.tqe_next != NULL ||
672 	    strq->ss_params.fb.next_spoke.tqe_prev != NULL)) {
673 		if (asoc->ss_data.last_out_stream == strq) {
674 			asoc->ss_data.last_out_stream = TAILQ_PREV(asoc->ss_data.last_out_stream, sctpwheel_listhead,
675 			    ss_params.fb.next_spoke);
676 			if (asoc->ss_data.last_out_stream == NULL) {
677 				asoc->ss_data.last_out_stream = TAILQ_LAST(&asoc->ss_data.out.wheel,
678 				    sctpwheel_listhead);
679 			}
680 			if (asoc->ss_data.last_out_stream == strq) {
681 				asoc->ss_data.last_out_stream = NULL;
682 			}
683 		}
684 		TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.fb.next_spoke);
685 		strq->ss_params.fb.next_spoke.tqe_next = NULL;
686 		strq->ss_params.fb.next_spoke.tqe_prev = NULL;
687 	}
688 	if (holds_lock == 0) {
689 		SCTP_TCB_SEND_UNLOCK(stcb);
690 	}
691 	return;
692 }
693 
694 static struct sctp_stream_out *
695 sctp_ss_fb_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
696     struct sctp_association *asoc)
697 {
698 	struct sctp_stream_out *strq = NULL, *strqt;
699 
700 	if (asoc->ss_data.locked_on_sending) {
701 		return (asoc->ss_data.locked_on_sending);
702 	}
703 	if (asoc->ss_data.last_out_stream == NULL ||
704 	    TAILQ_FIRST(&asoc->ss_data.out.wheel) == TAILQ_LAST(&asoc->ss_data.out.wheel, sctpwheel_listhead)) {
705 		strqt = TAILQ_FIRST(&asoc->ss_data.out.wheel);
706 	} else {
707 		strqt = TAILQ_NEXT(asoc->ss_data.last_out_stream, ss_params.fb.next_spoke);
708 	}
709 	do {
710 		if ((strqt != NULL) &&
711 		    ((SCTP_BASE_SYSCTL(sctp_cmt_on_off) > 0) ||
712 		    (SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0 &&
713 		    (net == NULL || (TAILQ_FIRST(&strqt->outqueue) && TAILQ_FIRST(&strqt->outqueue)->net == NULL) ||
714 		    (net != NULL && TAILQ_FIRST(&strqt->outqueue) && TAILQ_FIRST(&strqt->outqueue)->net != NULL &&
715 		    TAILQ_FIRST(&strqt->outqueue)->net == net))))) {
716 			if ((strqt->ss_params.fb.rounds >= 0) && (strq == NULL ||
717 			    strqt->ss_params.fb.rounds < strq->ss_params.fb.rounds)) {
718 				strq = strqt;
719 			}
720 		}
721 		if (strqt != NULL) {
722 			strqt = TAILQ_NEXT(strqt, ss_params.fb.next_spoke);
723 		} else {
724 			strqt = TAILQ_FIRST(&asoc->ss_data.out.wheel);
725 		}
726 	} while (strqt != strq);
727 	return (strq);
728 }
729 
730 static void
731 sctp_ss_fb_scheduled(struct sctp_tcb *stcb, struct sctp_nets *net SCTP_UNUSED,
732     struct sctp_association *asoc, struct sctp_stream_out *strq,
733     int moved_how_much SCTP_UNUSED)
734 {
735 	struct sctp_stream_queue_pending *sp;
736 	struct sctp_stream_out *strqt;
737 	int subtract;
738 
739 	if (stcb->asoc.idata_supported == 0) {
740 		sp = TAILQ_FIRST(&strq->outqueue);
741 		if ((sp != NULL) && (sp->some_taken == 1)) {
742 			stcb->asoc.ss_data.locked_on_sending = strq;
743 		} else {
744 			stcb->asoc.ss_data.locked_on_sending = NULL;
745 		}
746 	} else {
747 		stcb->asoc.ss_data.locked_on_sending = NULL;
748 	}
749 	subtract = strq->ss_params.fb.rounds;
750 	TAILQ_FOREACH(strqt, &asoc->ss_data.out.wheel, ss_params.fb.next_spoke) {
751 		strqt->ss_params.fb.rounds -= subtract;
752 		if (strqt->ss_params.fb.rounds < 0)
753 			strqt->ss_params.fb.rounds = 0;
754 	}
755 	if (TAILQ_FIRST(&strq->outqueue)) {
756 		strq->ss_params.fb.rounds = TAILQ_FIRST(&strq->outqueue)->length;
757 	} else {
758 		strq->ss_params.fb.rounds = -1;
759 	}
760 	asoc->ss_data.last_out_stream = strq;
761 	return;
762 }
763 
764 /*
765  * First-come, first-serve algorithm.
766  * Maintains the order provided by the application.
767  */
768 static void
769 sctp_ss_fcfs_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
770     struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp,
771     int holds_lock);
772 
773 static void
774 sctp_ss_fcfs_init(struct sctp_tcb *stcb, struct sctp_association *asoc,
775     int holds_lock)
776 {
777 	uint32_t x, n = 0, add_more = 1;
778 	struct sctp_stream_queue_pending *sp;
779 	uint16_t i;
780 
781 	if (holds_lock == 0) {
782 		SCTP_TCB_SEND_LOCK(stcb);
783 	}
784 	TAILQ_INIT(&asoc->ss_data.out.list);
785 	/*
786 	 * If there is data in the stream queues already, the scheduler of
787 	 * an existing association has been changed. We can only cycle
788 	 * through the stream queues and add everything to the FCFS queue.
789 	 */
790 	while (add_more) {
791 		add_more = 0;
792 		for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
793 			sp = TAILQ_FIRST(&stcb->asoc.strmout[i].outqueue);
794 			x = 0;
795 			/* Find n. message in current stream queue */
796 			while (sp != NULL && x < n) {
797 				sp = TAILQ_NEXT(sp, next);
798 				x++;
799 			}
800 			if (sp != NULL) {
801 				sctp_ss_fcfs_add(stcb, &stcb->asoc, &stcb->asoc.strmout[i], sp, 1);
802 				add_more = 1;
803 			}
804 		}
805 		n++;
806 	}
807 	if (holds_lock == 0) {
808 		SCTP_TCB_SEND_UNLOCK(stcb);
809 	}
810 	return;
811 }
812 
813 static void
814 sctp_ss_fcfs_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
815     int clear_values, int holds_lock)
816 {
817 	struct sctp_stream_queue_pending *sp;
818 
819 	if (clear_values) {
820 		if (holds_lock == 0) {
821 			SCTP_TCB_SEND_LOCK(stcb);
822 		}
823 		while (!TAILQ_EMPTY(&asoc->ss_data.out.list)) {
824 			sp = TAILQ_FIRST(&asoc->ss_data.out.list);
825 			TAILQ_REMOVE(&asoc->ss_data.out.list, sp, ss_next);
826 			sp->ss_next.tqe_next = NULL;
827 			sp->ss_next.tqe_prev = NULL;
828 		}
829 		if (holds_lock == 0) {
830 			SCTP_TCB_SEND_UNLOCK(stcb);
831 		}
832 	}
833 	return;
834 }
835 
836 static void
837 sctp_ss_fcfs_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
838 {
839 	if (with_strq != NULL) {
840 		if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
841 			stcb->asoc.ss_data.locked_on_sending = strq;
842 		}
843 		if (stcb->asoc.ss_data.last_out_stream == with_strq) {
844 			stcb->asoc.ss_data.last_out_stream = strq;
845 		}
846 	}
847 	strq->ss_params.fb.next_spoke.tqe_next = NULL;
848 	strq->ss_params.fb.next_spoke.tqe_prev = NULL;
849 	return;
850 }
851 
852 static void
853 sctp_ss_fcfs_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
854     struct sctp_stream_out *strq SCTP_UNUSED, struct sctp_stream_queue_pending *sp,
855     int holds_lock)
856 {
857 	if (holds_lock == 0) {
858 		SCTP_TCB_SEND_LOCK(stcb);
859 	}
860 	if (sp && (sp->ss_next.tqe_next == NULL) &&
861 	    (sp->ss_next.tqe_prev == NULL)) {
862 		TAILQ_INSERT_TAIL(&asoc->ss_data.out.list, sp, ss_next);
863 	}
864 	if (holds_lock == 0) {
865 		SCTP_TCB_SEND_UNLOCK(stcb);
866 	}
867 	return;
868 }
869 
870 static int
871 sctp_ss_fcfs_is_empty(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc)
872 {
873 	if (TAILQ_EMPTY(&asoc->ss_data.out.list)) {
874 		return (1);
875 	} else {
876 		return (0);
877 	}
878 }
879 
880 static void
881 sctp_ss_fcfs_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
882     struct sctp_stream_out *strq SCTP_UNUSED, struct sctp_stream_queue_pending *sp,
883     int holds_lock)
884 {
885 	if (holds_lock == 0) {
886 		SCTP_TCB_SEND_LOCK(stcb);
887 	}
888 	if (sp &&
889 	    ((sp->ss_next.tqe_next != NULL) ||
890 	    (sp->ss_next.tqe_prev != NULL))) {
891 		TAILQ_REMOVE(&asoc->ss_data.out.list, sp, ss_next);
892 		sp->ss_next.tqe_next = NULL;
893 		sp->ss_next.tqe_prev = NULL;
894 	}
895 	if (holds_lock == 0) {
896 		SCTP_TCB_SEND_UNLOCK(stcb);
897 	}
898 	return;
899 }
900 
901 
902 static struct sctp_stream_out *
903 sctp_ss_fcfs_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
904     struct sctp_association *asoc)
905 {
906 	struct sctp_stream_out *strq;
907 	struct sctp_stream_queue_pending *sp;
908 
909 	if (asoc->ss_data.locked_on_sending) {
910 		return (asoc->ss_data.locked_on_sending);
911 	}
912 	sp = TAILQ_FIRST(&asoc->ss_data.out.list);
913 default_again:
914 	if (sp != NULL) {
915 		strq = &asoc->strmout[sp->sid];
916 	} else {
917 		strq = NULL;
918 	}
919 
920 	/*
921 	 * If CMT is off, we must validate that the stream in question has
922 	 * the first item pointed towards are network destination requested
923 	 * by the caller. Note that if we turn out to be locked to a stream
924 	 * (assigning TSN's then we must stop, since we cannot look for
925 	 * another stream with data to send to that destination). In CMT's
926 	 * case, by skipping this check, we will send one data packet
927 	 * towards the requested net.
928 	 */
929 	if (net != NULL && strq != NULL &&
930 	    SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
931 		if (TAILQ_FIRST(&strq->outqueue) &&
932 		    TAILQ_FIRST(&strq->outqueue)->net != NULL &&
933 		    TAILQ_FIRST(&strq->outqueue)->net != net) {
934 			sp = TAILQ_NEXT(sp, ss_next);
935 			goto default_again;
936 		}
937 	}
938 	return (strq);
939 }
940 
941 const struct sctp_ss_functions sctp_ss_functions[] = {
942 /* SCTP_SS_DEFAULT */
943 	{
944 		.sctp_ss_init = sctp_ss_default_init,
945 		.sctp_ss_clear = sctp_ss_default_clear,
946 		.sctp_ss_init_stream = sctp_ss_default_init_stream,
947 		.sctp_ss_add_to_stream = sctp_ss_default_add,
948 		.sctp_ss_is_empty = sctp_ss_default_is_empty,
949 		.sctp_ss_remove_from_stream = sctp_ss_default_remove,
950 		.sctp_ss_select_stream = sctp_ss_default_select,
951 		.sctp_ss_scheduled = sctp_ss_default_scheduled,
952 		.sctp_ss_packet_done = sctp_ss_default_packet_done,
953 		.sctp_ss_get_value = sctp_ss_default_get_value,
954 		.sctp_ss_set_value = sctp_ss_default_set_value,
955 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
956 	},
957 /* SCTP_SS_ROUND_ROBIN */
958 	{
959 		.sctp_ss_init = sctp_ss_default_init,
960 		.sctp_ss_clear = sctp_ss_default_clear,
961 		.sctp_ss_init_stream = sctp_ss_default_init_stream,
962 		.sctp_ss_add_to_stream = sctp_ss_rr_add,
963 		.sctp_ss_is_empty = sctp_ss_default_is_empty,
964 		.sctp_ss_remove_from_stream = sctp_ss_default_remove,
965 		.sctp_ss_select_stream = sctp_ss_default_select,
966 		.sctp_ss_scheduled = sctp_ss_default_scheduled,
967 		.sctp_ss_packet_done = sctp_ss_default_packet_done,
968 		.sctp_ss_get_value = sctp_ss_default_get_value,
969 		.sctp_ss_set_value = sctp_ss_default_set_value,
970 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
971 	},
972 /* SCTP_SS_ROUND_ROBIN_PACKET */
973 	{
974 		.sctp_ss_init = sctp_ss_default_init,
975 		.sctp_ss_clear = sctp_ss_default_clear,
976 		.sctp_ss_init_stream = sctp_ss_default_init_stream,
977 		.sctp_ss_add_to_stream = sctp_ss_rr_add,
978 		.sctp_ss_is_empty = sctp_ss_default_is_empty,
979 		.sctp_ss_remove_from_stream = sctp_ss_default_remove,
980 		.sctp_ss_select_stream = sctp_ss_rrp_select,
981 		.sctp_ss_scheduled = sctp_ss_default_scheduled,
982 		.sctp_ss_packet_done = sctp_ss_rrp_packet_done,
983 		.sctp_ss_get_value = sctp_ss_default_get_value,
984 		.sctp_ss_set_value = sctp_ss_default_set_value,
985 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
986 	},
987 /* SCTP_SS_PRIORITY */
988 	{
989 		.sctp_ss_init = sctp_ss_default_init,
990 		.sctp_ss_clear = sctp_ss_prio_clear,
991 		.sctp_ss_init_stream = sctp_ss_prio_init_stream,
992 		.sctp_ss_add_to_stream = sctp_ss_prio_add,
993 		.sctp_ss_is_empty = sctp_ss_default_is_empty,
994 		.sctp_ss_remove_from_stream = sctp_ss_prio_remove,
995 		.sctp_ss_select_stream = sctp_ss_prio_select,
996 		.sctp_ss_scheduled = sctp_ss_default_scheduled,
997 		.sctp_ss_packet_done = sctp_ss_default_packet_done,
998 		.sctp_ss_get_value = sctp_ss_prio_get_value,
999 		.sctp_ss_set_value = sctp_ss_prio_set_value,
1000 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
1001 	},
1002 /* SCTP_SS_FAIR_BANDWITH */
1003 	{
1004 		.sctp_ss_init = sctp_ss_default_init,
1005 		.sctp_ss_clear = sctp_ss_fb_clear,
1006 		.sctp_ss_init_stream = sctp_ss_fb_init_stream,
1007 		.sctp_ss_add_to_stream = sctp_ss_fb_add,
1008 		.sctp_ss_is_empty = sctp_ss_default_is_empty,
1009 		.sctp_ss_remove_from_stream = sctp_ss_fb_remove,
1010 		.sctp_ss_select_stream = sctp_ss_fb_select,
1011 		.sctp_ss_scheduled = sctp_ss_fb_scheduled,
1012 		.sctp_ss_packet_done = sctp_ss_default_packet_done,
1013 		.sctp_ss_get_value = sctp_ss_default_get_value,
1014 		.sctp_ss_set_value = sctp_ss_default_set_value,
1015 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
1016 	},
1017 /* SCTP_SS_FIRST_COME */
1018 	{
1019 		.sctp_ss_init = sctp_ss_fcfs_init,
1020 		.sctp_ss_clear = sctp_ss_fcfs_clear,
1021 		.sctp_ss_init_stream = sctp_ss_fcfs_init_stream,
1022 		.sctp_ss_add_to_stream = sctp_ss_fcfs_add,
1023 		.sctp_ss_is_empty = sctp_ss_fcfs_is_empty,
1024 		.sctp_ss_remove_from_stream = sctp_ss_fcfs_remove,
1025 		.sctp_ss_select_stream = sctp_ss_fcfs_select,
1026 		.sctp_ss_scheduled = sctp_ss_default_scheduled,
1027 		.sctp_ss_packet_done = sctp_ss_default_packet_done,
1028 		.sctp_ss_get_value = sctp_ss_default_get_value,
1029 		.sctp_ss_set_value = sctp_ss_default_set_value,
1030 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
1031 	}
1032 };
1033