xref: /freebsd/sys/netinet/sctp_ss_functions.c (revision b9f654b163bce26de79705e77b872427c9f2afa1)
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 	strqt = asoc->ss_data.last_out_stream;
521 prio_again:
522 	/* Find the next stream to use */
523 	if (strqt == NULL) {
524 		strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
525 	} else {
526 		strqn = TAILQ_NEXT(strqt, ss_params.prio.next_spoke);
527 		if (strqn != NULL &&
528 		    strqn->ss_params.prio.priority == strqt->ss_params.prio.priority) {
529 			strq = strqn;
530 		} else {
531 			strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
532 		}
533 	}
534 
535 	/*
536 	 * If CMT is off, we must validate that the stream in question has
537 	 * the first item pointed towards are network destination requested
538 	 * by the caller. Note that if we turn out to be locked to a stream
539 	 * (assigning TSN's then we must stop, since we cannot look for
540 	 * another stream with data to send to that destination). In CMT's
541 	 * case, by skipping this check, we will send one data packet
542 	 * towards the requested net.
543 	 */
544 	if (net != NULL && strq != NULL &&
545 	    SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
546 		if (TAILQ_FIRST(&strq->outqueue) &&
547 		    TAILQ_FIRST(&strq->outqueue)->net != NULL &&
548 		    TAILQ_FIRST(&strq->outqueue)->net != net) {
549 			if (strq == asoc->ss_data.last_out_stream) {
550 				return (NULL);
551 			} else {
552 				strqt = strq;
553 				goto prio_again;
554 			}
555 		}
556 	}
557 	return (strq);
558 }
559 
560 static int
561 sctp_ss_prio_get_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED,
562     struct sctp_stream_out *strq, uint16_t *value)
563 {
564 	if (strq == NULL) {
565 		return (-1);
566 	}
567 	*value = strq->ss_params.prio.priority;
568 	return (1);
569 }
570 
571 static int
572 sctp_ss_prio_set_value(struct sctp_tcb *stcb, struct sctp_association *asoc,
573     struct sctp_stream_out *strq, uint16_t value)
574 {
575 	if (strq == NULL) {
576 		return (-1);
577 	}
578 	strq->ss_params.prio.priority = value;
579 	sctp_ss_prio_remove(stcb, asoc, strq, NULL, 1);
580 	sctp_ss_prio_add(stcb, asoc, strq, NULL, 1);
581 	return (1);
582 }
583 
584 /*
585  * Fair bandwidth algorithm.
586  * Maintains an equal troughput per stream.
587  */
588 static void
589 sctp_ss_fb_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
590     int clear_values, int holds_lock)
591 {
592 	if (holds_lock == 0) {
593 		SCTP_TCB_SEND_LOCK(stcb);
594 	}
595 	while (!TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
596 		struct sctp_stream_out *strq;
597 
598 		strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
599 		if (clear_values) {
600 			strq->ss_params.fb.rounds = -1;
601 		}
602 		TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.fb.next_spoke);
603 		strq->ss_params.fb.next_spoke.tqe_next = NULL;
604 		strq->ss_params.fb.next_spoke.tqe_prev = NULL;
605 	}
606 	asoc->ss_data.last_out_stream = NULL;
607 	if (holds_lock == 0) {
608 		SCTP_TCB_SEND_UNLOCK(stcb);
609 	}
610 	return;
611 }
612 
613 static void
614 sctp_ss_fb_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
615 {
616 	if (with_strq != NULL) {
617 		if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
618 			stcb->asoc.ss_data.locked_on_sending = strq;
619 		}
620 		if (stcb->asoc.ss_data.last_out_stream == with_strq) {
621 			stcb->asoc.ss_data.last_out_stream = strq;
622 		}
623 	}
624 	strq->ss_params.fb.next_spoke.tqe_next = NULL;
625 	strq->ss_params.fb.next_spoke.tqe_prev = NULL;
626 	if (with_strq != NULL) {
627 		strq->ss_params.fb.rounds = with_strq->ss_params.fb.rounds;
628 	} else {
629 		strq->ss_params.fb.rounds = -1;
630 	}
631 	return;
632 }
633 
634 static void
635 sctp_ss_fb_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
636     struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED,
637     int holds_lock)
638 {
639 	if (holds_lock == 0) {
640 		SCTP_TCB_SEND_LOCK(stcb);
641 	}
642 	if (!TAILQ_EMPTY(&strq->outqueue) &&
643 	    (strq->ss_params.fb.next_spoke.tqe_next == NULL) &&
644 	    (strq->ss_params.fb.next_spoke.tqe_prev == NULL)) {
645 		if (strq->ss_params.fb.rounds < 0)
646 			strq->ss_params.fb.rounds = TAILQ_FIRST(&strq->outqueue)->length;
647 		TAILQ_INSERT_TAIL(&asoc->ss_data.out.wheel, strq, ss_params.fb.next_spoke);
648 	}
649 	if (holds_lock == 0) {
650 		SCTP_TCB_SEND_UNLOCK(stcb);
651 	}
652 	return;
653 }
654 
655 static void
656 sctp_ss_fb_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
657     struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED,
658     int holds_lock)
659 {
660 	if (holds_lock == 0) {
661 		SCTP_TCB_SEND_LOCK(stcb);
662 	}
663 	/*
664 	 * Remove from wheel if stream queue is empty and actually is on the
665 	 * wheel
666 	 */
667 	if (TAILQ_EMPTY(&strq->outqueue) &&
668 	    (strq->ss_params.fb.next_spoke.tqe_next != NULL ||
669 	    strq->ss_params.fb.next_spoke.tqe_prev != NULL)) {
670 		if (asoc->ss_data.last_out_stream == strq) {
671 			asoc->ss_data.last_out_stream = TAILQ_PREV(asoc->ss_data.last_out_stream, sctpwheel_listhead,
672 			    ss_params.fb.next_spoke);
673 			if (asoc->ss_data.last_out_stream == NULL) {
674 				asoc->ss_data.last_out_stream = TAILQ_LAST(&asoc->ss_data.out.wheel,
675 				    sctpwheel_listhead);
676 			}
677 			if (asoc->ss_data.last_out_stream == strq) {
678 				asoc->ss_data.last_out_stream = NULL;
679 			}
680 		}
681 		TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.fb.next_spoke);
682 		strq->ss_params.fb.next_spoke.tqe_next = NULL;
683 		strq->ss_params.fb.next_spoke.tqe_prev = NULL;
684 	}
685 	if (holds_lock == 0) {
686 		SCTP_TCB_SEND_UNLOCK(stcb);
687 	}
688 	return;
689 }
690 
691 static struct sctp_stream_out *
692 sctp_ss_fb_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
693     struct sctp_association *asoc)
694 {
695 	struct sctp_stream_out *strq = NULL, *strqt;
696 
697 	if (asoc->ss_data.last_out_stream == NULL ||
698 	    TAILQ_FIRST(&asoc->ss_data.out.wheel) == TAILQ_LAST(&asoc->ss_data.out.wheel, sctpwheel_listhead)) {
699 		strqt = TAILQ_FIRST(&asoc->ss_data.out.wheel);
700 	} else {
701 		strqt = TAILQ_NEXT(asoc->ss_data.last_out_stream, ss_params.fb.next_spoke);
702 	}
703 	do {
704 		if ((strqt != NULL) &&
705 		    ((SCTP_BASE_SYSCTL(sctp_cmt_on_off) > 0) ||
706 		    (SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0 &&
707 		    (net == NULL || (TAILQ_FIRST(&strqt->outqueue) && TAILQ_FIRST(&strqt->outqueue)->net == NULL) ||
708 		    (net != NULL && TAILQ_FIRST(&strqt->outqueue) && TAILQ_FIRST(&strqt->outqueue)->net != NULL &&
709 		    TAILQ_FIRST(&strqt->outqueue)->net == net))))) {
710 			if ((strqt->ss_params.fb.rounds >= 0) && (strq == NULL ||
711 			    strqt->ss_params.fb.rounds < strq->ss_params.fb.rounds)) {
712 				strq = strqt;
713 			}
714 		}
715 		if (strqt != NULL) {
716 			strqt = TAILQ_NEXT(strqt, ss_params.fb.next_spoke);
717 		} else {
718 			strqt = TAILQ_FIRST(&asoc->ss_data.out.wheel);
719 		}
720 	} while (strqt != strq);
721 	return (strq);
722 }
723 
724 static void
725 sctp_ss_fb_scheduled(struct sctp_tcb *stcb, struct sctp_nets *net SCTP_UNUSED,
726     struct sctp_association *asoc, struct sctp_stream_out *strq,
727     int moved_how_much SCTP_UNUSED)
728 {
729 	struct sctp_stream_queue_pending *sp;
730 	struct sctp_stream_out *strqt;
731 	int subtract;
732 
733 	if (stcb->asoc.idata_supported == 0) {
734 		sp = TAILQ_FIRST(&strq->outqueue);
735 		if ((sp != NULL) && (sp->some_taken == 1)) {
736 			stcb->asoc.ss_data.locked_on_sending = strq;
737 		} else {
738 			stcb->asoc.ss_data.locked_on_sending = NULL;
739 		}
740 	} else {
741 		stcb->asoc.ss_data.locked_on_sending = NULL;
742 	}
743 	subtract = strq->ss_params.fb.rounds;
744 	TAILQ_FOREACH(strqt, &asoc->ss_data.out.wheel, ss_params.fb.next_spoke) {
745 		strqt->ss_params.fb.rounds -= subtract;
746 		if (strqt->ss_params.fb.rounds < 0)
747 			strqt->ss_params.fb.rounds = 0;
748 	}
749 	if (TAILQ_FIRST(&strq->outqueue)) {
750 		strq->ss_params.fb.rounds = TAILQ_FIRST(&strq->outqueue)->length;
751 	} else {
752 		strq->ss_params.fb.rounds = -1;
753 	}
754 	asoc->ss_data.last_out_stream = strq;
755 	return;
756 }
757 
758 /*
759  * First-come, first-serve algorithm.
760  * Maintains the order provided by the application.
761  */
762 static void
763 sctp_ss_fcfs_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
764     struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp,
765     int holds_lock);
766 
767 static void
768 sctp_ss_fcfs_init(struct sctp_tcb *stcb, struct sctp_association *asoc,
769     int holds_lock)
770 {
771 	uint32_t x, n = 0, add_more = 1;
772 	struct sctp_stream_queue_pending *sp;
773 	uint16_t i;
774 
775 	if (holds_lock == 0) {
776 		SCTP_TCB_SEND_LOCK(stcb);
777 	}
778 	TAILQ_INIT(&asoc->ss_data.out.list);
779 	/*
780 	 * If there is data in the stream queues already, the scheduler of
781 	 * an existing association has been changed. We can only cycle
782 	 * through the stream queues and add everything to the FCFS queue.
783 	 */
784 	while (add_more) {
785 		add_more = 0;
786 		for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
787 			sp = TAILQ_FIRST(&stcb->asoc.strmout[i].outqueue);
788 			x = 0;
789 			/* Find n. message in current stream queue */
790 			while (sp != NULL && x < n) {
791 				sp = TAILQ_NEXT(sp, next);
792 				x++;
793 			}
794 			if (sp != NULL) {
795 				sctp_ss_fcfs_add(stcb, &stcb->asoc, &stcb->asoc.strmout[i], sp, 1);
796 				add_more = 1;
797 			}
798 		}
799 		n++;
800 	}
801 	if (holds_lock == 0) {
802 		SCTP_TCB_SEND_UNLOCK(stcb);
803 	}
804 	return;
805 }
806 
807 static void
808 sctp_ss_fcfs_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
809     int clear_values, int holds_lock)
810 {
811 	struct sctp_stream_queue_pending *sp;
812 
813 	if (clear_values) {
814 		if (holds_lock == 0) {
815 			SCTP_TCB_SEND_LOCK(stcb);
816 		}
817 		while (!TAILQ_EMPTY(&asoc->ss_data.out.list)) {
818 			sp = TAILQ_FIRST(&asoc->ss_data.out.list);
819 			TAILQ_REMOVE(&asoc->ss_data.out.list, sp, ss_next);
820 			sp->ss_next.tqe_next = NULL;
821 			sp->ss_next.tqe_prev = NULL;
822 		}
823 		if (holds_lock == 0) {
824 			SCTP_TCB_SEND_UNLOCK(stcb);
825 		}
826 	}
827 	return;
828 }
829 
830 static void
831 sctp_ss_fcfs_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
832 {
833 	if (with_strq != NULL) {
834 		if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
835 			stcb->asoc.ss_data.locked_on_sending = strq;
836 		}
837 		if (stcb->asoc.ss_data.last_out_stream == with_strq) {
838 			stcb->asoc.ss_data.last_out_stream = strq;
839 		}
840 	}
841 	strq->ss_params.fb.next_spoke.tqe_next = NULL;
842 	strq->ss_params.fb.next_spoke.tqe_prev = NULL;
843 	return;
844 }
845 
846 static void
847 sctp_ss_fcfs_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
848     struct sctp_stream_out *strq SCTP_UNUSED, struct sctp_stream_queue_pending *sp,
849     int holds_lock)
850 {
851 	if (holds_lock == 0) {
852 		SCTP_TCB_SEND_LOCK(stcb);
853 	}
854 	if (sp && (sp->ss_next.tqe_next == NULL) &&
855 	    (sp->ss_next.tqe_prev == NULL)) {
856 		TAILQ_INSERT_TAIL(&asoc->ss_data.out.list, sp, ss_next);
857 	}
858 	if (holds_lock == 0) {
859 		SCTP_TCB_SEND_UNLOCK(stcb);
860 	}
861 	return;
862 }
863 
864 static int
865 sctp_ss_fcfs_is_empty(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc)
866 {
867 	if (TAILQ_EMPTY(&asoc->ss_data.out.list)) {
868 		return (1);
869 	} else {
870 		return (0);
871 	}
872 }
873 
874 static void
875 sctp_ss_fcfs_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
876     struct sctp_stream_out *strq SCTP_UNUSED, struct sctp_stream_queue_pending *sp,
877     int holds_lock)
878 {
879 	if (holds_lock == 0) {
880 		SCTP_TCB_SEND_LOCK(stcb);
881 	}
882 	if (sp &&
883 	    ((sp->ss_next.tqe_next != NULL) ||
884 	    (sp->ss_next.tqe_prev != NULL))) {
885 		TAILQ_REMOVE(&asoc->ss_data.out.list, sp, ss_next);
886 		sp->ss_next.tqe_next = NULL;
887 		sp->ss_next.tqe_prev = NULL;
888 	}
889 	if (holds_lock == 0) {
890 		SCTP_TCB_SEND_UNLOCK(stcb);
891 	}
892 	return;
893 }
894 
895 
896 static struct sctp_stream_out *
897 sctp_ss_fcfs_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
898     struct sctp_association *asoc)
899 {
900 	struct sctp_stream_out *strq;
901 	struct sctp_stream_queue_pending *sp;
902 
903 	sp = TAILQ_FIRST(&asoc->ss_data.out.list);
904 default_again:
905 	if (sp != NULL) {
906 		strq = &asoc->strmout[sp->sid];
907 	} else {
908 		strq = NULL;
909 	}
910 
911 	/*
912 	 * If CMT is off, we must validate that the stream in question has
913 	 * the first item pointed towards are network destination requested
914 	 * by the caller. Note that if we turn out to be locked to a stream
915 	 * (assigning TSN's then we must stop, since we cannot look for
916 	 * another stream with data to send to that destination). In CMT's
917 	 * case, by skipping this check, we will send one data packet
918 	 * towards the requested net.
919 	 */
920 	if (net != NULL && strq != NULL &&
921 	    SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
922 		if (TAILQ_FIRST(&strq->outqueue) &&
923 		    TAILQ_FIRST(&strq->outqueue)->net != NULL &&
924 		    TAILQ_FIRST(&strq->outqueue)->net != net) {
925 			sp = TAILQ_NEXT(sp, ss_next);
926 			goto default_again;
927 		}
928 	}
929 	return (strq);
930 }
931 
932 const struct sctp_ss_functions sctp_ss_functions[] = {
933 /* SCTP_SS_DEFAULT */
934 	{
935 		.sctp_ss_init = sctp_ss_default_init,
936 		.sctp_ss_clear = sctp_ss_default_clear,
937 		.sctp_ss_init_stream = sctp_ss_default_init_stream,
938 		.sctp_ss_add_to_stream = sctp_ss_default_add,
939 		.sctp_ss_is_empty = sctp_ss_default_is_empty,
940 		.sctp_ss_remove_from_stream = sctp_ss_default_remove,
941 		.sctp_ss_select_stream = sctp_ss_default_select,
942 		.sctp_ss_scheduled = sctp_ss_default_scheduled,
943 		.sctp_ss_packet_done = sctp_ss_default_packet_done,
944 		.sctp_ss_get_value = sctp_ss_default_get_value,
945 		.sctp_ss_set_value = sctp_ss_default_set_value,
946 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
947 	},
948 /* SCTP_SS_ROUND_ROBIN */
949 	{
950 		.sctp_ss_init = sctp_ss_default_init,
951 		.sctp_ss_clear = sctp_ss_default_clear,
952 		.sctp_ss_init_stream = sctp_ss_default_init_stream,
953 		.sctp_ss_add_to_stream = sctp_ss_rr_add,
954 		.sctp_ss_is_empty = sctp_ss_default_is_empty,
955 		.sctp_ss_remove_from_stream = sctp_ss_default_remove,
956 		.sctp_ss_select_stream = sctp_ss_default_select,
957 		.sctp_ss_scheduled = sctp_ss_default_scheduled,
958 		.sctp_ss_packet_done = sctp_ss_default_packet_done,
959 		.sctp_ss_get_value = sctp_ss_default_get_value,
960 		.sctp_ss_set_value = sctp_ss_default_set_value,
961 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
962 	},
963 /* SCTP_SS_ROUND_ROBIN_PACKET */
964 	{
965 		.sctp_ss_init = sctp_ss_default_init,
966 		.sctp_ss_clear = sctp_ss_default_clear,
967 		.sctp_ss_init_stream = sctp_ss_default_init_stream,
968 		.sctp_ss_add_to_stream = sctp_ss_rr_add,
969 		.sctp_ss_is_empty = sctp_ss_default_is_empty,
970 		.sctp_ss_remove_from_stream = sctp_ss_default_remove,
971 		.sctp_ss_select_stream = sctp_ss_rrp_select,
972 		.sctp_ss_scheduled = sctp_ss_default_scheduled,
973 		.sctp_ss_packet_done = sctp_ss_rrp_packet_done,
974 		.sctp_ss_get_value = sctp_ss_default_get_value,
975 		.sctp_ss_set_value = sctp_ss_default_set_value,
976 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
977 	},
978 /* SCTP_SS_PRIORITY */
979 	{
980 		.sctp_ss_init = sctp_ss_default_init,
981 		.sctp_ss_clear = sctp_ss_prio_clear,
982 		.sctp_ss_init_stream = sctp_ss_prio_init_stream,
983 		.sctp_ss_add_to_stream = sctp_ss_prio_add,
984 		.sctp_ss_is_empty = sctp_ss_default_is_empty,
985 		.sctp_ss_remove_from_stream = sctp_ss_prio_remove,
986 		.sctp_ss_select_stream = sctp_ss_prio_select,
987 		.sctp_ss_scheduled = sctp_ss_default_scheduled,
988 		.sctp_ss_packet_done = sctp_ss_default_packet_done,
989 		.sctp_ss_get_value = sctp_ss_prio_get_value,
990 		.sctp_ss_set_value = sctp_ss_prio_set_value,
991 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
992 	},
993 /* SCTP_SS_FAIR_BANDWITH */
994 	{
995 		.sctp_ss_init = sctp_ss_default_init,
996 		.sctp_ss_clear = sctp_ss_fb_clear,
997 		.sctp_ss_init_stream = sctp_ss_fb_init_stream,
998 		.sctp_ss_add_to_stream = sctp_ss_fb_add,
999 		.sctp_ss_is_empty = sctp_ss_default_is_empty,
1000 		.sctp_ss_remove_from_stream = sctp_ss_fb_remove,
1001 		.sctp_ss_select_stream = sctp_ss_fb_select,
1002 		.sctp_ss_scheduled = sctp_ss_fb_scheduled,
1003 		.sctp_ss_packet_done = sctp_ss_default_packet_done,
1004 		.sctp_ss_get_value = sctp_ss_default_get_value,
1005 		.sctp_ss_set_value = sctp_ss_default_set_value,
1006 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
1007 	},
1008 /* SCTP_SS_FIRST_COME */
1009 	{
1010 		.sctp_ss_init = sctp_ss_fcfs_init,
1011 		.sctp_ss_clear = sctp_ss_fcfs_clear,
1012 		.sctp_ss_init_stream = sctp_ss_fcfs_init_stream,
1013 		.sctp_ss_add_to_stream = sctp_ss_fcfs_add,
1014 		.sctp_ss_is_empty = sctp_ss_fcfs_is_empty,
1015 		.sctp_ss_remove_from_stream = sctp_ss_fcfs_remove,
1016 		.sctp_ss_select_stream = sctp_ss_fcfs_select,
1017 		.sctp_ss_scheduled = sctp_ss_default_scheduled,
1018 		.sctp_ss_packet_done = sctp_ss_default_packet_done,
1019 		.sctp_ss_get_value = sctp_ss_default_get_value,
1020 		.sctp_ss_set_value = sctp_ss_default_set_value,
1021 		.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
1022 	}
1023 };
1024