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 const 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