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