]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netinet/sctp_ss_functions.c
Use memset/memcpy instead of bzero/bcopy.
[FreeBSD/FreeBSD.git] / sys / netinet / sctp_ss_functions.c
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$");
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->sid < strq->sid)) {
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->sid];
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 };