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