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