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