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