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