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.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
9 * a) Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
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.
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.
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
32 #include <netinet/sctp_pcb.h>
35 * Default simple round-robin algorithm.
36 * Just interates the streams in the order they appear.
40 sctp_ss_default_add(struct sctp_tcb *, struct sctp_association *,
41 struct sctp_stream_out *,
42 struct sctp_stream_queue_pending *, int);
45 sctp_ss_default_remove(struct sctp_tcb *, struct sctp_association *,
46 struct sctp_stream_out *,
47 struct sctp_stream_queue_pending *, int);
50 sctp_ss_default_init(struct sctp_tcb *stcb, struct sctp_association *asoc,
55 TAILQ_INIT(&asoc->ss_data.out_wheel);
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.
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],
70 sctp_ss_default_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
71 int clear_values SCTP_UNUSED, int holds_lock)
73 if (holds_lock == 0) {
74 SCTP_TCB_SEND_LOCK(stcb);
76 while (!TAILQ_EMPTY(&asoc->ss_data.out_wheel)) {
77 struct sctp_stream_out *strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
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;
83 asoc->last_out_stream = NULL;
84 if (holds_lock == 0) {
85 SCTP_TCB_SEND_UNLOCK(stcb);
91 sctp_ss_default_init_stream(struct sctp_stream_out *strq, struct sctp_stream_out *with_strq SCTP_UNUSED)
93 strq->ss_params.rr.next_spoke.tqe_next = NULL;
94 strq->ss_params.rr.next_spoke.tqe_prev = NULL;
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)
103 if (holds_lock == 0) {
104 SCTP_TCB_SEND_LOCK(stcb);
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);
113 if (holds_lock == 0) {
114 SCTP_TCB_SEND_UNLOCK(stcb);
120 sctp_ss_default_is_empty(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc)
122 if (TAILQ_EMPTY(&asoc->ss_data.out_wheel)) {
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)
134 if (holds_lock == 0) {
135 SCTP_TCB_SEND_LOCK(stcb);
138 * Remove from wheel if stream queue is empty and actually is on the
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,
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,
152 if (asoc->last_out_stream == strq) {
153 asoc->last_out_stream = NULL;
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;
160 if (holds_lock == 0) {
161 SCTP_TCB_SEND_UNLOCK(stcb);
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)
171 struct sctp_stream_out *strq, *strqt;
173 strqt = asoc->last_out_stream;
175 /* Find the next stream to use */
177 strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
179 strq = TAILQ_NEXT(strqt, ss_params.rr.next_spoke);
181 strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
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.
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) {
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)
215 asoc->last_out_stream = strq;
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)
223 /* Nothing to be done here */
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)
231 /* Nothing to be done here */
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)
239 /* Nothing to be done here */
244 * Real round-robin algorithm.
245 * Always interates the streams in ascending order.
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)
252 struct sctp_stream_out *strqt;
254 if (holds_lock == 0) {
255 SCTP_TCB_SEND_LOCK(stcb);
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);
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);
268 TAILQ_INSERT_BEFORE(strqt, strq, ss_params.rr.next_spoke);
270 TAILQ_INSERT_TAIL(&asoc->ss_data.out_wheel, strq, ss_params.rr.next_spoke);
274 if (holds_lock == 0) {
275 SCTP_TCB_SEND_UNLOCK(stcb);
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.
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)
289 return (asoc->last_out_stream);
293 sctp_ss_rrp_packet_done(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
294 struct sctp_association *asoc)
296 struct sctp_stream_out *strq, *strqt;
298 strqt = asoc->last_out_stream;
300 /* Find the next stream to use */
302 strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
304 strq = TAILQ_NEXT(strqt, ss_params.rr.next_spoke);
306 strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
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.
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) {
332 asoc->last_out_stream = strq;
338 * Priority algorithm.
339 * Always prefers streams based on their priority id.
342 sctp_ss_prio_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
343 int clear_values, int holds_lock)
345 if (holds_lock == 0) {
346 SCTP_TCB_SEND_LOCK(stcb);
348 while (!TAILQ_EMPTY(&asoc->ss_data.out_wheel)) {
349 struct sctp_stream_out *strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
352 strq->ss_params.prio.priority = 0;
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;
359 asoc->last_out_stream = NULL;
360 if (holds_lock == 0) {
361 SCTP_TCB_SEND_UNLOCK(stcb);
367 sctp_ss_prio_init_stream(struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
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;
374 strq->ss_params.prio.priority = 0;
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,
384 struct sctp_stream_out *strqt;
386 if (holds_lock == 0) {
387 SCTP_TCB_SEND_LOCK(stcb);
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);
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);
401 TAILQ_INSERT_BEFORE(strqt, strq, ss_params.prio.next_spoke);
403 TAILQ_INSERT_TAIL(&asoc->ss_data.out_wheel, strq, ss_params.prio.next_spoke);
407 if (holds_lock == 0) {
408 SCTP_TCB_SEND_UNLOCK(stcb);
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,
418 if (holds_lock == 0) {
419 SCTP_TCB_SEND_LOCK(stcb);
422 * Remove from wheel if stream queue is empty and actually is on the
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,
435 if (asoc->last_out_stream == strq) {
436 asoc->last_out_stream = NULL;
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;
443 if (holds_lock == 0) {
444 SCTP_TCB_SEND_UNLOCK(stcb);
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)
453 struct sctp_stream_out *strq, *strqt, *strqn;
455 strqt = asoc->last_out_stream;
457 /* Find the next stream to use */
459 strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
461 strqn = TAILQ_NEXT(strqt, ss_params.prio.next_spoke);
463 strqn->ss_params.prio.priority == strqt->ss_params.prio.priority) {
466 strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
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.
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) {
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)
502 *value = strq->ss_params.prio.priority;
507 sctp_ss_prio_set_value(struct sctp_tcb *stcb, struct sctp_association *asoc,
508 struct sctp_stream_out *strq, uint16_t value)
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);
520 * Fair bandwidth algorithm.
521 * Maintains an equal troughput per stream.
524 sctp_ss_fb_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
525 int clear_values, int holds_lock)
527 if (holds_lock == 0) {
528 SCTP_TCB_SEND_LOCK(stcb);
530 while (!TAILQ_EMPTY(&asoc->ss_data.out_wheel)) {
531 struct sctp_stream_out *strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
534 strq->ss_params.fb.rounds = -1;
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;
540 asoc->last_out_stream = NULL;
541 if (holds_lock == 0) {
542 SCTP_TCB_SEND_UNLOCK(stcb);
548 sctp_ss_fb_init_stream(struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
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;
555 strq->ss_params.fb.rounds = -1;
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,
565 if (holds_lock == 0) {
566 SCTP_TCB_SEND_LOCK(stcb);
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);
575 if (holds_lock == 0) {
576 SCTP_TCB_SEND_UNLOCK(stcb);
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,
586 if (holds_lock == 0) {
587 SCTP_TCB_SEND_LOCK(stcb);
590 * Remove from wheel if stream queue is empty and actually is on the
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,
603 if (asoc->last_out_stream == strq) {
604 asoc->last_out_stream = NULL;
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;
611 if (holds_lock == 0) {
612 SCTP_TCB_SEND_UNLOCK(stcb);
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)
621 struct sctp_stream_out *strq = NULL, *strqt;
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);
627 strqt = TAILQ_NEXT(asoc->last_out_stream, ss_params.fb.next_spoke);
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)) {
642 strqt = TAILQ_NEXT(strqt, ss_params.fb.next_spoke);
644 strqt = TAILQ_FIRST(&asoc->ss_data.out_wheel);
646 } while (strqt != strq);
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)
655 struct sctp_stream_out *strqt;
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;
664 if (TAILQ_FIRST(&strq->outqueue)) {
665 strq->ss_params.fb.rounds = TAILQ_FIRST(&strq->outqueue)->length;
667 strq->ss_params.fb.rounds = -1;
669 asoc->last_out_stream = strq;
674 * First-come, first-serve algorithm.
675 * Maintains the order provided by the application.
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,
683 sctp_ss_fcfs_init(struct sctp_tcb *stcb, struct sctp_association *asoc,
686 uint32_t x, n = 0, add_more = 1;
687 struct sctp_stream_queue_pending *sp;
690 TAILQ_INIT(&asoc->ss_data.out_list);
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.
698 for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
699 sp = TAILQ_FIRST(&stcb->asoc.strmout[i].outqueue);
701 /* Find n. message in current stream queue */
702 while (sp != NULL && x < n) {
703 sp = TAILQ_NEXT(sp, next);
707 sctp_ss_fcfs_add(stcb, &stcb->asoc, &stcb->asoc.strmout[i], sp, holds_lock);
717 sctp_ss_fcfs_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
718 int clear_values, int holds_lock)
721 if (holds_lock == 0) {
722 SCTP_TCB_SEND_LOCK(stcb);
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);
727 if (holds_lock == 0) {
728 SCTP_TCB_SEND_UNLOCK(stcb);
735 sctp_ss_fcfs_init_stream(struct sctp_stream_out *strq SCTP_UNUSED, struct sctp_stream_out *with_strq SCTP_UNUSED)
737 /* Nothing to be done here */
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,
746 if (holds_lock == 0) {
747 SCTP_TCB_SEND_LOCK(stcb);
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);
753 if (holds_lock == 0) {
754 SCTP_TCB_SEND_UNLOCK(stcb);
760 sctp_ss_fcfs_is_empty(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc)
762 if (TAILQ_EMPTY(&asoc->ss_data.out_list)) {
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,
774 if (holds_lock == 0) {
775 SCTP_TCB_SEND_LOCK(stcb);
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);
782 if (holds_lock == 0) {
783 SCTP_TCB_SEND_UNLOCK(stcb);
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)
793 struct sctp_stream_out *strq;
794 struct sctp_stream_queue_pending *sp;
796 sp = TAILQ_FIRST(&asoc->ss_data.out_list);
799 strq = &asoc->strmout[sp->stream];
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.
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);
825 struct sctp_ss_functions sctp_ss_functions[] = {
826 /* SCTP_SS_DEFAULT */
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
840 /* SCTP_SS_ROUND_ROBIN */
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
854 /* SCTP_SS_ROUND_ROBIN_PACKET */
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
868 /* SCTP_SS_PRIORITY */
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
882 /* SCTP_SS_FAIR_BANDWITH */
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
896 /* SCTP_SS_FIRST_COME */
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