2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
11 * a) Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
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.
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.
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
34 #include <netinet/sctp_pcb.h>
37 * Default simple round-robin algorithm.
38 * Just interates the streams in the order they appear.
42 sctp_ss_default_add(struct sctp_tcb *, struct sctp_association *,
43 struct sctp_stream_out *,
44 struct sctp_stream_queue_pending *, int);
47 sctp_ss_default_remove(struct sctp_tcb *, struct sctp_association *,
48 struct sctp_stream_out *,
49 struct sctp_stream_queue_pending *, int);
52 sctp_ss_default_init(struct sctp_tcb *stcb, struct sctp_association *asoc,
57 asoc->ss_data.locked_on_sending = NULL;
58 asoc->ss_data.last_out_stream = NULL;
59 TAILQ_INIT(&asoc->ss_data.out.wheel);
61 * If there is data in the stream queues already, the scheduler of
62 * an existing association has been changed. We need to add all
63 * stream queues to the wheel.
65 for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
66 stcb->asoc.ss_functions.sctp_ss_add_to_stream(stcb, &stcb->asoc,
67 &stcb->asoc.strmout[i],
74 sctp_ss_default_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
75 int clear_values SCTP_UNUSED, int holds_lock)
77 if (holds_lock == 0) {
78 SCTP_TCB_SEND_LOCK(stcb);
80 while (!TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
81 struct sctp_stream_out *strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
83 TAILQ_REMOVE(&asoc->ss_data.out.wheel, TAILQ_FIRST(&asoc->ss_data.out.wheel), ss_params.rr.next_spoke);
84 strq->ss_params.rr.next_spoke.tqe_next = NULL;
85 strq->ss_params.rr.next_spoke.tqe_prev = NULL;
87 asoc->ss_data.last_out_stream = NULL;
88 if (holds_lock == 0) {
89 SCTP_TCB_SEND_UNLOCK(stcb);
95 sctp_ss_default_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
97 if (with_strq != NULL) {
98 if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
99 stcb->asoc.ss_data.locked_on_sending = strq;
101 if (stcb->asoc.ss_data.last_out_stream == with_strq) {
102 stcb->asoc.ss_data.last_out_stream = strq;
105 strq->ss_params.rr.next_spoke.tqe_next = NULL;
106 strq->ss_params.rr.next_spoke.tqe_prev = NULL;
111 sctp_ss_default_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
112 struct sctp_stream_out *strq,
113 struct sctp_stream_queue_pending *sp SCTP_UNUSED, int holds_lock)
115 if (holds_lock == 0) {
116 SCTP_TCB_SEND_LOCK(stcb);
118 /* Add to wheel if not already on it and stream queue not empty */
119 if (!TAILQ_EMPTY(&strq->outqueue) &&
120 (strq->ss_params.rr.next_spoke.tqe_next == NULL) &&
121 (strq->ss_params.rr.next_spoke.tqe_prev == NULL)) {
122 TAILQ_INSERT_TAIL(&asoc->ss_data.out.wheel,
123 strq, ss_params.rr.next_spoke);
125 if (holds_lock == 0) {
126 SCTP_TCB_SEND_UNLOCK(stcb);
132 sctp_ss_default_is_empty(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc)
134 if (TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
142 sctp_ss_default_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
143 struct sctp_stream_out *strq,
144 struct sctp_stream_queue_pending *sp SCTP_UNUSED, int holds_lock)
146 if (holds_lock == 0) {
147 SCTP_TCB_SEND_LOCK(stcb);
150 * Remove from wheel if stream queue is empty and actually is on the
153 if (TAILQ_EMPTY(&strq->outqueue) &&
154 (strq->ss_params.rr.next_spoke.tqe_next != NULL ||
155 strq->ss_params.rr.next_spoke.tqe_prev != NULL)) {
156 if (asoc->ss_data.last_out_stream == strq) {
157 asoc->ss_data.last_out_stream = TAILQ_PREV(asoc->ss_data.last_out_stream,
159 ss_params.rr.next_spoke);
160 if (asoc->ss_data.last_out_stream == NULL) {
161 asoc->ss_data.last_out_stream = TAILQ_LAST(&asoc->ss_data.out.wheel,
164 if (asoc->ss_data.last_out_stream == strq) {
165 asoc->ss_data.last_out_stream = NULL;
168 TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.rr.next_spoke);
169 strq->ss_params.rr.next_spoke.tqe_next = NULL;
170 strq->ss_params.rr.next_spoke.tqe_prev = NULL;
172 if (holds_lock == 0) {
173 SCTP_TCB_SEND_UNLOCK(stcb);
179 static struct sctp_stream_out *
180 sctp_ss_default_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
181 struct sctp_association *asoc)
183 struct sctp_stream_out *strq, *strqt;
185 if (asoc->ss_data.locked_on_sending) {
186 return (asoc->ss_data.locked_on_sending);
188 strqt = asoc->ss_data.last_out_stream;
190 /* Find the next stream to use */
192 strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
194 strq = TAILQ_NEXT(strqt, ss_params.rr.next_spoke);
196 strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
201 * If CMT is off, we must validate that the stream in question has
202 * the first item pointed towards are network destination requested
203 * by the caller. Note that if we turn out to be locked to a stream
204 * (assigning TSN's then we must stop, since we cannot look for
205 * another stream with data to send to that destination). In CMT's
206 * case, by skipping this check, we will send one data packet
207 * towards the requested net.
209 if (net != NULL && strq != NULL &&
210 SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
211 if (TAILQ_FIRST(&strq->outqueue) &&
212 TAILQ_FIRST(&strq->outqueue)->net != NULL &&
213 TAILQ_FIRST(&strq->outqueue)->net != net) {
214 if (strq == asoc->ss_data.last_out_stream) {
226 sctp_ss_default_scheduled(struct sctp_tcb *stcb,
227 struct sctp_nets *net SCTP_UNUSED,
228 struct sctp_association *asoc,
229 struct sctp_stream_out *strq,
230 int moved_how_much SCTP_UNUSED)
232 struct sctp_stream_queue_pending *sp;
234 asoc->ss_data.last_out_stream = strq;
235 if (stcb->asoc.idata_supported == 0) {
236 sp = TAILQ_FIRST(&strq->outqueue);
237 if ((sp != NULL) && (sp->some_taken == 1)) {
238 stcb->asoc.ss_data.locked_on_sending = strq;
240 stcb->asoc.ss_data.locked_on_sending = NULL;
243 stcb->asoc.ss_data.locked_on_sending = NULL;
249 sctp_ss_default_packet_done(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net SCTP_UNUSED,
250 struct sctp_association *asoc SCTP_UNUSED)
252 /* Nothing to be done here */
257 sctp_ss_default_get_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED,
258 struct sctp_stream_out *strq SCTP_UNUSED, uint16_t *value SCTP_UNUSED)
260 /* Nothing to be done here */
265 sctp_ss_default_set_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED,
266 struct sctp_stream_out *strq SCTP_UNUSED, uint16_t value SCTP_UNUSED)
268 /* Nothing to be done here */
273 sctp_ss_default_is_user_msgs_incomplete(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc)
275 struct sctp_stream_out *strq;
276 struct sctp_stream_queue_pending *sp;
278 if (asoc->stream_queue_cnt != 1) {
281 strq = asoc->ss_data.locked_on_sending;
285 sp = TAILQ_FIRST(&strq->outqueue);
289 return (!sp->msg_is_complete);
293 * Real round-robin algorithm.
294 * Always interates the streams in ascending order.
297 sctp_ss_rr_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
298 struct sctp_stream_out *strq,
299 struct sctp_stream_queue_pending *sp SCTP_UNUSED, int holds_lock)
301 struct sctp_stream_out *strqt;
303 if (holds_lock == 0) {
304 SCTP_TCB_SEND_LOCK(stcb);
306 if (!TAILQ_EMPTY(&strq->outqueue) &&
307 (strq->ss_params.rr.next_spoke.tqe_next == NULL) &&
308 (strq->ss_params.rr.next_spoke.tqe_prev == NULL)) {
309 if (TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
310 TAILQ_INSERT_HEAD(&asoc->ss_data.out.wheel, strq, ss_params.rr.next_spoke);
312 strqt = TAILQ_FIRST(&asoc->ss_data.out.wheel);
313 while (strqt != NULL && (strqt->sid < strq->sid)) {
314 strqt = TAILQ_NEXT(strqt, ss_params.rr.next_spoke);
317 TAILQ_INSERT_BEFORE(strqt, strq, ss_params.rr.next_spoke);
319 TAILQ_INSERT_TAIL(&asoc->ss_data.out.wheel, strq, ss_params.rr.next_spoke);
323 if (holds_lock == 0) {
324 SCTP_TCB_SEND_UNLOCK(stcb);
330 * Real round-robin per packet algorithm.
331 * Always interates the streams in ascending order and
332 * only fills messages of the same stream in a packet.
334 static struct sctp_stream_out *
335 sctp_ss_rrp_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net SCTP_UNUSED,
336 struct sctp_association *asoc)
338 return (asoc->ss_data.last_out_stream);
342 sctp_ss_rrp_packet_done(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
343 struct sctp_association *asoc)
345 struct sctp_stream_out *strq, *strqt;
347 strqt = asoc->ss_data.last_out_stream;
349 /* Find the next stream to use */
351 strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
353 strq = TAILQ_NEXT(strqt, ss_params.rr.next_spoke);
355 strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
360 * If CMT is off, we must validate that the stream in question has
361 * the first item pointed towards are network destination requested
362 * by the caller. Note that if we turn out to be locked to a stream
363 * (assigning TSN's then we must stop, since we cannot look for
364 * another stream with data to send to that destination). In CMT's
365 * case, by skipping this check, we will send one data packet
366 * towards the requested net.
368 if (net != NULL && strq != NULL &&
369 SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
370 if (TAILQ_FIRST(&strq->outqueue) &&
371 TAILQ_FIRST(&strq->outqueue)->net != NULL &&
372 TAILQ_FIRST(&strq->outqueue)->net != net) {
373 if (strq == asoc->ss_data.last_out_stream) {
381 asoc->ss_data.last_out_stream = strq;
387 * Priority algorithm.
388 * Always prefers streams based on their priority id.
391 sctp_ss_prio_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
392 int clear_values, int holds_lock)
394 if (holds_lock == 0) {
395 SCTP_TCB_SEND_LOCK(stcb);
397 while (!TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
398 struct sctp_stream_out *strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
401 strq->ss_params.prio.priority = 0;
403 TAILQ_REMOVE(&asoc->ss_data.out.wheel, TAILQ_FIRST(&asoc->ss_data.out.wheel), ss_params.prio.next_spoke);
404 strq->ss_params.prio.next_spoke.tqe_next = NULL;
405 strq->ss_params.prio.next_spoke.tqe_prev = NULL;
408 asoc->ss_data.last_out_stream = NULL;
409 if (holds_lock == 0) {
410 SCTP_TCB_SEND_UNLOCK(stcb);
416 sctp_ss_prio_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
418 if (with_strq != NULL) {
419 if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
420 stcb->asoc.ss_data.locked_on_sending = strq;
422 if (stcb->asoc.ss_data.last_out_stream == with_strq) {
423 stcb->asoc.ss_data.last_out_stream = strq;
426 strq->ss_params.prio.next_spoke.tqe_next = NULL;
427 strq->ss_params.prio.next_spoke.tqe_prev = NULL;
428 if (with_strq != NULL) {
429 strq->ss_params.prio.priority = with_strq->ss_params.prio.priority;
431 strq->ss_params.prio.priority = 0;
437 sctp_ss_prio_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
438 struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED,
441 struct sctp_stream_out *strqt;
443 if (holds_lock == 0) {
444 SCTP_TCB_SEND_LOCK(stcb);
446 /* Add to wheel if not already on it and stream queue not empty */
447 if (!TAILQ_EMPTY(&strq->outqueue) &&
448 (strq->ss_params.prio.next_spoke.tqe_next == NULL) &&
449 (strq->ss_params.prio.next_spoke.tqe_prev == NULL)) {
450 if (TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
451 TAILQ_INSERT_HEAD(&asoc->ss_data.out.wheel, strq, ss_params.prio.next_spoke);
453 strqt = TAILQ_FIRST(&asoc->ss_data.out.wheel);
454 while (strqt != NULL && strqt->ss_params.prio.priority < strq->ss_params.prio.priority) {
455 strqt = TAILQ_NEXT(strqt, ss_params.prio.next_spoke);
458 TAILQ_INSERT_BEFORE(strqt, strq, ss_params.prio.next_spoke);
460 TAILQ_INSERT_TAIL(&asoc->ss_data.out.wheel, strq, ss_params.prio.next_spoke);
464 if (holds_lock == 0) {
465 SCTP_TCB_SEND_UNLOCK(stcb);
471 sctp_ss_prio_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
472 struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED,
475 if (holds_lock == 0) {
476 SCTP_TCB_SEND_LOCK(stcb);
479 * Remove from wheel if stream queue is empty and actually is on the
482 if (TAILQ_EMPTY(&strq->outqueue) &&
483 (strq->ss_params.prio.next_spoke.tqe_next != NULL ||
484 strq->ss_params.prio.next_spoke.tqe_prev != NULL)) {
485 if (asoc->ss_data.last_out_stream == strq) {
486 asoc->ss_data.last_out_stream = TAILQ_PREV(asoc->ss_data.last_out_stream, sctpwheel_listhead,
487 ss_params.prio.next_spoke);
488 if (asoc->ss_data.last_out_stream == NULL) {
489 asoc->ss_data.last_out_stream = TAILQ_LAST(&asoc->ss_data.out.wheel,
492 if (asoc->ss_data.last_out_stream == strq) {
493 asoc->ss_data.last_out_stream = NULL;
496 TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.prio.next_spoke);
497 strq->ss_params.prio.next_spoke.tqe_next = NULL;
498 strq->ss_params.prio.next_spoke.tqe_prev = NULL;
500 if (holds_lock == 0) {
501 SCTP_TCB_SEND_UNLOCK(stcb);
506 static struct sctp_stream_out *
507 sctp_ss_prio_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
508 struct sctp_association *asoc)
510 struct sctp_stream_out *strq, *strqt, *strqn;
512 strqt = asoc->ss_data.last_out_stream;
514 /* Find the next stream to use */
516 strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
518 strqn = TAILQ_NEXT(strqt, ss_params.prio.next_spoke);
520 strqn->ss_params.prio.priority == strqt->ss_params.prio.priority) {
523 strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
528 * If CMT is off, we must validate that the stream in question has
529 * the first item pointed towards are network destination requested
530 * by the caller. Note that if we turn out to be locked to a stream
531 * (assigning TSN's then we must stop, since we cannot look for
532 * another stream with data to send to that destination). In CMT's
533 * case, by skipping this check, we will send one data packet
534 * towards the requested net.
536 if (net != NULL && strq != NULL &&
537 SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
538 if (TAILQ_FIRST(&strq->outqueue) &&
539 TAILQ_FIRST(&strq->outqueue)->net != NULL &&
540 TAILQ_FIRST(&strq->outqueue)->net != net) {
541 if (strq == asoc->ss_data.last_out_stream) {
553 sctp_ss_prio_get_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED,
554 struct sctp_stream_out *strq, uint16_t *value)
559 *value = strq->ss_params.prio.priority;
564 sctp_ss_prio_set_value(struct sctp_tcb *stcb, struct sctp_association *asoc,
565 struct sctp_stream_out *strq, uint16_t value)
570 strq->ss_params.prio.priority = value;
571 sctp_ss_prio_remove(stcb, asoc, strq, NULL, 1);
572 sctp_ss_prio_add(stcb, asoc, strq, NULL, 1);
577 * Fair bandwidth algorithm.
578 * Maintains an equal troughput per stream.
581 sctp_ss_fb_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
582 int clear_values, int holds_lock)
584 if (holds_lock == 0) {
585 SCTP_TCB_SEND_LOCK(stcb);
587 while (!TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
588 struct sctp_stream_out *strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
591 strq->ss_params.fb.rounds = -1;
593 TAILQ_REMOVE(&asoc->ss_data.out.wheel, TAILQ_FIRST(&asoc->ss_data.out.wheel), ss_params.fb.next_spoke);
594 strq->ss_params.fb.next_spoke.tqe_next = NULL;
595 strq->ss_params.fb.next_spoke.tqe_prev = NULL;
597 asoc->ss_data.last_out_stream = NULL;
598 if (holds_lock == 0) {
599 SCTP_TCB_SEND_UNLOCK(stcb);
605 sctp_ss_fb_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
607 if (with_strq != NULL) {
608 if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
609 stcb->asoc.ss_data.locked_on_sending = strq;
611 if (stcb->asoc.ss_data.last_out_stream == with_strq) {
612 stcb->asoc.ss_data.last_out_stream = strq;
615 strq->ss_params.fb.next_spoke.tqe_next = NULL;
616 strq->ss_params.fb.next_spoke.tqe_prev = NULL;
617 if (with_strq != NULL) {
618 strq->ss_params.fb.rounds = with_strq->ss_params.fb.rounds;
620 strq->ss_params.fb.rounds = -1;
626 sctp_ss_fb_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
627 struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED,
630 if (holds_lock == 0) {
631 SCTP_TCB_SEND_LOCK(stcb);
633 if (!TAILQ_EMPTY(&strq->outqueue) &&
634 (strq->ss_params.fb.next_spoke.tqe_next == NULL) &&
635 (strq->ss_params.fb.next_spoke.tqe_prev == NULL)) {
636 if (strq->ss_params.fb.rounds < 0)
637 strq->ss_params.fb.rounds = TAILQ_FIRST(&strq->outqueue)->length;
638 TAILQ_INSERT_TAIL(&asoc->ss_data.out.wheel, strq, ss_params.fb.next_spoke);
640 if (holds_lock == 0) {
641 SCTP_TCB_SEND_UNLOCK(stcb);
647 sctp_ss_fb_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
648 struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED,
651 if (holds_lock == 0) {
652 SCTP_TCB_SEND_LOCK(stcb);
655 * Remove from wheel if stream queue is empty and actually is on the
658 if (TAILQ_EMPTY(&strq->outqueue) &&
659 (strq->ss_params.fb.next_spoke.tqe_next != NULL ||
660 strq->ss_params.fb.next_spoke.tqe_prev != NULL)) {
661 if (asoc->ss_data.last_out_stream == strq) {
662 asoc->ss_data.last_out_stream = TAILQ_PREV(asoc->ss_data.last_out_stream, sctpwheel_listhead,
663 ss_params.fb.next_spoke);
664 if (asoc->ss_data.last_out_stream == NULL) {
665 asoc->ss_data.last_out_stream = TAILQ_LAST(&asoc->ss_data.out.wheel,
668 if (asoc->ss_data.last_out_stream == strq) {
669 asoc->ss_data.last_out_stream = NULL;
672 TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.fb.next_spoke);
673 strq->ss_params.fb.next_spoke.tqe_next = NULL;
674 strq->ss_params.fb.next_spoke.tqe_prev = NULL;
676 if (holds_lock == 0) {
677 SCTP_TCB_SEND_UNLOCK(stcb);
682 static struct sctp_stream_out *
683 sctp_ss_fb_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
684 struct sctp_association *asoc)
686 struct sctp_stream_out *strq = NULL, *strqt;
688 if (asoc->ss_data.last_out_stream == NULL ||
689 TAILQ_FIRST(&asoc->ss_data.out.wheel) == TAILQ_LAST(&asoc->ss_data.out.wheel, sctpwheel_listhead)) {
690 strqt = TAILQ_FIRST(&asoc->ss_data.out.wheel);
692 strqt = TAILQ_NEXT(asoc->ss_data.last_out_stream, ss_params.fb.next_spoke);
695 if ((strqt != NULL) &&
696 ((SCTP_BASE_SYSCTL(sctp_cmt_on_off) > 0) ||
697 (SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0 &&
698 (net == NULL || (TAILQ_FIRST(&strqt->outqueue) && TAILQ_FIRST(&strqt->outqueue)->net == NULL) ||
699 (net != NULL && TAILQ_FIRST(&strqt->outqueue) && TAILQ_FIRST(&strqt->outqueue)->net != NULL &&
700 TAILQ_FIRST(&strqt->outqueue)->net == net))))) {
701 if ((strqt->ss_params.fb.rounds >= 0) && (strq == NULL ||
702 strqt->ss_params.fb.rounds < strq->ss_params.fb.rounds)) {
707 strqt = TAILQ_NEXT(strqt, ss_params.fb.next_spoke);
709 strqt = TAILQ_FIRST(&asoc->ss_data.out.wheel);
711 } while (strqt != strq);
716 sctp_ss_fb_scheduled(struct sctp_tcb *stcb, struct sctp_nets *net SCTP_UNUSED,
717 struct sctp_association *asoc, struct sctp_stream_out *strq,
718 int moved_how_much SCTP_UNUSED)
720 struct sctp_stream_queue_pending *sp;
721 struct sctp_stream_out *strqt;
724 if (stcb->asoc.idata_supported == 0) {
725 sp = TAILQ_FIRST(&strq->outqueue);
726 if ((sp != NULL) && (sp->some_taken == 1)) {
727 stcb->asoc.ss_data.locked_on_sending = strq;
729 stcb->asoc.ss_data.locked_on_sending = NULL;
732 stcb->asoc.ss_data.locked_on_sending = NULL;
734 subtract = strq->ss_params.fb.rounds;
735 TAILQ_FOREACH(strqt, &asoc->ss_data.out.wheel, ss_params.fb.next_spoke) {
736 strqt->ss_params.fb.rounds -= subtract;
737 if (strqt->ss_params.fb.rounds < 0)
738 strqt->ss_params.fb.rounds = 0;
740 if (TAILQ_FIRST(&strq->outqueue)) {
741 strq->ss_params.fb.rounds = TAILQ_FIRST(&strq->outqueue)->length;
743 strq->ss_params.fb.rounds = -1;
745 asoc->ss_data.last_out_stream = strq;
750 * First-come, first-serve algorithm.
751 * Maintains the order provided by the application.
754 sctp_ss_fcfs_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
755 struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp,
759 sctp_ss_fcfs_init(struct sctp_tcb *stcb, struct sctp_association *asoc,
762 uint32_t x, n = 0, add_more = 1;
763 struct sctp_stream_queue_pending *sp;
766 TAILQ_INIT(&asoc->ss_data.out.list);
768 * If there is data in the stream queues already, the scheduler of
769 * an existing association has been changed. We can only cycle
770 * through the stream queues and add everything to the FCFS queue.
774 for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
775 sp = TAILQ_FIRST(&stcb->asoc.strmout[i].outqueue);
777 /* Find n. message in current stream queue */
778 while (sp != NULL && x < n) {
779 sp = TAILQ_NEXT(sp, next);
783 sctp_ss_fcfs_add(stcb, &stcb->asoc, &stcb->asoc.strmout[i], sp, holds_lock);
793 sctp_ss_fcfs_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
794 int clear_values, int holds_lock)
797 if (holds_lock == 0) {
798 SCTP_TCB_SEND_LOCK(stcb);
800 while (!TAILQ_EMPTY(&asoc->ss_data.out.list)) {
801 TAILQ_REMOVE(&asoc->ss_data.out.list, TAILQ_FIRST(&asoc->ss_data.out.list), ss_next);
803 if (holds_lock == 0) {
804 SCTP_TCB_SEND_UNLOCK(stcb);
811 sctp_ss_fcfs_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
813 if (with_strq != NULL) {
814 if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
815 stcb->asoc.ss_data.locked_on_sending = strq;
817 if (stcb->asoc.ss_data.last_out_stream == with_strq) {
818 stcb->asoc.ss_data.last_out_stream = strq;
825 sctp_ss_fcfs_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
826 struct sctp_stream_out *strq SCTP_UNUSED, struct sctp_stream_queue_pending *sp,
829 if (holds_lock == 0) {
830 SCTP_TCB_SEND_LOCK(stcb);
832 if (sp && (sp->ss_next.tqe_next == NULL) &&
833 (sp->ss_next.tqe_prev == NULL)) {
834 TAILQ_INSERT_TAIL(&asoc->ss_data.out.list, sp, ss_next);
836 if (holds_lock == 0) {
837 SCTP_TCB_SEND_UNLOCK(stcb);
843 sctp_ss_fcfs_is_empty(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc)
845 if (TAILQ_EMPTY(&asoc->ss_data.out.list)) {
853 sctp_ss_fcfs_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
854 struct sctp_stream_out *strq SCTP_UNUSED, struct sctp_stream_queue_pending *sp,
857 if (holds_lock == 0) {
858 SCTP_TCB_SEND_LOCK(stcb);
861 ((sp->ss_next.tqe_next != NULL) ||
862 (sp->ss_next.tqe_prev != NULL))) {
863 TAILQ_REMOVE(&asoc->ss_data.out.list, sp, ss_next);
865 if (holds_lock == 0) {
866 SCTP_TCB_SEND_UNLOCK(stcb);
872 static struct sctp_stream_out *
873 sctp_ss_fcfs_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
874 struct sctp_association *asoc)
876 struct sctp_stream_out *strq;
877 struct sctp_stream_queue_pending *sp;
879 sp = TAILQ_FIRST(&asoc->ss_data.out.list);
882 strq = &asoc->strmout[sp->sid];
888 * If CMT is off, we must validate that the stream in question has
889 * the first item pointed towards are network destination requested
890 * by the caller. Note that if we turn out to be locked to a stream
891 * (assigning TSN's then we must stop, since we cannot look for
892 * another stream with data to send to that destination). In CMT's
893 * case, by skipping this check, we will send one data packet
894 * towards the requested net.
896 if (net != NULL && strq != NULL &&
897 SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
898 if (TAILQ_FIRST(&strq->outqueue) &&
899 TAILQ_FIRST(&strq->outqueue)->net != NULL &&
900 TAILQ_FIRST(&strq->outqueue)->net != net) {
901 sp = TAILQ_NEXT(sp, ss_next);
908 const struct sctp_ss_functions sctp_ss_functions[] = {
909 /* SCTP_SS_DEFAULT */
911 .sctp_ss_init = sctp_ss_default_init,
912 .sctp_ss_clear = sctp_ss_default_clear,
913 .sctp_ss_init_stream = sctp_ss_default_init_stream,
914 .sctp_ss_add_to_stream = sctp_ss_default_add,
915 .sctp_ss_is_empty = sctp_ss_default_is_empty,
916 .sctp_ss_remove_from_stream = sctp_ss_default_remove,
917 .sctp_ss_select_stream = sctp_ss_default_select,
918 .sctp_ss_scheduled = sctp_ss_default_scheduled,
919 .sctp_ss_packet_done = sctp_ss_default_packet_done,
920 .sctp_ss_get_value = sctp_ss_default_get_value,
921 .sctp_ss_set_value = sctp_ss_default_set_value,
922 .sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
924 /* SCTP_SS_ROUND_ROBIN */
926 .sctp_ss_init = sctp_ss_default_init,
927 .sctp_ss_clear = sctp_ss_default_clear,
928 .sctp_ss_init_stream = sctp_ss_default_init_stream,
929 .sctp_ss_add_to_stream = sctp_ss_rr_add,
930 .sctp_ss_is_empty = sctp_ss_default_is_empty,
931 .sctp_ss_remove_from_stream = sctp_ss_default_remove,
932 .sctp_ss_select_stream = sctp_ss_default_select,
933 .sctp_ss_scheduled = sctp_ss_default_scheduled,
934 .sctp_ss_packet_done = sctp_ss_default_packet_done,
935 .sctp_ss_get_value = sctp_ss_default_get_value,
936 .sctp_ss_set_value = sctp_ss_default_set_value,
937 .sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
939 /* SCTP_SS_ROUND_ROBIN_PACKET */
941 .sctp_ss_init = sctp_ss_default_init,
942 .sctp_ss_clear = sctp_ss_default_clear,
943 .sctp_ss_init_stream = sctp_ss_default_init_stream,
944 .sctp_ss_add_to_stream = sctp_ss_rr_add,
945 .sctp_ss_is_empty = sctp_ss_default_is_empty,
946 .sctp_ss_remove_from_stream = sctp_ss_default_remove,
947 .sctp_ss_select_stream = sctp_ss_rrp_select,
948 .sctp_ss_scheduled = sctp_ss_default_scheduled,
949 .sctp_ss_packet_done = sctp_ss_rrp_packet_done,
950 .sctp_ss_get_value = sctp_ss_default_get_value,
951 .sctp_ss_set_value = sctp_ss_default_set_value,
952 .sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
954 /* SCTP_SS_PRIORITY */
956 .sctp_ss_init = sctp_ss_default_init,
957 .sctp_ss_clear = sctp_ss_prio_clear,
958 .sctp_ss_init_stream = sctp_ss_prio_init_stream,
959 .sctp_ss_add_to_stream = sctp_ss_prio_add,
960 .sctp_ss_is_empty = sctp_ss_default_is_empty,
961 .sctp_ss_remove_from_stream = sctp_ss_prio_remove,
962 .sctp_ss_select_stream = sctp_ss_prio_select,
963 .sctp_ss_scheduled = sctp_ss_default_scheduled,
964 .sctp_ss_packet_done = sctp_ss_default_packet_done,
965 .sctp_ss_get_value = sctp_ss_prio_get_value,
966 .sctp_ss_set_value = sctp_ss_prio_set_value,
967 .sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
969 /* SCTP_SS_FAIR_BANDWITH */
971 .sctp_ss_init = sctp_ss_default_init,
972 .sctp_ss_clear = sctp_ss_fb_clear,
973 .sctp_ss_init_stream = sctp_ss_fb_init_stream,
974 .sctp_ss_add_to_stream = sctp_ss_fb_add,
975 .sctp_ss_is_empty = sctp_ss_default_is_empty,
976 .sctp_ss_remove_from_stream = sctp_ss_fb_remove,
977 .sctp_ss_select_stream = sctp_ss_fb_select,
978 .sctp_ss_scheduled = sctp_ss_fb_scheduled,
979 .sctp_ss_packet_done = sctp_ss_default_packet_done,
980 .sctp_ss_get_value = sctp_ss_default_get_value,
981 .sctp_ss_set_value = sctp_ss_default_set_value,
982 .sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
984 /* SCTP_SS_FIRST_COME */
986 .sctp_ss_init = sctp_ss_fcfs_init,
987 .sctp_ss_clear = sctp_ss_fcfs_clear,
988 .sctp_ss_init_stream = sctp_ss_fcfs_init_stream,
989 .sctp_ss_add_to_stream = sctp_ss_fcfs_add,
990 .sctp_ss_is_empty = sctp_ss_fcfs_is_empty,
991 .sctp_ss_remove_from_stream = sctp_ss_fcfs_remove,
992 .sctp_ss_select_stream = sctp_ss_fcfs_select,
993 .sctp_ss_scheduled = sctp_ss_default_scheduled,
994 .sctp_ss_packet_done = sctp_ss_default_packet_done,
995 .sctp_ss_get_value = sctp_ss_default_get_value,
996 .sctp_ss_set_value = sctp_ss_default_set_value,
997 .sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete