1 /*-
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 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD: releng/11.0/sys/netinet/sctp_ss_functions.c 303956 2016-08-11 10:14:03Z tuexen $");
31
32 #include <netinet/sctp_pcb.h>
33
34 /*
35 * Default simple round-robin algorithm.
36 * Just interates the streams in the order they appear.
37 */
38
39 static void
40 sctp_ss_default_add(struct sctp_tcb *, struct sctp_association *,
41 struct sctp_stream_out *,
42 struct sctp_stream_queue_pending *, int);
43
44 static void
45 sctp_ss_default_remove(struct sctp_tcb *, struct sctp_association *,
46 struct sctp_stream_out *,
47 struct sctp_stream_queue_pending *, int);
48
49 static void
50 sctp_ss_default_init(struct sctp_tcb *stcb, struct sctp_association *asoc,
51 int holds_lock)
52 {
53 uint16_t i;
54
55 asoc->ss_data.locked_on_sending = NULL;
56 asoc->ss_data.last_out_stream = NULL;
57 TAILQ_INIT(&asoc->ss_data.out.wheel);
58 /*
59 * If there is data in the stream queues already, the scheduler of
60 * an existing association has been changed. We need to add all
61 * stream queues to the wheel.
62 */
63 for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
64 stcb->asoc.ss_functions.sctp_ss_add_to_stream(stcb, &stcb->asoc,
65 &stcb->asoc.strmout[i],
66 NULL, holds_lock);
67 }
68 return;
69 }
70
71 static void
72 sctp_ss_default_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
73 int clear_values SCTP_UNUSED, int holds_lock)
74 {
75 if (holds_lock == 0) {
76 SCTP_TCB_SEND_LOCK(stcb);
77 }
78 while (!TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
79 struct sctp_stream_out *strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
80
81 TAILQ_REMOVE(&asoc->ss_data.out.wheel, TAILQ_FIRST(&asoc->ss_data.out.wheel), ss_params.rr.next_spoke);
82 strq->ss_params.rr.next_spoke.tqe_next = NULL;
83 strq->ss_params.rr.next_spoke.tqe_prev = NULL;
84 }
85 asoc->ss_data.last_out_stream = NULL;
86 if (holds_lock == 0) {
87 SCTP_TCB_SEND_UNLOCK(stcb);
88 }
89 return;
90 }
91
92 static void
93 sctp_ss_default_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
94 {
95 if (with_strq != NULL) {
96 if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
97 stcb->asoc.ss_data.locked_on_sending = strq;
98 }
99 if (stcb->asoc.ss_data.last_out_stream == with_strq) {
100 stcb->asoc.ss_data.last_out_stream = strq;
101 }
102 }
103 strq->ss_params.rr.next_spoke.tqe_next = NULL;
104 strq->ss_params.rr.next_spoke.tqe_prev = NULL;
105 return;
106 }
107
108 static void
109 sctp_ss_default_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
110 struct sctp_stream_out *strq,
111 struct sctp_stream_queue_pending *sp SCTP_UNUSED, int holds_lock)
112 {
113 if (holds_lock == 0) {
114 SCTP_TCB_SEND_LOCK(stcb);
115 }
116 /* Add to wheel if not already on it and stream queue not empty */
117 if (!TAILQ_EMPTY(&strq->outqueue) &&
118 (strq->ss_params.rr.next_spoke.tqe_next == NULL) &&
119 (strq->ss_params.rr.next_spoke.tqe_prev == NULL)) {
120 TAILQ_INSERT_TAIL(&asoc->ss_data.out.wheel,
121 strq, ss_params.rr.next_spoke);
122 }
123 if (holds_lock == 0) {
124 SCTP_TCB_SEND_UNLOCK(stcb);
125 }
126 return;
127 }
128
129 static int
130 sctp_ss_default_is_empty(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc)
131 {
132 if (TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
133 return (1);
134 } else {
135 return (0);
136 }
137 }
138
139 static void
140 sctp_ss_default_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
141 struct sctp_stream_out *strq,
142 struct sctp_stream_queue_pending *sp SCTP_UNUSED, int holds_lock)
143 {
144 if (holds_lock == 0) {
145 SCTP_TCB_SEND_LOCK(stcb);
146 }
147 /*
148 * Remove from wheel if stream queue is empty and actually is on the
149 * wheel
150 */
151 if (TAILQ_EMPTY(&strq->outqueue) &&
152 (strq->ss_params.rr.next_spoke.tqe_next != NULL ||
153 strq->ss_params.rr.next_spoke.tqe_prev != NULL)) {
154 if (asoc->ss_data.last_out_stream == strq) {
155 asoc->ss_data.last_out_stream = TAILQ_PREV(asoc->ss_data.last_out_stream,
156 sctpwheel_listhead,
157 ss_params.rr.next_spoke);
158 if (asoc->ss_data.last_out_stream == NULL) {
159 asoc->ss_data.last_out_stream = TAILQ_LAST(&asoc->ss_data.out.wheel,
160 sctpwheel_listhead);
161 }
162 if (asoc->ss_data.last_out_stream == strq) {
163 asoc->ss_data.last_out_stream = NULL;
164 }
165 }
166 TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.rr.next_spoke);
167 strq->ss_params.rr.next_spoke.tqe_next = NULL;
168 strq->ss_params.rr.next_spoke.tqe_prev = NULL;
169 }
170 if (holds_lock == 0) {
171 SCTP_TCB_SEND_UNLOCK(stcb);
172 }
173 return;
174 }
175
176
177 static struct sctp_stream_out *
178 sctp_ss_default_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
179 struct sctp_association *asoc)
180 {
181 struct sctp_stream_out *strq, *strqt;
182
183 if (asoc->ss_data.locked_on_sending) {
184 return (asoc->ss_data.locked_on_sending);
185 }
186 strqt = asoc->ss_data.last_out_stream;
187 default_again:
188 /* Find the next stream to use */
189 if (strqt == NULL) {
190 strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
191 } else {
192 strq = TAILQ_NEXT(strqt, ss_params.rr.next_spoke);
193 if (strq == NULL) {
194 strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
195 }
196 }
197
198 /*
199 * If CMT is off, we must validate that the stream in question has
200 * the first item pointed towards are network destination requested
201 * by the caller. Note that if we turn out to be locked to a stream
202 * (assigning TSN's then we must stop, since we cannot look for
203 * another stream with data to send to that destination). In CMT's
204 * case, by skipping this check, we will send one data packet
205 * towards the requested net.
206 */
207 if (net != NULL && strq != NULL &&
208 SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
209 if (TAILQ_FIRST(&strq->outqueue) &&
210 TAILQ_FIRST(&strq->outqueue)->net != NULL &&
211 TAILQ_FIRST(&strq->outqueue)->net != net) {
212 if (strq == asoc->ss_data.last_out_stream) {
213 return (NULL);
214 } else {
215 strqt = strq;
216 goto default_again;
217 }
218 }
219 }
220 return (strq);
221 }
222
223 static void
224 sctp_ss_default_scheduled(struct sctp_tcb *stcb,
225 struct sctp_nets *net SCTP_UNUSED,
226 struct sctp_association *asoc,
227 struct sctp_stream_out *strq,
228 int moved_how_much SCTP_UNUSED)
229 {
230 struct sctp_stream_queue_pending *sp;
231
232 asoc->ss_data.last_out_stream = strq;
233 if (stcb->asoc.idata_supported == 0) {
234 sp = TAILQ_FIRST(&strq->outqueue);
235 if ((sp != NULL) && (sp->some_taken == 1)) {
236 stcb->asoc.ss_data.locked_on_sending = strq;
237 } else {
238 stcb->asoc.ss_data.locked_on_sending = NULL;
239 }
240 } else {
241 stcb->asoc.ss_data.locked_on_sending = NULL;
242 }
243 return;
244 }
245
246 static void
247 sctp_ss_default_packet_done(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net SCTP_UNUSED,
248 struct sctp_association *asoc SCTP_UNUSED)
249 {
250 /* Nothing to be done here */
251 return;
252 }
253
254 static int
255 sctp_ss_default_get_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED,
256 struct sctp_stream_out *strq SCTP_UNUSED, uint16_t * value SCTP_UNUSED)
257 {
258 /* Nothing to be done here */
259 return (-1);
260 }
261
262 static int
263 sctp_ss_default_set_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED,
264 struct sctp_stream_out *strq SCTP_UNUSED, uint16_t value SCTP_UNUSED)
265 {
266 /* Nothing to be done here */
267 return (-1);
268 }
269
270 static int
271 sctp_ss_default_is_user_msgs_incomplete(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED)
272 {
273 return (0);
274 }
275
276 /*
277 * Real round-robin algorithm.
278 * Always interates the streams in ascending order.
279 */
280 static void
281 sctp_ss_rr_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
282 struct sctp_stream_out *strq,
283 struct sctp_stream_queue_pending *sp SCTP_UNUSED, int holds_lock)
284 {
285 struct sctp_stream_out *strqt;
286
287 if (holds_lock == 0) {
288 SCTP_TCB_SEND_LOCK(stcb);
289 }
290 if (!TAILQ_EMPTY(&strq->outqueue) &&
291 (strq->ss_params.rr.next_spoke.tqe_next == NULL) &&
292 (strq->ss_params.rr.next_spoke.tqe_prev == NULL)) {
293 if (TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
294 TAILQ_INSERT_HEAD(&asoc->ss_data.out.wheel, strq, ss_params.rr.next_spoke);
295 } else {
296 strqt = TAILQ_FIRST(&asoc->ss_data.out.wheel);
297 while (strqt != NULL && (strqt->stream_no < strq->stream_no)) {
298 strqt = TAILQ_NEXT(strqt, ss_params.rr.next_spoke);
299 }
300 if (strqt != NULL) {
301 TAILQ_INSERT_BEFORE(strqt, strq, ss_params.rr.next_spoke);
302 } else {
303 TAILQ_INSERT_TAIL(&asoc->ss_data.out.wheel, strq, ss_params.rr.next_spoke);
304 }
305 }
306 }
307 if (holds_lock == 0) {
308 SCTP_TCB_SEND_UNLOCK(stcb);
309 }
310 return;
311 }
312
313 /*
314 * Real round-robin per packet algorithm.
315 * Always interates the streams in ascending order and
316 * only fills messages of the same stream in a packet.
317 */
318 static struct sctp_stream_out *
319 sctp_ss_rrp_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net SCTP_UNUSED,
320 struct sctp_association *asoc)
321 {
322 return (asoc->ss_data.last_out_stream);
323 }
324
325 static void
326 sctp_ss_rrp_packet_done(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
327 struct sctp_association *asoc)
328 {
329 struct sctp_stream_out *strq, *strqt;
330
331 strqt = asoc->ss_data.last_out_stream;
332 rrp_again:
333 /* Find the next stream to use */
334 if (strqt == NULL) {
335 strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
336 } else {
337 strq = TAILQ_NEXT(strqt, ss_params.rr.next_spoke);
338 if (strq == NULL) {
339 strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
340 }
341 }
342
343 /*
344 * If CMT is off, we must validate that the stream in question has
345 * the first item pointed towards are network destination requested
346 * by the caller. Note that if we turn out to be locked to a stream
347 * (assigning TSN's then we must stop, since we cannot look for
348 * another stream with data to send to that destination). In CMT's
349 * case, by skipping this check, we will send one data packet
350 * towards the requested net.
351 */
352 if (net != NULL && strq != NULL &&
353 SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
354 if (TAILQ_FIRST(&strq->outqueue) &&
355 TAILQ_FIRST(&strq->outqueue)->net != NULL &&
356 TAILQ_FIRST(&strq->outqueue)->net != net) {
357 if (strq == asoc->ss_data.last_out_stream) {
358 strq = NULL;
359 } else {
360 strqt = strq;
361 goto rrp_again;
362 }
363 }
364 }
365 asoc->ss_data.last_out_stream = strq;
366 return;
367 }
368
369
370 /*
371 * Priority algorithm.
372 * Always prefers streams based on their priority id.
373 */
374 static void
375 sctp_ss_prio_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
376 int clear_values, int holds_lock)
377 {
378 if (holds_lock == 0) {
379 SCTP_TCB_SEND_LOCK(stcb);
380 }
381 while (!TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
382 struct sctp_stream_out *strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
383
384 if (clear_values) {
385 strq->ss_params.prio.priority = 0;
386 }
387 TAILQ_REMOVE(&asoc->ss_data.out.wheel, TAILQ_FIRST(&asoc->ss_data.out.wheel), ss_params.prio.next_spoke);
388 strq->ss_params.prio.next_spoke.tqe_next = NULL;
389 strq->ss_params.prio.next_spoke.tqe_prev = NULL;
390
391 }
392 asoc->ss_data.last_out_stream = NULL;
393 if (holds_lock == 0) {
394 SCTP_TCB_SEND_UNLOCK(stcb);
395 }
396 return;
397 }
398
399 static void
400 sctp_ss_prio_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
401 {
402 if (with_strq != NULL) {
403 if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
404 stcb->asoc.ss_data.locked_on_sending = strq;
405 }
406 if (stcb->asoc.ss_data.last_out_stream == with_strq) {
407 stcb->asoc.ss_data.last_out_stream = strq;
408 }
409 }
410 strq->ss_params.prio.next_spoke.tqe_next = NULL;
411 strq->ss_params.prio.next_spoke.tqe_prev = NULL;
412 if (with_strq != NULL) {
413 strq->ss_params.prio.priority = with_strq->ss_params.prio.priority;
414 } else {
415 strq->ss_params.prio.priority = 0;
416 }
417 return;
418 }
419
420 static void
421 sctp_ss_prio_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
422 struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED,
423 int holds_lock)
424 {
425 struct sctp_stream_out *strqt;
426
427 if (holds_lock == 0) {
428 SCTP_TCB_SEND_LOCK(stcb);
429 }
430 /* Add to wheel if not already on it and stream queue not empty */
431 if (!TAILQ_EMPTY(&strq->outqueue) &&
432 (strq->ss_params.prio.next_spoke.tqe_next == NULL) &&
433 (strq->ss_params.prio.next_spoke.tqe_prev == NULL)) {
434 if (TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
435 TAILQ_INSERT_HEAD(&asoc->ss_data.out.wheel, strq, ss_params.prio.next_spoke);
436 } else {
437 strqt = TAILQ_FIRST(&asoc->ss_data.out.wheel);
438 while (strqt != NULL && strqt->ss_params.prio.priority < strq->ss_params.prio.priority) {
439 strqt = TAILQ_NEXT(strqt, ss_params.prio.next_spoke);
440 }
441 if (strqt != NULL) {
442 TAILQ_INSERT_BEFORE(strqt, strq, ss_params.prio.next_spoke);
443 } else {
444 TAILQ_INSERT_TAIL(&asoc->ss_data.out.wheel, strq, ss_params.prio.next_spoke);
445 }
446 }
447 }
448 if (holds_lock == 0) {
449 SCTP_TCB_SEND_UNLOCK(stcb);
450 }
451 return;
452 }
453
454 static void
455 sctp_ss_prio_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
456 struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED,
457 int holds_lock)
458 {
459 if (holds_lock == 0) {
460 SCTP_TCB_SEND_LOCK(stcb);
461 }
462 /*
463 * Remove from wheel if stream queue is empty and actually is on the
464 * wheel
465 */
466 if (TAILQ_EMPTY(&strq->outqueue) &&
467 (strq->ss_params.prio.next_spoke.tqe_next != NULL ||
468 strq->ss_params.prio.next_spoke.tqe_prev != NULL)) {
469 if (asoc->ss_data.last_out_stream == strq) {
470 asoc->ss_data.last_out_stream = TAILQ_PREV(asoc->ss_data.last_out_stream, sctpwheel_listhead,
471 ss_params.prio.next_spoke);
472 if (asoc->ss_data.last_out_stream == NULL) {
473 asoc->ss_data.last_out_stream = TAILQ_LAST(&asoc->ss_data.out.wheel,
474 sctpwheel_listhead);
475 }
476 if (asoc->ss_data.last_out_stream == strq) {
477 asoc->ss_data.last_out_stream = NULL;
478 }
479 }
480 TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.prio.next_spoke);
481 strq->ss_params.prio.next_spoke.tqe_next = NULL;
482 strq->ss_params.prio.next_spoke.tqe_prev = NULL;
483 }
484 if (holds_lock == 0) {
485 SCTP_TCB_SEND_UNLOCK(stcb);
486 }
487 return;
488 }
489
490 static struct sctp_stream_out *
491 sctp_ss_prio_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
492 struct sctp_association *asoc)
493 {
494 struct sctp_stream_out *strq, *strqt, *strqn;
495
496 strqt = asoc->ss_data.last_out_stream;
497 prio_again:
498 /* Find the next stream to use */
499 if (strqt == NULL) {
500 strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
501 } else {
502 strqn = TAILQ_NEXT(strqt, ss_params.prio.next_spoke);
503 if (strqn != NULL &&
504 strqn->ss_params.prio.priority == strqt->ss_params.prio.priority) {
505 strq = strqn;
506 } else {
507 strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
508 }
509 }
510
511 /*
512 * If CMT is off, we must validate that the stream in question has
513 * the first item pointed towards are network destination requested
514 * by the caller. Note that if we turn out to be locked to a stream
515 * (assigning TSN's then we must stop, since we cannot look for
516 * another stream with data to send to that destination). In CMT's
517 * case, by skipping this check, we will send one data packet
518 * towards the requested net.
519 */
520 if (net != NULL && strq != NULL &&
521 SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
522 if (TAILQ_FIRST(&strq->outqueue) &&
523 TAILQ_FIRST(&strq->outqueue)->net != NULL &&
524 TAILQ_FIRST(&strq->outqueue)->net != net) {
525 if (strq == asoc->ss_data.last_out_stream) {
526 return (NULL);
527 } else {
528 strqt = strq;
529 goto prio_again;
530 }
531 }
532 }
533 return (strq);
534 }
535
536 static int
537 sctp_ss_prio_get_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED,
538 struct sctp_stream_out *strq, uint16_t * value)
539 {
540 if (strq == NULL) {
541 return (-1);
542 }
543 *value = strq->ss_params.prio.priority;
544 return (1);
545 }
546
547 static int
548 sctp_ss_prio_set_value(struct sctp_tcb *stcb, struct sctp_association *asoc,
549 struct sctp_stream_out *strq, uint16_t value)
550 {
551 if (strq == NULL) {
552 return (-1);
553 }
554 strq->ss_params.prio.priority = value;
555 sctp_ss_prio_remove(stcb, asoc, strq, NULL, 1);
556 sctp_ss_prio_add(stcb, asoc, strq, NULL, 1);
557 return (1);
558 }
559
560 /*
561 * Fair bandwidth algorithm.
562 * Maintains an equal troughput per stream.
563 */
564 static void
565 sctp_ss_fb_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
566 int clear_values, int holds_lock)
567 {
568 if (holds_lock == 0) {
569 SCTP_TCB_SEND_LOCK(stcb);
570 }
571 while (!TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
572 struct sctp_stream_out *strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
573
574 if (clear_values) {
575 strq->ss_params.fb.rounds = -1;
576 }
577 TAILQ_REMOVE(&asoc->ss_data.out.wheel, TAILQ_FIRST(&asoc->ss_data.out.wheel), ss_params.fb.next_spoke);
578 strq->ss_params.fb.next_spoke.tqe_next = NULL;
579 strq->ss_params.fb.next_spoke.tqe_prev = NULL;
580 }
581 asoc->ss_data.last_out_stream = NULL;
582 if (holds_lock == 0) {
583 SCTP_TCB_SEND_UNLOCK(stcb);
584 }
585 return;
586 }
587
588 static void
589 sctp_ss_fb_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
590 {
591 if (with_strq != NULL) {
592 if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
593 stcb->asoc.ss_data.locked_on_sending = strq;
594 }
595 if (stcb->asoc.ss_data.last_out_stream == with_strq) {
596 stcb->asoc.ss_data.last_out_stream = strq;
597 }
598 }
599 strq->ss_params.fb.next_spoke.tqe_next = NULL;
600 strq->ss_params.fb.next_spoke.tqe_prev = NULL;
601 if (with_strq != NULL) {
602 strq->ss_params.fb.rounds = with_strq->ss_params.fb.rounds;
603 } else {
604 strq->ss_params.fb.rounds = -1;
605 }
606 return;
607 }
608
609 static void
610 sctp_ss_fb_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
611 struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED,
612 int holds_lock)
613 {
614 if (holds_lock == 0) {
615 SCTP_TCB_SEND_LOCK(stcb);
616 }
617 if (!TAILQ_EMPTY(&strq->outqueue) &&
618 (strq->ss_params.fb.next_spoke.tqe_next == NULL) &&
619 (strq->ss_params.fb.next_spoke.tqe_prev == NULL)) {
620 if (strq->ss_params.fb.rounds < 0)
621 strq->ss_params.fb.rounds = TAILQ_FIRST(&strq->outqueue)->length;
622 TAILQ_INSERT_TAIL(&asoc->ss_data.out.wheel, strq, ss_params.fb.next_spoke);
623 }
624 if (holds_lock == 0) {
625 SCTP_TCB_SEND_UNLOCK(stcb);
626 }
627 return;
628 }
629
630 static void
631 sctp_ss_fb_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
632 struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED,
633 int holds_lock)
634 {
635 if (holds_lock == 0) {
636 SCTP_TCB_SEND_LOCK(stcb);
637 }
638 /*
639 * Remove from wheel if stream queue is empty and actually is on the
640 * wheel
641 */
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
666 static struct sctp_stream_out *
667 sctp_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
699 static void
700 sctp_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 */
737 static void
738 sctp_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
742 static void
743 sctp_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, the scheduler of
753 * an existing association has been changed. We can only cycle
754 * through the stream queues and add everything to the FCFS queue.
755 */
756 while (add_more) {
757 add_more = 0;
758 for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
759 sp = TAILQ_FIRST(&stcb->asoc.strmout[i].outqueue);
760 x = 0;
761 /* Find n. message in current stream queue */
762 while (sp != NULL && x < n) {
763 sp = TAILQ_NEXT(sp, next);
764 x++;
765 }
766 if (sp != NULL) {
767 sctp_ss_fcfs_add(stcb, &stcb->asoc, &stcb->asoc.strmout[i], sp, holds_lock);
768 add_more = 1;
769 }
770 }
771 n++;
772 }
773 return;
774 }
775
776 static void
777 sctp_ss_fcfs_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
778 int clear_values, int holds_lock)
779 {
780 if (clear_values) {
781 if (holds_lock == 0) {
782 SCTP_TCB_SEND_LOCK(stcb);
783 }
784 while (!TAILQ_EMPTY(&asoc->ss_data.out.list)) {
785 TAILQ_REMOVE(&asoc->ss_data.out.list, TAILQ_FIRST(&asoc->ss_data.out.list), ss_next);
786 }
787 if (holds_lock == 0) {
788 SCTP_TCB_SEND_UNLOCK(stcb);
789 }
790 }
791 return;
792 }
793
794 static void
795 sctp_ss_fcfs_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
796 {
797 if (with_strq != NULL) {
798 if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
799 stcb->asoc.ss_data.locked_on_sending = strq;
800 }
801 if (stcb->asoc.ss_data.last_out_stream == with_strq) {
802 stcb->asoc.ss_data.last_out_stream = strq;
803 }
804 }
805 return;
806 }
807
808 static void
809 sctp_ss_fcfs_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
810 struct sctp_stream_out *strq SCTP_UNUSED, struct sctp_stream_queue_pending *sp,
811 int holds_lock)
812 {
813 if (holds_lock == 0) {
814 SCTP_TCB_SEND_LOCK(stcb);
815 }
816 if (sp && (sp->ss_next.tqe_next == NULL) &&
817 (sp->ss_next.tqe_prev == NULL)) {
818 TAILQ_INSERT_TAIL(&asoc->ss_data.out.list, sp, ss_next);
819 }
820 if (holds_lock == 0) {
821 SCTP_TCB_SEND_UNLOCK(stcb);
822 }
823 return;
824 }
825
826 static int
827 sctp_ss_fcfs_is_empty(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc)
828 {
829 if (TAILQ_EMPTY(&asoc->ss_data.out.list)) {
830 return (1);
831 } else {
832 return (0);
833 }
834 }
835
836 static void
837 sctp_ss_fcfs_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
838 struct sctp_stream_out *strq SCTP_UNUSED, struct sctp_stream_queue_pending *sp,
839 int holds_lock)
840 {
841 if (holds_lock == 0) {
842 SCTP_TCB_SEND_LOCK(stcb);
843 }
844 if (sp &&
845 ((sp->ss_next.tqe_next != NULL) ||
846 (sp->ss_next.tqe_prev != NULL))) {
847 TAILQ_REMOVE(&asoc->ss_data.out.list, sp, ss_next);
848 }
849 if (holds_lock == 0) {
850 SCTP_TCB_SEND_UNLOCK(stcb);
851 }
852 return;
853 }
854
855
856 static struct sctp_stream_out *
857 sctp_ss_fcfs_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
858 struct sctp_association *asoc)
859 {
860 struct sctp_stream_out *strq;
861 struct sctp_stream_queue_pending *sp;
862
863 sp = TAILQ_FIRST(&asoc->ss_data.out.list);
864 default_again:
865 if (sp != NULL) {
866 strq = &asoc->strmout[sp->stream];
867 } else {
868 strq = NULL;
869 }
870
871 /*
872 * If CMT is off, we must validate that the stream in question has
873 * the first item pointed towards are network destination requested
874 * by the caller. Note that if we turn out to be locked to a stream
875 * (assigning TSN's then we must stop, since we cannot look for
876 * another stream with data to send to that destination). In CMT's
877 * case, by skipping this check, we will send one data packet
878 * towards the requested net.
879 */
880 if (net != NULL && strq != NULL &&
881 SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
882 if (TAILQ_FIRST(&strq->outqueue) &&
883 TAILQ_FIRST(&strq->outqueue)->net != NULL &&
884 TAILQ_FIRST(&strq->outqueue)->net != net) {
885 sp = TAILQ_NEXT(sp, ss_next);
886 goto default_again;
887 }
888 }
889 return (strq);
890 }
891
892 const struct sctp_ss_functions sctp_ss_functions[] = {
893 /* SCTP_SS_DEFAULT */
894 {
895 .sctp_ss_init = sctp_ss_default_init,
896 .sctp_ss_clear = sctp_ss_default_clear,
897 .sctp_ss_init_stream = sctp_ss_default_init_stream,
898 .sctp_ss_add_to_stream = sctp_ss_default_add,
899 .sctp_ss_is_empty = sctp_ss_default_is_empty,
900 .sctp_ss_remove_from_stream = sctp_ss_default_remove,
901 .sctp_ss_select_stream = sctp_ss_default_select,
902 .sctp_ss_scheduled = sctp_ss_default_scheduled,
903 .sctp_ss_packet_done = sctp_ss_default_packet_done,
904 .sctp_ss_get_value = sctp_ss_default_get_value,
905 .sctp_ss_set_value = sctp_ss_default_set_value,
906 .sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
907 },
908 /* SCTP_SS_ROUND_ROBIN */
909 {
910 .sctp_ss_init = sctp_ss_default_init,
911 .sctp_ss_clear = sctp_ss_default_clear,
912 .sctp_ss_init_stream = sctp_ss_default_init_stream,
913 .sctp_ss_add_to_stream = sctp_ss_rr_add,
914 .sctp_ss_is_empty = sctp_ss_default_is_empty,
915 .sctp_ss_remove_from_stream = sctp_ss_default_remove,
916 .sctp_ss_select_stream = sctp_ss_default_select,
917 .sctp_ss_scheduled = sctp_ss_default_scheduled,
918 .sctp_ss_packet_done = sctp_ss_default_packet_done,
919 .sctp_ss_get_value = sctp_ss_default_get_value,
920 .sctp_ss_set_value = sctp_ss_default_set_value,
921 .sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
922 },
923 /* SCTP_SS_ROUND_ROBIN_PACKET */
924 {
925 .sctp_ss_init = sctp_ss_default_init,
926 .sctp_ss_clear = sctp_ss_default_clear,
927 .sctp_ss_init_stream = sctp_ss_default_init_stream,
928 .sctp_ss_add_to_stream = sctp_ss_rr_add,
929 .sctp_ss_is_empty = sctp_ss_default_is_empty,
930 .sctp_ss_remove_from_stream = sctp_ss_default_remove,
931 .sctp_ss_select_stream = sctp_ss_rrp_select,
932 .sctp_ss_scheduled = sctp_ss_default_scheduled,
933 .sctp_ss_packet_done = sctp_ss_rrp_packet_done,
934 .sctp_ss_get_value = sctp_ss_default_get_value,
935 .sctp_ss_set_value = sctp_ss_default_set_value,
936 .sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
937 },
938 /* SCTP_SS_PRIORITY */
939 {
940 .sctp_ss_init = sctp_ss_default_init,
941 .sctp_ss_clear = sctp_ss_prio_clear,
942 .sctp_ss_init_stream = sctp_ss_prio_init_stream,
943 .sctp_ss_add_to_stream = sctp_ss_prio_add,
944 .sctp_ss_is_empty = sctp_ss_default_is_empty,
945 .sctp_ss_remove_from_stream = sctp_ss_prio_remove,
946 .sctp_ss_select_stream = sctp_ss_prio_select,
947 .sctp_ss_scheduled = sctp_ss_default_scheduled,
948 .sctp_ss_packet_done = sctp_ss_default_packet_done,
949 .sctp_ss_get_value = sctp_ss_prio_get_value,
950 .sctp_ss_set_value = sctp_ss_prio_set_value,
951 .sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
952 },
953 /* SCTP_SS_FAIR_BANDWITH */
954 {
955 .sctp_ss_init = sctp_ss_default_init,
956 .sctp_ss_clear = sctp_ss_fb_clear,
957 .sctp_ss_init_stream = sctp_ss_fb_init_stream,
958 .sctp_ss_add_to_stream = sctp_ss_fb_add,
959 .sctp_ss_is_empty = sctp_ss_default_is_empty,
960 .sctp_ss_remove_from_stream = sctp_ss_fb_remove,
961 .sctp_ss_select_stream = sctp_ss_fb_select,
962 .sctp_ss_scheduled = sctp_ss_fb_scheduled,
963 .sctp_ss_packet_done = sctp_ss_default_packet_done,
964 .sctp_ss_get_value = sctp_ss_default_get_value,
965 .sctp_ss_set_value = sctp_ss_default_set_value,
966 .sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
967 },
968 /* SCTP_SS_FIRST_COME */
969 {
970 .sctp_ss_init = sctp_ss_fcfs_init,
971 .sctp_ss_clear = sctp_ss_fcfs_clear,
972 .sctp_ss_init_stream = sctp_ss_fcfs_init_stream,
973 .sctp_ss_add_to_stream = sctp_ss_fcfs_add,
974 .sctp_ss_is_empty = sctp_ss_fcfs_is_empty,
975 .sctp_ss_remove_from_stream = sctp_ss_fcfs_remove,
976 .sctp_ss_select_stream = sctp_ss_fcfs_select,
977 .sctp_ss_scheduled = sctp_ss_default_scheduled,
978 .sctp_ss_packet_done = sctp_ss_default_packet_done,
979 .sctp_ss_get_value = sctp_ss_default_get_value,
980 .sctp_ss_set_value = sctp_ss_default_set_value,
981 .sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
982 }
983 };
Cache object: e99ab2d5ca11a7226e3544dbe789b83d
|