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
sctp_ss_default_init(struct sctp_tcb * stcb,struct sctp_association * asoc,int holds_lock)55 sctp_ss_default_init(struct sctp_tcb *stcb, struct sctp_association *asoc,
56 int holds_lock)
57 {
58 uint16_t i;
59
60 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 for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
68 stcb->asoc.ss_functions.sctp_ss_add_to_stream(stcb, &stcb->asoc,
69 &stcb->asoc.strmout[i],
70 NULL, holds_lock);
71 }
72 return;
73 }
74
75 static void
sctp_ss_default_clear(struct sctp_tcb * stcb,struct sctp_association * asoc,int clear_values SCTP_UNUSED,int holds_lock)76 sctp_ss_default_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
77 int clear_values SCTP_UNUSED, int holds_lock)
78 {
79 if (holds_lock == 0) {
80 SCTP_TCB_SEND_LOCK(stcb);
81 }
82 while (!TAILQ_EMPTY(&asoc->ss_data.out_wheel)) {
83 struct sctp_stream_out *strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
84 TAILQ_REMOVE(&asoc->ss_data.out_wheel, TAILQ_FIRST(&asoc->ss_data.out_wheel), ss_params.rr.next_spoke);
85 strq->ss_params.rr.next_spoke.tqe_next = NULL;
86 strq->ss_params.rr.next_spoke.tqe_prev = NULL;
87 }
88 asoc->last_out_stream = NULL;
89 if (holds_lock == 0) {
90 SCTP_TCB_SEND_UNLOCK(stcb);
91 }
92 return;
93 }
94
95 static void
sctp_ss_default_init_stream(struct sctp_stream_out * strq,struct sctp_stream_out * with_strq SCTP_UNUSED)96 sctp_ss_default_init_stream(struct sctp_stream_out *strq, struct sctp_stream_out *with_strq SCTP_UNUSED)
97 {
98 strq->ss_params.rr.next_spoke.tqe_next = NULL;
99 strq->ss_params.rr.next_spoke.tqe_prev = NULL;
100 return;
101 }
102
103 static void
sctp_ss_default_add(struct sctp_tcb * stcb,struct sctp_association * asoc,struct sctp_stream_out * strq,struct sctp_stream_queue_pending * sp SCTP_UNUSED,int holds_lock)104 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 if (holds_lock == 0) {
109 SCTP_TCB_SEND_LOCK(stcb);
110 }
111 /* Add to wheel if not already on it and stream queue not empty */
112 if (!TAILQ_EMPTY(&strq->outqueue) &&
113 (strq->ss_params.rr.next_spoke.tqe_next == NULL) &&
114 (strq->ss_params.rr.next_spoke.tqe_prev == NULL)) {
115 TAILQ_INSERT_TAIL(&asoc->ss_data.out_wheel,
116 strq, ss_params.rr.next_spoke);
117 }
118 if (holds_lock == 0) {
119 SCTP_TCB_SEND_UNLOCK(stcb);
120 }
121 return;
122 }
123
124 static int
sctp_ss_default_is_empty(struct sctp_tcb * stcb SCTP_UNUSED,struct sctp_association * asoc)125 sctp_ss_default_is_empty(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc)
126 {
127 if (TAILQ_EMPTY(&asoc->ss_data.out_wheel)) {
128 return (1);
129 } else {
130 return (0);
131 }
132 }
133
134 static void
sctp_ss_default_remove(struct sctp_tcb * stcb,struct sctp_association * asoc,struct sctp_stream_out * strq,struct sctp_stream_queue_pending * sp SCTP_UNUSED,int holds_lock)135 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 if (holds_lock == 0) {
140 SCTP_TCB_SEND_LOCK(stcb);
141 }
142 /* Remove from wheel if stream queue is empty and actually is on the wheel */
143 if (TAILQ_EMPTY(&strq->outqueue) &&
144 (strq->ss_params.rr.next_spoke.tqe_next != NULL ||
145 strq->ss_params.rr.next_spoke.tqe_prev != NULL)) {
146 if (asoc->last_out_stream == strq) {
147 asoc->last_out_stream = TAILQ_PREV(asoc->last_out_stream,
148 sctpwheel_listhead,
149 ss_params.rr.next_spoke);
150 if (asoc->last_out_stream == NULL) {
151 asoc->last_out_stream = TAILQ_LAST(&asoc->ss_data.out_wheel,
152 sctpwheel_listhead);
153 }
154 if (asoc->last_out_stream == strq) {
155 asoc->last_out_stream = NULL;
156 }
157 }
158 TAILQ_REMOVE(&asoc->ss_data.out_wheel, strq, ss_params.rr.next_spoke);
159 strq->ss_params.rr.next_spoke.tqe_next = NULL;
160 strq->ss_params.rr.next_spoke.tqe_prev = NULL;
161 }
162 if (holds_lock == 0) {
163 SCTP_TCB_SEND_UNLOCK(stcb);
164 }
165 return;
166 }
167
168
169 static struct sctp_stream_out *
sctp_ss_default_select(struct sctp_tcb * stcb SCTP_UNUSED,struct sctp_nets * net,struct sctp_association * asoc)170 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 strqt = asoc->last_out_stream;
176 default_again:
177 /* Find the next stream to use */
178 if (strqt == NULL) {
179 strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
180 } else {
181 strq = TAILQ_NEXT(strqt, ss_params.rr.next_spoke);
182 if (strq == NULL) {
183 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 if (net != NULL && strq != NULL &&
199 SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
200 if (TAILQ_FIRST(&strq->outqueue) &&
201 TAILQ_FIRST(&strq->outqueue)->net != NULL &&
202 TAILQ_FIRST(&strq->outqueue)->net != net) {
203 if (strq == asoc->last_out_stream) {
204 return (NULL);
205 } else {
206 strqt = strq;
207 goto default_again;
208 }
209 }
210 }
211 return (strq);
212 }
213
214 static void
sctp_ss_default_scheduled(struct sctp_tcb * stcb SCTP_UNUSED,struct sctp_nets * net SCTP_UNUSED,struct sctp_association * asoc SCTP_UNUSED,struct sctp_stream_out * strq,int moved_how_much SCTP_UNUSED)215 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 asoc->last_out_stream = strq;
220 return;
221 }
222
223 static void
sctp_ss_default_packet_done(struct sctp_tcb * stcb SCTP_UNUSED,struct sctp_nets * net SCTP_UNUSED,struct sctp_association * asoc SCTP_UNUSED)224 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 return;
229 }
230
231 static int
sctp_ss_default_get_value(struct sctp_tcb * stcb SCTP_UNUSED,struct sctp_association * asoc SCTP_UNUSED,struct sctp_stream_out * strq SCTP_UNUSED,uint16_t * value SCTP_UNUSED)232 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 return (-1);
237 }
238
239 static int
sctp_ss_default_set_value(struct sctp_tcb * stcb SCTP_UNUSED,struct sctp_association * asoc SCTP_UNUSED,struct sctp_stream_out * strq SCTP_UNUSED,uint16_t value SCTP_UNUSED)240 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 return (-1);
245 }
246
247 /*
248 * Real round-robin algorithm.
249 * Always interates the streams in ascending order.
250 */
251 static void
sctp_ss_rr_add(struct sctp_tcb * stcb,struct sctp_association * asoc,struct sctp_stream_out * strq,struct sctp_stream_queue_pending * sp SCTP_UNUSED,int holds_lock)252 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 if (holds_lock == 0) {
259 SCTP_TCB_SEND_LOCK(stcb);
260 }
261 if (!TAILQ_EMPTY(&strq->outqueue) &&
262 (strq->ss_params.rr.next_spoke.tqe_next == NULL) &&
263 (strq->ss_params.rr.next_spoke.tqe_prev == NULL)) {
264 if (TAILQ_EMPTY(&asoc->ss_data.out_wheel)) {
265 TAILQ_INSERT_HEAD(&asoc->ss_data.out_wheel, strq, ss_params.rr.next_spoke);
266 } else {
267 strqt = TAILQ_FIRST(&asoc->ss_data.out_wheel);
268 while (strqt != NULL && (strqt->stream_no < strq->stream_no)) {
269 strqt = TAILQ_NEXT(strqt, ss_params.rr.next_spoke);
270 }
271 if (strqt != NULL) {
272 TAILQ_INSERT_BEFORE(strqt, strq, ss_params.rr.next_spoke);
273 } else {
274 TAILQ_INSERT_TAIL(&asoc->ss_data.out_wheel, strq, ss_params.rr.next_spoke);
275 }
276 }
277 }
278 if (holds_lock == 0) {
279 SCTP_TCB_SEND_UNLOCK(stcb);
280 }
281 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 *
sctp_ss_rrp_select(struct sctp_tcb * stcb SCTP_UNUSED,struct sctp_nets * net SCTP_UNUSED,struct sctp_association * asoc)290 sctp_ss_rrp_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net SCTP_UNUSED,
291 struct sctp_association *asoc)
292 {
293 return (asoc->last_out_stream);
294 }
295
296 static void
sctp_ss_rrp_packet_done(struct sctp_tcb * stcb SCTP_UNUSED,struct sctp_nets * net,struct sctp_association * asoc)297 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 strqt = asoc->last_out_stream;
303 rrp_again:
304 /* Find the next stream to use */
305 if (strqt == NULL) {
306 strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
307 } else {
308 strq = TAILQ_NEXT(strqt, ss_params.rr.next_spoke);
309 if (strq == NULL) {
310 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 if (net != NULL && strq != NULL &&
326 SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
327 if (TAILQ_FIRST(&strq->outqueue) &&
328 TAILQ_FIRST(&strq->outqueue)->net != NULL &&
329 TAILQ_FIRST(&strq->outqueue)->net != net) {
330 if (strq == asoc->last_out_stream) {
331 strq = NULL;
332 } else {
333 strqt = strq;
334 goto rrp_again;
335 }
336 }
337 }
338 asoc->last_out_stream = strq;
339 return;
340 }
341
342
343 /*
344 * Priority algorithm.
345 * Always prefers streams based on their priority id.
346 */
347 static void
sctp_ss_prio_clear(struct sctp_tcb * stcb,struct sctp_association * asoc,int clear_values,int holds_lock)348 sctp_ss_prio_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
349 int clear_values, int holds_lock)
350 {
351 if (holds_lock == 0) {
352 SCTP_TCB_SEND_LOCK(stcb);
353 }
354 while (!TAILQ_EMPTY(&asoc->ss_data.out_wheel)) {
355 struct sctp_stream_out *strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
356 if (clear_values) {
357 strq->ss_params.prio.priority = 0;
358 }
359 TAILQ_REMOVE(&asoc->ss_data.out_wheel, TAILQ_FIRST(&asoc->ss_data.out_wheel), ss_params.prio.next_spoke);
360 strq->ss_params.prio.next_spoke.tqe_next = NULL;
361 strq->ss_params.prio.next_spoke.tqe_prev = NULL;
362
363 }
364 asoc->last_out_stream = NULL;
365 if (holds_lock == 0) {
366 SCTP_TCB_SEND_UNLOCK(stcb);
367 }
368 return;
369 }
370
371 static void
sctp_ss_prio_init_stream(struct sctp_stream_out * strq,struct sctp_stream_out * with_strq)372 sctp_ss_prio_init_stream(struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
373 {
374 strq->ss_params.prio.next_spoke.tqe_next = NULL;
375 strq->ss_params.prio.next_spoke.tqe_prev = NULL;
376 if (with_strq != NULL) {
377 strq->ss_params.prio.priority = with_strq->ss_params.prio.priority;
378 } else {
379 strq->ss_params.prio.priority = 0;
380 }
381 return;
382 }
383
384 static void
sctp_ss_prio_add(struct sctp_tcb * stcb,struct sctp_association * asoc,struct sctp_stream_out * strq,struct sctp_stream_queue_pending * sp SCTP_UNUSED,int holds_lock)385 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 if (holds_lock == 0) {
392 SCTP_TCB_SEND_LOCK(stcb);
393 }
394 /* Add to wheel if not already on it and stream queue not empty */
395 if (!TAILQ_EMPTY(&strq->outqueue) &&
396 (strq->ss_params.prio.next_spoke.tqe_next == NULL) &&
397 (strq->ss_params.prio.next_spoke.tqe_prev == NULL)) {
398 if (TAILQ_EMPTY(&asoc->ss_data.out_wheel)) {
399 TAILQ_INSERT_HEAD(&asoc->ss_data.out_wheel, strq, ss_params.prio.next_spoke);
400 } else {
401 strqt = TAILQ_FIRST(&asoc->ss_data.out_wheel);
402 while (strqt != NULL && strqt->ss_params.prio.priority < strq->ss_params.prio.priority) {
403 strqt = TAILQ_NEXT(strqt, ss_params.prio.next_spoke);
404 }
405 if (strqt != NULL) {
406 TAILQ_INSERT_BEFORE(strqt, strq, ss_params.prio.next_spoke);
407 } else {
408 TAILQ_INSERT_TAIL(&asoc->ss_data.out_wheel, strq, ss_params.prio.next_spoke);
409 }
410 }
411 }
412 if (holds_lock == 0) {
413 SCTP_TCB_SEND_UNLOCK(stcb);
414 }
415 return;
416 }
417
418 static void
sctp_ss_prio_remove(struct sctp_tcb * stcb,struct sctp_association * asoc,struct sctp_stream_out * strq,struct sctp_stream_queue_pending * sp SCTP_UNUSED,int holds_lock)419 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 if (holds_lock == 0) {
424 SCTP_TCB_SEND_LOCK(stcb);
425 }
426 /* Remove from wheel if stream queue is empty and actually is on the wheel */
427 if (TAILQ_EMPTY(&strq->outqueue) &&
428 (strq->ss_params.prio.next_spoke.tqe_next != NULL ||
429 strq->ss_params.prio.next_spoke.tqe_prev != NULL)) {
430 if (asoc->last_out_stream == strq) {
431 asoc->last_out_stream = TAILQ_PREV(asoc->last_out_stream, sctpwheel_listhead,
432 ss_params.prio.next_spoke);
433 if (asoc->last_out_stream == NULL) {
434 asoc->last_out_stream = TAILQ_LAST(&asoc->ss_data.out_wheel,
435 sctpwheel_listhead);
436 }
437 if (asoc->last_out_stream == strq) {
438 asoc->last_out_stream = NULL;
439 }
440 }
441 TAILQ_REMOVE(&asoc->ss_data.out_wheel, strq, ss_params.prio.next_spoke);
442 strq->ss_params.prio.next_spoke.tqe_next = NULL;
443 strq->ss_params.prio.next_spoke.tqe_prev = NULL;
444 }
445 if (holds_lock == 0) {
446 SCTP_TCB_SEND_UNLOCK(stcb);
447 }
448 return;
449 }
450
451 static struct sctp_stream_out*
sctp_ss_prio_select(struct sctp_tcb * stcb SCTP_UNUSED,struct sctp_nets * net,struct sctp_association * asoc)452 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 strqt = asoc->last_out_stream;
458 prio_again:
459 /* Find the next stream to use */
460 if (strqt == NULL) {
461 strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
462 } else {
463 strqn = TAILQ_NEXT(strqt, ss_params.prio.next_spoke);
464 if (strqn != NULL &&
465 strqn->ss_params.prio.priority == strqt->ss_params.prio.priority) {
466 strq = strqn;
467 } else {
468 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 if (net != NULL && strq != NULL &&
484 SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
485 if (TAILQ_FIRST(&strq->outqueue) &&
486 TAILQ_FIRST(&strq->outqueue)->net != NULL &&
487 TAILQ_FIRST(&strq->outqueue)->net != net) {
488 if (strq == asoc->last_out_stream) {
489 return (NULL);
490 } else {
491 strqt = strq;
492 goto prio_again;
493 }
494 }
495 }
496 return (strq);
497 }
498
499 static int
sctp_ss_prio_get_value(struct sctp_tcb * stcb SCTP_UNUSED,struct sctp_association * asoc SCTP_UNUSED,struct sctp_stream_out * strq,uint16_t * value)500 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 if (strq == NULL) {
504 return (-1);
505 }
506 *value = strq->ss_params.prio.priority;
507 return (1);
508 }
509
510 static int
sctp_ss_prio_set_value(struct sctp_tcb * stcb,struct sctp_association * asoc,struct sctp_stream_out * strq,uint16_t value)511 sctp_ss_prio_set_value(struct sctp_tcb *stcb, struct sctp_association *asoc,
512 struct sctp_stream_out *strq, uint16_t value)
513 {
514 if (strq == NULL) {
515 return (-1);
516 }
517 strq->ss_params.prio.priority = value;
518 sctp_ss_prio_remove(stcb, asoc, strq, NULL, 1);
519 sctp_ss_prio_add(stcb, asoc, strq, NULL, 1);
520 return (1);
521 }
522
523 /*
524 * Fair bandwidth algorithm.
525 * Maintains an equal troughput per stream.
526 */
527 static void
sctp_ss_fb_clear(struct sctp_tcb * stcb,struct sctp_association * asoc,int clear_values,int holds_lock)528 sctp_ss_fb_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
529 int clear_values, int holds_lock)
530 {
531 if (holds_lock == 0) {
532 SCTP_TCB_SEND_LOCK(stcb);
533 }
534 while (!TAILQ_EMPTY(&asoc->ss_data.out_wheel)) {
535 struct sctp_stream_out *strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
536 if (clear_values) {
537 strq->ss_params.fb.rounds = -1;
538 }
539 TAILQ_REMOVE(&asoc->ss_data.out_wheel, TAILQ_FIRST(&asoc->ss_data.out_wheel), ss_params.fb.next_spoke);
540 strq->ss_params.fb.next_spoke.tqe_next = NULL;
541 strq->ss_params.fb.next_spoke.tqe_prev = NULL;
542 }
543 asoc->last_out_stream = NULL;
544 if (holds_lock == 0) {
545 SCTP_TCB_SEND_UNLOCK(stcb);
546 }
547 return;
548 }
549
550 static void
sctp_ss_fb_init_stream(struct sctp_stream_out * strq,struct sctp_stream_out * with_strq)551 sctp_ss_fb_init_stream(struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
552 {
553 strq->ss_params.fb.next_spoke.tqe_next = NULL;
554 strq->ss_params.fb.next_spoke.tqe_prev = NULL;
555 if (with_strq != NULL) {
556 strq->ss_params.fb.rounds = with_strq->ss_params.fb.rounds;
557 } else {
558 strq->ss_params.fb.rounds = -1;
559 }
560 return;
561 }
562
563 static void
sctp_ss_fb_add(struct sctp_tcb * stcb,struct sctp_association * asoc,struct sctp_stream_out * strq,struct sctp_stream_queue_pending * sp SCTP_UNUSED,int holds_lock)564 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 if (holds_lock == 0) {
569 SCTP_TCB_SEND_LOCK(stcb);
570 }
571 if (!TAILQ_EMPTY(&strq->outqueue) &&
572 (strq->ss_params.fb.next_spoke.tqe_next == NULL) &&
573 (strq->ss_params.fb.next_spoke.tqe_prev == NULL)) {
574 if (strq->ss_params.fb.rounds < 0)
575 strq->ss_params.fb.rounds = TAILQ_FIRST(&strq->outqueue)->length;
576 TAILQ_INSERT_TAIL(&asoc->ss_data.out_wheel, strq, ss_params.fb.next_spoke);
577 }
578 if (holds_lock == 0) {
579 SCTP_TCB_SEND_UNLOCK(stcb);
580 }
581 return;
582 }
583
584 static void
sctp_ss_fb_remove(struct sctp_tcb * stcb,struct sctp_association * asoc,struct sctp_stream_out * strq,struct sctp_stream_queue_pending * sp SCTP_UNUSED,int holds_lock)585 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 if (holds_lock == 0) {
590 SCTP_TCB_SEND_LOCK(stcb);
591 }
592 /* Remove from wheel if stream queue is empty and actually is on the wheel */
593 if (TAILQ_EMPTY(&strq->outqueue) &&
594 (strq->ss_params.fb.next_spoke.tqe_next != NULL ||
595 strq->ss_params.fb.next_spoke.tqe_prev != NULL)) {
596 if (asoc->last_out_stream == strq) {
597 asoc->last_out_stream = TAILQ_PREV(asoc->last_out_stream, sctpwheel_listhead,
598 ss_params.fb.next_spoke);
599 if (asoc->last_out_stream == NULL) {
600 asoc->last_out_stream = TAILQ_LAST(&asoc->ss_data.out_wheel,
601 sctpwheel_listhead);
602 }
603 if (asoc->last_out_stream == strq) {
604 asoc->last_out_stream = NULL;
605 }
606 }
607 TAILQ_REMOVE(&asoc->ss_data.out_wheel, strq, ss_params.fb.next_spoke);
608 strq->ss_params.fb.next_spoke.tqe_next = NULL;
609 strq->ss_params.fb.next_spoke.tqe_prev = NULL;
610 }
611 if (holds_lock == 0) {
612 SCTP_TCB_SEND_UNLOCK(stcb);
613 }
614 return;
615 }
616
617 static struct sctp_stream_out*
sctp_ss_fb_select(struct sctp_tcb * stcb SCTP_UNUSED,struct sctp_nets * net,struct sctp_association * asoc)618 sctp_ss_fb_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
619 struct sctp_association *asoc)
620 {
621 struct sctp_stream_out *strq = NULL, *strqt;
622
623 if (asoc->last_out_stream == NULL ||
624 TAILQ_FIRST(&asoc->ss_data.out_wheel) == TAILQ_LAST(&asoc->ss_data.out_wheel, sctpwheel_listhead)) {
625 strqt = TAILQ_FIRST(&asoc->ss_data.out_wheel);
626 } else {
627 strqt = TAILQ_NEXT(asoc->last_out_stream, ss_params.fb.next_spoke);
628 }
629 do {
630 if ((strqt != NULL) &&
631 ((SCTP_BASE_SYSCTL(sctp_cmt_on_off) > 0) ||
632 (SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0 &&
633 (net == NULL || (TAILQ_FIRST(&strqt->outqueue) && TAILQ_FIRST(&strqt->outqueue)->net == NULL) ||
634 (net != NULL && TAILQ_FIRST(&strqt->outqueue) && TAILQ_FIRST(&strqt->outqueue)->net != NULL &&
635 TAILQ_FIRST(&strqt->outqueue)->net == net))))) {
636 if ((strqt->ss_params.fb.rounds >= 0) && (strq == NULL ||
637 strqt->ss_params.fb.rounds < strq->ss_params.fb.rounds)) {
638 strq = strqt;
639 }
640 }
641 if (strqt != NULL) {
642 strqt = TAILQ_NEXT(strqt, ss_params.fb.next_spoke);
643 } else {
644 strqt = TAILQ_FIRST(&asoc->ss_data.out_wheel);
645 }
646 } while (strqt != strq);
647 return (strq);
648 }
649
650 static void
sctp_ss_fb_scheduled(struct sctp_tcb * stcb SCTP_UNUSED,struct sctp_nets * net SCTP_UNUSED,struct sctp_association * asoc,struct sctp_stream_out * strq,int moved_how_much SCTP_UNUSED)651 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 subtract = strq->ss_params.fb.rounds;
659 TAILQ_FOREACH(strqt, &asoc->ss_data.out_wheel, ss_params.fb.next_spoke) {
660 strqt->ss_params.fb.rounds -= subtract;
661 if (strqt->ss_params.fb.rounds < 0)
662 strqt->ss_params.fb.rounds = 0;
663 }
664 if (TAILQ_FIRST(&strq->outqueue)) {
665 strq->ss_params.fb.rounds = TAILQ_FIRST(&strq->outqueue)->length;
666 } else {
667 strq->ss_params.fb.rounds = -1;
668 }
669 asoc->last_out_stream = strq;
670 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
sctp_ss_fcfs_init(struct sctp_tcb * stcb,struct sctp_association * asoc,int holds_lock)683 sctp_ss_fcfs_init(struct sctp_tcb *stcb, struct sctp_association *asoc,
684 int holds_lock)
685 {
686 uint32_t x, n = 0, add_more = 1;
687 struct sctp_stream_queue_pending *sp;
688 uint16_t i;
689
690 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 while (add_more) {
699 add_more = 0;
700 for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
701 sp = TAILQ_FIRST(&stcb->asoc.strmout[i].outqueue);
702 x = 0;
703 /* Find n. message in current stream queue */
704 while (sp != NULL && x < n) {
705 sp = TAILQ_NEXT(sp, next);
706 x++;
707 }
708 if (sp != NULL) {
709 sctp_ss_fcfs_add(stcb, &stcb->asoc, &stcb->asoc.strmout[i], sp, holds_lock);
710 add_more = 1;
711 }
712 }
713 n++;
714 }
715 return;
716 }
717
718 static void
sctp_ss_fcfs_clear(struct sctp_tcb * stcb,struct sctp_association * asoc,int clear_values,int holds_lock)719 sctp_ss_fcfs_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
720 int clear_values, int holds_lock)
721 {
722 if (clear_values) {
723 if (holds_lock == 0) {
724 SCTP_TCB_SEND_LOCK(stcb);
725 }
726 while (!TAILQ_EMPTY(&asoc->ss_data.out_list)) {
727 TAILQ_REMOVE(&asoc->ss_data.out_list, TAILQ_FIRST(&asoc->ss_data.out_list), ss_next);
728 }
729 if (holds_lock == 0) {
730 SCTP_TCB_SEND_UNLOCK(stcb);
731 }
732 }
733 return;
734 }
735
736 static void
sctp_ss_fcfs_init_stream(struct sctp_stream_out * strq SCTP_UNUSED,struct sctp_stream_out * with_strq SCTP_UNUSED)737 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 return;
741 }
742
743 static void
sctp_ss_fcfs_add(struct sctp_tcb * stcb,struct sctp_association * asoc,struct sctp_stream_out * strq SCTP_UNUSED,struct sctp_stream_queue_pending * sp,int holds_lock)744 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 if (holds_lock == 0) {
749 SCTP_TCB_SEND_LOCK(stcb);
750 }
751 if (sp && (sp->ss_next.tqe_next == NULL) &&
752 (sp->ss_next.tqe_prev == NULL)) {
753 TAILQ_INSERT_TAIL(&asoc->ss_data.out_list, sp, ss_next);
754 }
755 if (holds_lock == 0) {
756 SCTP_TCB_SEND_UNLOCK(stcb);
757 }
758 return;
759 }
760
761 static int
sctp_ss_fcfs_is_empty(struct sctp_tcb * stcb SCTP_UNUSED,struct sctp_association * asoc)762 sctp_ss_fcfs_is_empty(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc)
763 {
764 if (TAILQ_EMPTY(&asoc->ss_data.out_list)) {
765 return (1);
766 } else {
767 return (0);
768 }
769 }
770
771 static void
sctp_ss_fcfs_remove(struct sctp_tcb * stcb,struct sctp_association * asoc,struct sctp_stream_out * strq SCTP_UNUSED,struct sctp_stream_queue_pending * sp,int holds_lock)772 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 if (holds_lock == 0) {
777 SCTP_TCB_SEND_LOCK(stcb);
778 }
779 if (sp &&
780 ((sp->ss_next.tqe_next != NULL) ||
781 (sp->ss_next.tqe_prev != NULL))) {
782 TAILQ_REMOVE(&asoc->ss_data.out_list, sp, ss_next);
783 }
784 if (holds_lock == 0) {
785 SCTP_TCB_SEND_UNLOCK(stcb);
786 }
787 return;
788 }
789
790
791 static struct sctp_stream_out *
sctp_ss_fcfs_select(struct sctp_tcb * stcb SCTP_UNUSED,struct sctp_nets * net,struct sctp_association * asoc)792 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 sp = TAILQ_FIRST(&asoc->ss_data.out_list);
799 default_again:
800 if (sp != NULL) {
801 strq = &asoc->strmout[sp->stream];
802 } else {
803 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 if (net != NULL && strq != NULL &&
819 SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
820 if (TAILQ_FIRST(&strq->outqueue) &&
821 TAILQ_FIRST(&strq->outqueue)->net != NULL &&
822 TAILQ_FIRST(&strq->outqueue)->net != net) {
823 sp = TAILQ_NEXT(sp, ss_next);
824 goto default_again;
825 }
826 }
827 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 };
1000