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