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