1217760Stuexen/*-
2235828Stuexen * Copyright (c) 2010-2012, by Michael Tuexen. All rights reserved.
3235828Stuexen * Copyright (c) 2010-2012, by Randall Stewart. All rights reserved.
4235828Stuexen * Copyright (c) 2010-2012, by Robin Seggelmann. All rights reserved.
5217760Stuexen *
6217760Stuexen * Redistribution and use in source and binary forms, with or without
7217760Stuexen * modification, are permitted provided that the following conditions are met:
8217760Stuexen *
9217760Stuexen * a) Redistributions of source code must retain the above copyright notice,
10228653Stuexen *    this list of conditions and the following disclaimer.
11217760Stuexen *
12217760Stuexen * b) Redistributions in binary form must reproduce the above copyright
13217760Stuexen *    notice, this list of conditions and the following disclaimer in
14228653Stuexen *    the documentation and/or other materials provided with the distribution.
15217760Stuexen *
16217760Stuexen * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17217760Stuexen * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
18217760Stuexen * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19217760Stuexen * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20217760Stuexen * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21217760Stuexen * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22217760Stuexen * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23217760Stuexen * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24217760Stuexen * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25217760Stuexen * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
26217760Stuexen * THE POSSIBILITY OF SUCH DAMAGE.
27217760Stuexen */
28217760Stuexen
29217760Stuexen#include <sys/cdefs.h>
30217760Stuexen__FBSDID("$FreeBSD$");
31217760Stuexen
32217760Stuexen#include <netinet/sctp_pcb.h>
33217760Stuexen
34217760Stuexen/*
35217760Stuexen * Default simple round-robin algorithm.
36217760Stuexen * Just interates the streams in the order they appear.
37217760Stuexen */
38217760Stuexen
39217760Stuexenstatic void
40217760Stuexensctp_ss_default_add(struct sctp_tcb *, struct sctp_association *,
41217760Stuexen    struct sctp_stream_out *,
42217760Stuexen    struct sctp_stream_queue_pending *, int);
43217760Stuexen
44217760Stuexenstatic void
45217760Stuexensctp_ss_default_remove(struct sctp_tcb *, struct sctp_association *,
46217760Stuexen    struct sctp_stream_out *,
47217760Stuexen    struct sctp_stream_queue_pending *, int);
48217760Stuexen
49217760Stuexenstatic void
50217760Stuexensctp_ss_default_init(struct sctp_tcb *stcb, struct sctp_association *asoc,
51217760Stuexen    int holds_lock)
52217760Stuexen{
53217760Stuexen	uint16_t i;
54217760Stuexen
55217760Stuexen	TAILQ_INIT(&asoc->ss_data.out_wheel);
56218241Stuexen	/*
57218241Stuexen	 * If there is data in the stream queues already, the scheduler of
58218241Stuexen	 * an existing association has been changed. We need to add all
59218241Stuexen	 * stream queues to the wheel.
60218241Stuexen	 */
61217760Stuexen	for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
62218639Stuexen		stcb->asoc.ss_functions.sctp_ss_add_to_stream(stcb, &stcb->asoc,
63218639Stuexen		    &stcb->asoc.strmout[i],
64218639Stuexen		    NULL, holds_lock);
65217760Stuexen	}
66217760Stuexen	return;
67217760Stuexen}
68217760Stuexen
69217760Stuexenstatic void
70217760Stuexensctp_ss_default_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
71228653Stuexen    int clear_values SCTP_UNUSED, int holds_lock)
72217760Stuexen{
73218639Stuexen	if (holds_lock == 0) {
74218639Stuexen		SCTP_TCB_SEND_LOCK(stcb);
75218639Stuexen	}
76218639Stuexen	while (!TAILQ_EMPTY(&asoc->ss_data.out_wheel)) {
77218639Stuexen		struct sctp_stream_out *strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
78217760Stuexen
79218639Stuexen		TAILQ_REMOVE(&asoc->ss_data.out_wheel, TAILQ_FIRST(&asoc->ss_data.out_wheel), ss_params.rr.next_spoke);
80218639Stuexen		strq->ss_params.rr.next_spoke.tqe_next = NULL;
81218639Stuexen		strq->ss_params.rr.next_spoke.tqe_prev = NULL;
82217760Stuexen	}
83218639Stuexen	asoc->last_out_stream = NULL;
84218639Stuexen	if (holds_lock == 0) {
85218639Stuexen		SCTP_TCB_SEND_UNLOCK(stcb);
86218639Stuexen	}
87217760Stuexen	return;
88217760Stuexen}
89217760Stuexen
90217760Stuexenstatic void
91228653Stuexensctp_ss_default_init_stream(struct sctp_stream_out *strq, struct sctp_stream_out *with_strq SCTP_UNUSED)
92217760Stuexen{
93217760Stuexen	strq->ss_params.rr.next_spoke.tqe_next = NULL;
94217760Stuexen	strq->ss_params.rr.next_spoke.tqe_prev = NULL;
95217760Stuexen	return;
96217760Stuexen}
97217760Stuexen
98217760Stuexenstatic void
99217760Stuexensctp_ss_default_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
100217760Stuexen    struct sctp_stream_out *strq,
101228653Stuexen    struct sctp_stream_queue_pending *sp SCTP_UNUSED, int holds_lock)
102217760Stuexen{
103217760Stuexen	if (holds_lock == 0) {
104217760Stuexen		SCTP_TCB_SEND_LOCK(stcb);
105217760Stuexen	}
106218639Stuexen	/* Add to wheel if not already on it and stream queue not empty */
107218639Stuexen	if (!TAILQ_EMPTY(&strq->outqueue) &&
108218639Stuexen	    (strq->ss_params.rr.next_spoke.tqe_next == NULL) &&
109217760Stuexen	    (strq->ss_params.rr.next_spoke.tqe_prev == NULL)) {
110217760Stuexen		TAILQ_INSERT_TAIL(&asoc->ss_data.out_wheel,
111217760Stuexen		    strq, ss_params.rr.next_spoke);
112217760Stuexen	}
113217760Stuexen	if (holds_lock == 0) {
114217760Stuexen		SCTP_TCB_SEND_UNLOCK(stcb);
115217760Stuexen	}
116217760Stuexen	return;
117217760Stuexen}
118217760Stuexen
119217760Stuexenstatic int
120228653Stuexensctp_ss_default_is_empty(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc)
121217760Stuexen{
122217760Stuexen	if (TAILQ_EMPTY(&asoc->ss_data.out_wheel)) {
123217760Stuexen		return (1);
124217760Stuexen	} else {
125217760Stuexen		return (0);
126217760Stuexen	}
127217760Stuexen}
128217760Stuexen
129217760Stuexenstatic void
130217760Stuexensctp_ss_default_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
131217760Stuexen    struct sctp_stream_out *strq,
132228653Stuexen    struct sctp_stream_queue_pending *sp SCTP_UNUSED, int holds_lock)
133217760Stuexen{
134217760Stuexen	if (holds_lock == 0) {
135217760Stuexen		SCTP_TCB_SEND_LOCK(stcb);
136217760Stuexen	}
137218639Stuexen	/*
138218639Stuexen	 * Remove from wheel if stream queue is empty and actually is on the
139218639Stuexen	 * wheel
140218639Stuexen	 */
141218639Stuexen	if (TAILQ_EMPTY(&strq->outqueue) &&
142218639Stuexen	    (strq->ss_params.rr.next_spoke.tqe_next != NULL ||
143218639Stuexen	    strq->ss_params.rr.next_spoke.tqe_prev != NULL)) {
144217760Stuexen		if (asoc->last_out_stream == strq) {
145217760Stuexen			asoc->last_out_stream = TAILQ_PREV(asoc->last_out_stream,
146217760Stuexen			    sctpwheel_listhead,
147217760Stuexen			    ss_params.rr.next_spoke);
148217760Stuexen			if (asoc->last_out_stream == NULL) {
149217760Stuexen				asoc->last_out_stream = TAILQ_LAST(&asoc->ss_data.out_wheel,
150217760Stuexen				    sctpwheel_listhead);
151217760Stuexen			}
152217760Stuexen			if (asoc->last_out_stream == strq) {
153217760Stuexen				asoc->last_out_stream = NULL;
154217760Stuexen			}
155217760Stuexen		}
156217760Stuexen		TAILQ_REMOVE(&asoc->ss_data.out_wheel, strq, ss_params.rr.next_spoke);
157217760Stuexen		strq->ss_params.rr.next_spoke.tqe_next = NULL;
158217760Stuexen		strq->ss_params.rr.next_spoke.tqe_prev = NULL;
159217760Stuexen	}
160217760Stuexen	if (holds_lock == 0) {
161217760Stuexen		SCTP_TCB_SEND_UNLOCK(stcb);
162217760Stuexen	}
163217760Stuexen	return;
164217760Stuexen}
165217760Stuexen
166217760Stuexen
167217760Stuexenstatic struct sctp_stream_out *
168228653Stuexensctp_ss_default_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
169217760Stuexen    struct sctp_association *asoc)
170217760Stuexen{
171217760Stuexen	struct sctp_stream_out *strq, *strqt;
172217760Stuexen
173217760Stuexen	strqt = asoc->last_out_stream;
174217760Stuexendefault_again:
175217760Stuexen	/* Find the next stream to use */
176217760Stuexen	if (strqt == NULL) {
177217760Stuexen		strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
178217760Stuexen	} else {
179217760Stuexen		strq = TAILQ_NEXT(strqt, ss_params.rr.next_spoke);
180217760Stuexen		if (strq == NULL) {
181217760Stuexen			strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
182217760Stuexen		}
183217760Stuexen	}
184217760Stuexen
185217760Stuexen	/*
186217760Stuexen	 * If CMT is off, we must validate that the stream in question has
187218319Srrs	 * the first item pointed towards are network destination requested
188217760Stuexen	 * by the caller. Note that if we turn out to be locked to a stream
189217760Stuexen	 * (assigning TSN's then we must stop, since we cannot look for
190217760Stuexen	 * another stream with data to send to that destination). In CMT's
191217760Stuexen	 * case, by skipping this check, we will send one data packet
192217760Stuexen	 * towards the requested net.
193217760Stuexen	 */
194217760Stuexen	if (net != NULL && strq != NULL &&
195217760Stuexen	    SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
196217760Stuexen		if (TAILQ_FIRST(&strq->outqueue) &&
197217760Stuexen		    TAILQ_FIRST(&strq->outqueue)->net != NULL &&
198217760Stuexen		    TAILQ_FIRST(&strq->outqueue)->net != net) {
199217760Stuexen			if (strq == asoc->last_out_stream) {
200217760Stuexen				return (NULL);
201217760Stuexen			} else {
202217760Stuexen				strqt = strq;
203217760Stuexen				goto default_again;
204217760Stuexen			}
205217760Stuexen		}
206217760Stuexen	}
207217760Stuexen	return (strq);
208217760Stuexen}
209217760Stuexen
210217760Stuexenstatic void
211228653Stuexensctp_ss_default_scheduled(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net SCTP_UNUSED,
212228653Stuexen    struct sctp_association *asoc SCTP_UNUSED,
213228653Stuexen    struct sctp_stream_out *strq, int moved_how_much SCTP_UNUSED)
214217760Stuexen{
215217760Stuexen	asoc->last_out_stream = strq;
216217760Stuexen	return;
217217760Stuexen}
218217760Stuexen
219217760Stuexenstatic void
220228653Stuexensctp_ss_default_packet_done(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net SCTP_UNUSED,
221228653Stuexen    struct sctp_association *asoc SCTP_UNUSED)
222217760Stuexen{
223217760Stuexen	/* Nothing to be done here */
224217760Stuexen	return;
225217760Stuexen}
226217760Stuexen
227217760Stuexenstatic int
228228653Stuexensctp_ss_default_get_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED,
229228653Stuexen    struct sctp_stream_out *strq SCTP_UNUSED, uint16_t * value SCTP_UNUSED)
230217760Stuexen{
231217760Stuexen	/* Nothing to be done here */
232217760Stuexen	return (-1);
233217760Stuexen}
234217760Stuexen
235217760Stuexenstatic int
236228653Stuexensctp_ss_default_set_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED,
237228653Stuexen    struct sctp_stream_out *strq SCTP_UNUSED, uint16_t value SCTP_UNUSED)
238217760Stuexen{
239217760Stuexen	/* Nothing to be done here */
240217760Stuexen	return (-1);
241217760Stuexen}
242217760Stuexen
243217760Stuexen/*
244217760Stuexen * Real round-robin algorithm.
245217760Stuexen * Always interates the streams in ascending order.
246217760Stuexen */
247217760Stuexenstatic void
248217760Stuexensctp_ss_rr_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
249217760Stuexen    struct sctp_stream_out *strq,
250228653Stuexen    struct sctp_stream_queue_pending *sp SCTP_UNUSED, int holds_lock)
251217760Stuexen{
252217760Stuexen	struct sctp_stream_out *strqt;
253217760Stuexen
254217760Stuexen	if (holds_lock == 0) {
255217760Stuexen		SCTP_TCB_SEND_LOCK(stcb);
256217760Stuexen	}
257218639Stuexen	if (!TAILQ_EMPTY(&strq->outqueue) &&
258218639Stuexen	    (strq->ss_params.rr.next_spoke.tqe_next == NULL) &&
259217760Stuexen	    (strq->ss_params.rr.next_spoke.tqe_prev == NULL)) {
260217760Stuexen		if (TAILQ_EMPTY(&asoc->ss_data.out_wheel)) {
261217760Stuexen			TAILQ_INSERT_HEAD(&asoc->ss_data.out_wheel, strq, ss_params.rr.next_spoke);
262217760Stuexen		} else {
263217760Stuexen			strqt = TAILQ_FIRST(&asoc->ss_data.out_wheel);
264217760Stuexen			while (strqt != NULL && (strqt->stream_no < strq->stream_no)) {
265217760Stuexen				strqt = TAILQ_NEXT(strqt, ss_params.rr.next_spoke);
266217760Stuexen			}
267217760Stuexen			if (strqt != NULL) {
268217760Stuexen				TAILQ_INSERT_BEFORE(strqt, strq, ss_params.rr.next_spoke);
269217760Stuexen			} else {
270217760Stuexen				TAILQ_INSERT_TAIL(&asoc->ss_data.out_wheel, strq, ss_params.rr.next_spoke);
271217760Stuexen			}
272217760Stuexen		}
273217760Stuexen	}
274217760Stuexen	if (holds_lock == 0) {
275217760Stuexen		SCTP_TCB_SEND_UNLOCK(stcb);
276217760Stuexen	}
277217760Stuexen	return;
278217760Stuexen}
279217760Stuexen
280217760Stuexen/*
281217760Stuexen * Real round-robin per packet algorithm.
282217760Stuexen * Always interates the streams in ascending order and
283217760Stuexen * only fills messages of the same stream in a packet.
284217760Stuexen */
285218639Stuexenstatic struct sctp_stream_out *
286228653Stuexensctp_ss_rrp_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net SCTP_UNUSED,
287218639Stuexen    struct sctp_association *asoc)
288217760Stuexen{
289228907Stuexen	return (asoc->last_out_stream);
290217760Stuexen}
291217760Stuexen
292218639Stuexenstatic void
293228653Stuexensctp_ss_rrp_packet_done(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
294217760Stuexen    struct sctp_association *asoc)
295217760Stuexen{
296217760Stuexen	struct sctp_stream_out *strq, *strqt;
297217760Stuexen
298217760Stuexen	strqt = asoc->last_out_stream;
299217760Stuexenrrp_again:
300217760Stuexen	/* Find the next stream to use */
301217760Stuexen	if (strqt == NULL) {
302217760Stuexen		strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
303217760Stuexen	} else {
304217760Stuexen		strq = TAILQ_NEXT(strqt, ss_params.rr.next_spoke);
305217760Stuexen		if (strq == NULL) {
306217760Stuexen			strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
307217760Stuexen		}
308217760Stuexen	}
309217760Stuexen
310217760Stuexen	/*
311217760Stuexen	 * If CMT is off, we must validate that the stream in question has
312218319Srrs	 * the first item pointed towards are network destination requested
313217760Stuexen	 * by the caller. Note that if we turn out to be locked to a stream
314217760Stuexen	 * (assigning TSN's then we must stop, since we cannot look for
315217760Stuexen	 * another stream with data to send to that destination). In CMT's
316217760Stuexen	 * case, by skipping this check, we will send one data packet
317217760Stuexen	 * towards the requested net.
318217760Stuexen	 */
319217760Stuexen	if (net != NULL && strq != NULL &&
320217760Stuexen	    SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
321217760Stuexen		if (TAILQ_FIRST(&strq->outqueue) &&
322217760Stuexen		    TAILQ_FIRST(&strq->outqueue)->net != NULL &&
323217760Stuexen		    TAILQ_FIRST(&strq->outqueue)->net != net) {
324217760Stuexen			if (strq == asoc->last_out_stream) {
325218639Stuexen				strq = NULL;
326217760Stuexen			} else {
327217760Stuexen				strqt = strq;
328217760Stuexen				goto rrp_again;
329217760Stuexen			}
330217760Stuexen		}
331217760Stuexen	}
332217760Stuexen	asoc->last_out_stream = strq;
333217760Stuexen	return;
334217760Stuexen}
335217760Stuexen
336217760Stuexen
337217760Stuexen/*
338217760Stuexen * Priority algorithm.
339217760Stuexen * Always prefers streams based on their priority id.
340217760Stuexen */
341217760Stuexenstatic void
342217760Stuexensctp_ss_prio_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
343217760Stuexen    int clear_values, int holds_lock)
344217760Stuexen{
345218639Stuexen	if (holds_lock == 0) {
346218639Stuexen		SCTP_TCB_SEND_LOCK(stcb);
347218639Stuexen	}
348218639Stuexen	while (!TAILQ_EMPTY(&asoc->ss_data.out_wheel)) {
349218639Stuexen		struct sctp_stream_out *strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
350217760Stuexen
351218639Stuexen		if (clear_values) {
352218639Stuexen			strq->ss_params.prio.priority = 0;
353217760Stuexen		}
354218639Stuexen		TAILQ_REMOVE(&asoc->ss_data.out_wheel, TAILQ_FIRST(&asoc->ss_data.out_wheel), ss_params.prio.next_spoke);
355218639Stuexen		strq->ss_params.prio.next_spoke.tqe_next = NULL;
356218639Stuexen		strq->ss_params.prio.next_spoke.tqe_prev = NULL;
357218639Stuexen
358217760Stuexen	}
359218639Stuexen	asoc->last_out_stream = NULL;
360218639Stuexen	if (holds_lock == 0) {
361218639Stuexen		SCTP_TCB_SEND_UNLOCK(stcb);
362218639Stuexen	}
363217760Stuexen	return;
364217760Stuexen}
365217760Stuexen
366217760Stuexenstatic void
367218241Stuexensctp_ss_prio_init_stream(struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
368217760Stuexen{
369217760Stuexen	strq->ss_params.prio.next_spoke.tqe_next = NULL;
370217760Stuexen	strq->ss_params.prio.next_spoke.tqe_prev = NULL;
371218241Stuexen	if (with_strq != NULL) {
372218241Stuexen		strq->ss_params.prio.priority = with_strq->ss_params.prio.priority;
373218241Stuexen	} else {
374218241Stuexen		strq->ss_params.prio.priority = 0;
375218241Stuexen	}
376217760Stuexen	return;
377217760Stuexen}
378217760Stuexen
379217760Stuexenstatic void
380217760Stuexensctp_ss_prio_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
381228653Stuexen    struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED,
382217760Stuexen    int holds_lock)
383217760Stuexen{
384217760Stuexen	struct sctp_stream_out *strqt;
385217760Stuexen
386217760Stuexen	if (holds_lock == 0) {
387217760Stuexen		SCTP_TCB_SEND_LOCK(stcb);
388217760Stuexen	}
389218639Stuexen	/* Add to wheel if not already on it and stream queue not empty */
390218639Stuexen	if (!TAILQ_EMPTY(&strq->outqueue) &&
391218639Stuexen	    (strq->ss_params.prio.next_spoke.tqe_next == NULL) &&
392217760Stuexen	    (strq->ss_params.prio.next_spoke.tqe_prev == NULL)) {
393217760Stuexen		if (TAILQ_EMPTY(&asoc->ss_data.out_wheel)) {
394217760Stuexen			TAILQ_INSERT_HEAD(&asoc->ss_data.out_wheel, strq, ss_params.prio.next_spoke);
395217760Stuexen		} else {
396217760Stuexen			strqt = TAILQ_FIRST(&asoc->ss_data.out_wheel);
397217760Stuexen			while (strqt != NULL && strqt->ss_params.prio.priority < strq->ss_params.prio.priority) {
398217760Stuexen				strqt = TAILQ_NEXT(strqt, ss_params.prio.next_spoke);
399217760Stuexen			}
400217760Stuexen			if (strqt != NULL) {
401217760Stuexen				TAILQ_INSERT_BEFORE(strqt, strq, ss_params.prio.next_spoke);
402217760Stuexen			} else {
403217760Stuexen				TAILQ_INSERT_TAIL(&asoc->ss_data.out_wheel, strq, ss_params.prio.next_spoke);
404217760Stuexen			}
405217760Stuexen		}
406217760Stuexen	}
407217760Stuexen	if (holds_lock == 0) {
408217760Stuexen		SCTP_TCB_SEND_UNLOCK(stcb);
409217760Stuexen	}
410217760Stuexen	return;
411217760Stuexen}
412217760Stuexen
413217760Stuexenstatic void
414217760Stuexensctp_ss_prio_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
415228653Stuexen    struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED,
416217760Stuexen    int holds_lock)
417217760Stuexen{
418217760Stuexen	if (holds_lock == 0) {
419217760Stuexen		SCTP_TCB_SEND_LOCK(stcb);
420217760Stuexen	}
421218639Stuexen	/*
422218639Stuexen	 * Remove from wheel if stream queue is empty and actually is on the
423218639Stuexen	 * wheel
424218639Stuexen	 */
425218639Stuexen	if (TAILQ_EMPTY(&strq->outqueue) &&
426218639Stuexen	    (strq->ss_params.prio.next_spoke.tqe_next != NULL ||
427218639Stuexen	    strq->ss_params.prio.next_spoke.tqe_prev != NULL)) {
428217760Stuexen		if (asoc->last_out_stream == strq) {
429217760Stuexen			asoc->last_out_stream = TAILQ_PREV(asoc->last_out_stream, sctpwheel_listhead,
430217760Stuexen			    ss_params.prio.next_spoke);
431217760Stuexen			if (asoc->last_out_stream == NULL) {
432217760Stuexen				asoc->last_out_stream = TAILQ_LAST(&asoc->ss_data.out_wheel,
433217760Stuexen				    sctpwheel_listhead);
434217760Stuexen			}
435217760Stuexen			if (asoc->last_out_stream == strq) {
436217760Stuexen				asoc->last_out_stream = NULL;
437217760Stuexen			}
438217760Stuexen		}
439218639Stuexen		TAILQ_REMOVE(&asoc->ss_data.out_wheel, strq, ss_params.prio.next_spoke);
440217760Stuexen		strq->ss_params.prio.next_spoke.tqe_next = NULL;
441217760Stuexen		strq->ss_params.prio.next_spoke.tqe_prev = NULL;
442217760Stuexen	}
443217760Stuexen	if (holds_lock == 0) {
444217760Stuexen		SCTP_TCB_SEND_UNLOCK(stcb);
445217760Stuexen	}
446217760Stuexen	return;
447217760Stuexen}
448217760Stuexen
449217760Stuexenstatic struct sctp_stream_out *
450228653Stuexensctp_ss_prio_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
451217760Stuexen    struct sctp_association *asoc)
452217760Stuexen{
453217760Stuexen	struct sctp_stream_out *strq, *strqt, *strqn;
454217760Stuexen
455217760Stuexen	strqt = asoc->last_out_stream;
456217760Stuexenprio_again:
457217760Stuexen	/* Find the next stream to use */
458217760Stuexen	if (strqt == NULL) {
459217760Stuexen		strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
460217760Stuexen	} else {
461217760Stuexen		strqn = TAILQ_NEXT(strqt, ss_params.prio.next_spoke);
462217760Stuexen		if (strqn != NULL &&
463217760Stuexen		    strqn->ss_params.prio.priority == strqt->ss_params.prio.priority) {
464218639Stuexen			strq = strqn;
465217760Stuexen		} else {
466217760Stuexen			strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
467217760Stuexen		}
468217760Stuexen	}
469217760Stuexen
470217760Stuexen	/*
471217760Stuexen	 * If CMT is off, we must validate that the stream in question has
472218319Srrs	 * the first item pointed towards are network destination requested
473217760Stuexen	 * by the caller. Note that if we turn out to be locked to a stream
474217760Stuexen	 * (assigning TSN's then we must stop, since we cannot look for
475217760Stuexen	 * another stream with data to send to that destination). In CMT's
476217760Stuexen	 * case, by skipping this check, we will send one data packet
477217760Stuexen	 * towards the requested net.
478217760Stuexen	 */
479217760Stuexen	if (net != NULL && strq != NULL &&
480217760Stuexen	    SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
481217760Stuexen		if (TAILQ_FIRST(&strq->outqueue) &&
482217760Stuexen		    TAILQ_FIRST(&strq->outqueue)->net != NULL &&
483217760Stuexen		    TAILQ_FIRST(&strq->outqueue)->net != net) {
484217760Stuexen			if (strq == asoc->last_out_stream) {
485217760Stuexen				return (NULL);
486217760Stuexen			} else {
487217760Stuexen				strqt = strq;
488217760Stuexen				goto prio_again;
489217760Stuexen			}
490217760Stuexen		}
491217760Stuexen	}
492217760Stuexen	return (strq);
493217760Stuexen}
494217760Stuexen
495217760Stuexenstatic int
496228653Stuexensctp_ss_prio_get_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED,
497217760Stuexen    struct sctp_stream_out *strq, uint16_t * value)
498217760Stuexen{
499217760Stuexen	if (strq == NULL) {
500217760Stuexen		return (-1);
501217760Stuexen	}
502217760Stuexen	*value = strq->ss_params.prio.priority;
503217760Stuexen	return (1);
504217760Stuexen}
505217760Stuexen
506217760Stuexenstatic int
507217760Stuexensctp_ss_prio_set_value(struct sctp_tcb *stcb, struct sctp_association *asoc,
508217760Stuexen    struct sctp_stream_out *strq, uint16_t value)
509217760Stuexen{
510217760Stuexen	if (strq == NULL) {
511217760Stuexen		return (-1);
512217760Stuexen	}
513217760Stuexen	strq->ss_params.prio.priority = value;
514217760Stuexen	sctp_ss_prio_remove(stcb, asoc, strq, NULL, 1);
515217760Stuexen	sctp_ss_prio_add(stcb, asoc, strq, NULL, 1);
516217760Stuexen	return (1);
517217760Stuexen}
518217760Stuexen
519217760Stuexen/*
520217760Stuexen * Fair bandwidth algorithm.
521217760Stuexen * Maintains an equal troughput per stream.
522217760Stuexen */
523217760Stuexenstatic void
524217760Stuexensctp_ss_fb_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
525217760Stuexen    int clear_values, int holds_lock)
526217760Stuexen{
527218639Stuexen	if (holds_lock == 0) {
528218639Stuexen		SCTP_TCB_SEND_LOCK(stcb);
529218639Stuexen	}
530218639Stuexen	while (!TAILQ_EMPTY(&asoc->ss_data.out_wheel)) {
531218639Stuexen		struct sctp_stream_out *strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
532217760Stuexen
533218639Stuexen		if (clear_values) {
534218639Stuexen			strq->ss_params.fb.rounds = -1;
535217760Stuexen		}
536218639Stuexen		TAILQ_REMOVE(&asoc->ss_data.out_wheel, TAILQ_FIRST(&asoc->ss_data.out_wheel), ss_params.fb.next_spoke);
537218639Stuexen		strq->ss_params.fb.next_spoke.tqe_next = NULL;
538218639Stuexen		strq->ss_params.fb.next_spoke.tqe_prev = NULL;
539217760Stuexen	}
540218639Stuexen	asoc->last_out_stream = NULL;
541218639Stuexen	if (holds_lock == 0) {
542218639Stuexen		SCTP_TCB_SEND_UNLOCK(stcb);
543218639Stuexen	}
544217760Stuexen	return;
545217760Stuexen}
546217760Stuexen
547217760Stuexenstatic void
548218241Stuexensctp_ss_fb_init_stream(struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
549217760Stuexen{
550217760Stuexen	strq->ss_params.fb.next_spoke.tqe_next = NULL;
551217760Stuexen	strq->ss_params.fb.next_spoke.tqe_prev = NULL;
552218241Stuexen	if (with_strq != NULL) {
553218241Stuexen		strq->ss_params.fb.rounds = with_strq->ss_params.fb.rounds;
554218241Stuexen	} else {
555218241Stuexen		strq->ss_params.fb.rounds = -1;
556218241Stuexen	}
557217760Stuexen	return;
558217760Stuexen}
559217760Stuexen
560217760Stuexenstatic void
561217760Stuexensctp_ss_fb_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
562228653Stuexen    struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED,
563217760Stuexen    int holds_lock)
564217760Stuexen{
565217760Stuexen	if (holds_lock == 0) {
566217760Stuexen		SCTP_TCB_SEND_LOCK(stcb);
567217760Stuexen	}
568218639Stuexen	if (!TAILQ_EMPTY(&strq->outqueue) &&
569218639Stuexen	    (strq->ss_params.fb.next_spoke.tqe_next == NULL) &&
570218639Stuexen	    (strq->ss_params.fb.next_spoke.tqe_prev == NULL)) {
571218639Stuexen		if (strq->ss_params.fb.rounds < 0)
572217760Stuexen			strq->ss_params.fb.rounds = TAILQ_FIRST(&strq->outqueue)->length;
573218639Stuexen		TAILQ_INSERT_TAIL(&asoc->ss_data.out_wheel, strq, ss_params.fb.next_spoke);
574217760Stuexen	}
575217760Stuexen	if (holds_lock == 0) {
576217760Stuexen		SCTP_TCB_SEND_UNLOCK(stcb);
577217760Stuexen	}
578217760Stuexen	return;
579217760Stuexen}
580217760Stuexen
581217760Stuexenstatic void
582217760Stuexensctp_ss_fb_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
583228653Stuexen    struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED,
584217760Stuexen    int holds_lock)
585217760Stuexen{
586217760Stuexen	if (holds_lock == 0) {
587217760Stuexen		SCTP_TCB_SEND_LOCK(stcb);
588217760Stuexen	}
589218639Stuexen	/*
590218639Stuexen	 * Remove from wheel if stream queue is empty and actually is on the
591218639Stuexen	 * wheel
592218639Stuexen	 */
593218639Stuexen	if (TAILQ_EMPTY(&strq->outqueue) &&
594218639Stuexen	    (strq->ss_params.fb.next_spoke.tqe_next != NULL ||
595218639Stuexen	    strq->ss_params.fb.next_spoke.tqe_prev != NULL)) {
596217760Stuexen		if (asoc->last_out_stream == strq) {
597217760Stuexen			asoc->last_out_stream = TAILQ_PREV(asoc->last_out_stream, sctpwheel_listhead,
598217760Stuexen			    ss_params.fb.next_spoke);
599217760Stuexen			if (asoc->last_out_stream == NULL) {
600217760Stuexen				asoc->last_out_stream = TAILQ_LAST(&asoc->ss_data.out_wheel,
601217760Stuexen				    sctpwheel_listhead);
602217760Stuexen			}
603217760Stuexen			if (asoc->last_out_stream == strq) {
604217760Stuexen				asoc->last_out_stream = NULL;
605217760Stuexen			}
606217760Stuexen		}
607217760Stuexen		TAILQ_REMOVE(&asoc->ss_data.out_wheel, strq, ss_params.fb.next_spoke);
608217760Stuexen		strq->ss_params.fb.next_spoke.tqe_next = NULL;
609217760Stuexen		strq->ss_params.fb.next_spoke.tqe_prev = NULL;
610217760Stuexen	}
611217760Stuexen	if (holds_lock == 0) {
612217760Stuexen		SCTP_TCB_SEND_UNLOCK(stcb);
613217760Stuexen	}
614217760Stuexen	return;
615217760Stuexen}
616217760Stuexen
617217760Stuexenstatic struct sctp_stream_out *
618228653Stuexensctp_ss_fb_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
619217760Stuexen    struct sctp_association *asoc)
620217760Stuexen{
621217760Stuexen	struct sctp_stream_out *strq = NULL, *strqt;
622217760Stuexen
623218639Stuexen	if (asoc->last_out_stream == NULL ||
624218639Stuexen	    TAILQ_FIRST(&asoc->ss_data.out_wheel) == TAILQ_LAST(&asoc->ss_data.out_wheel, sctpwheel_listhead)) {
625217760Stuexen		strqt = TAILQ_FIRST(&asoc->ss_data.out_wheel);
626217760Stuexen	} else {
627218639Stuexen		strqt = TAILQ_NEXT(asoc->last_out_stream, ss_params.fb.next_spoke);
628217760Stuexen	}
629217760Stuexen	do {
630218639Stuexen		if ((strqt != NULL) &&
631218639Stuexen		    ((SCTP_BASE_SYSCTL(sctp_cmt_on_off) > 0) ||
632218639Stuexen		    (SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0 &&
633218639Stuexen		    (net == NULL || (TAILQ_FIRST(&strqt->outqueue) && TAILQ_FIRST(&strqt->outqueue)->net == NULL) ||
634218639Stuexen		    (net != NULL && TAILQ_FIRST(&strqt->outqueue) && TAILQ_FIRST(&strqt->outqueue)->net != NULL &&
635218639Stuexen		    TAILQ_FIRST(&strqt->outqueue)->net == net))))) {
636217760Stuexen			if ((strqt->ss_params.fb.rounds >= 0) && (strq == NULL ||
637217760Stuexen			    strqt->ss_params.fb.rounds < strq->ss_params.fb.rounds)) {
638217760Stuexen				strq = strqt;
639217760Stuexen			}
640217760Stuexen		}
641217760Stuexen		if (strqt != NULL) {
642217760Stuexen			strqt = TAILQ_NEXT(strqt, ss_params.fb.next_spoke);
643217760Stuexen		} else {
644217760Stuexen			strqt = TAILQ_FIRST(&asoc->ss_data.out_wheel);
645217760Stuexen		}
646217760Stuexen	} while (strqt != strq);
647217760Stuexen	return (strq);
648217760Stuexen}
649217760Stuexen
650217760Stuexenstatic void
651228653Stuexensctp_ss_fb_scheduled(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net SCTP_UNUSED,
652217760Stuexen    struct sctp_association *asoc, struct sctp_stream_out *strq,
653228653Stuexen    int moved_how_much SCTP_UNUSED)
654217760Stuexen{
655217760Stuexen	struct sctp_stream_out *strqt;
656217760Stuexen	int subtract;
657217760Stuexen
658217760Stuexen	subtract = strq->ss_params.fb.rounds;
659217760Stuexen	TAILQ_FOREACH(strqt, &asoc->ss_data.out_wheel, ss_params.fb.next_spoke) {
660217760Stuexen		strqt->ss_params.fb.rounds -= subtract;
661217760Stuexen		if (strqt->ss_params.fb.rounds < 0)
662217760Stuexen			strqt->ss_params.fb.rounds = 0;
663217760Stuexen	}
664217760Stuexen	if (TAILQ_FIRST(&strq->outqueue)) {
665217760Stuexen		strq->ss_params.fb.rounds = TAILQ_FIRST(&strq->outqueue)->length;
666217760Stuexen	} else {
667217760Stuexen		strq->ss_params.fb.rounds = -1;
668217760Stuexen	}
669217760Stuexen	asoc->last_out_stream = strq;
670217760Stuexen	return;
671217760Stuexen}
672217760Stuexen
673217760Stuexen/*
674217760Stuexen * First-come, first-serve algorithm.
675217760Stuexen * Maintains the order provided by the application.
676217760Stuexen */
677217760Stuexenstatic void
678218241Stuexensctp_ss_fcfs_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
679218241Stuexen    struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp,
680218241Stuexen    int holds_lock);
681218241Stuexen
682218241Stuexenstatic void
683217760Stuexensctp_ss_fcfs_init(struct sctp_tcb *stcb, struct sctp_association *asoc,
684217760Stuexen    int holds_lock)
685217760Stuexen{
686218241Stuexen	uint32_t x, n = 0, add_more = 1;
687217760Stuexen	struct sctp_stream_queue_pending *sp;
688217760Stuexen	uint16_t i;
689217760Stuexen
690217760Stuexen	TAILQ_INIT(&asoc->ss_data.out_list);
691218241Stuexen	/*
692218241Stuexen	 * If there is data in the stream queues already, the scheduler of
693218241Stuexen	 * an existing association has been changed. We can only cycle
694218241Stuexen	 * through the stream queues and add everything to the FCFS queue.
695218241Stuexen	 */
696217760Stuexen	while (add_more) {
697217760Stuexen		add_more = 0;
698217760Stuexen		for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
699218241Stuexen			sp = TAILQ_FIRST(&stcb->asoc.strmout[i].outqueue);
700218241Stuexen			x = 0;
701218241Stuexen			/* Find n. message in current stream queue */
702218241Stuexen			while (sp != NULL && x < n) {
703217760Stuexen				sp = TAILQ_NEXT(sp, next);
704218241Stuexen				x++;
705217760Stuexen			}
706217760Stuexen			if (sp != NULL) {
707218241Stuexen				sctp_ss_fcfs_add(stcb, &stcb->asoc, &stcb->asoc.strmout[i], sp, holds_lock);
708217760Stuexen				add_more = 1;
709217760Stuexen			}
710217760Stuexen		}
711218241Stuexen		n++;
712217760Stuexen	}
713217760Stuexen	return;
714217760Stuexen}
715217760Stuexen
716217760Stuexenstatic void
717217760Stuexensctp_ss_fcfs_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
718217760Stuexen    int clear_values, int holds_lock)
719217760Stuexen{
720217760Stuexen	if (clear_values) {
721218639Stuexen		if (holds_lock == 0) {
722218639Stuexen			SCTP_TCB_SEND_LOCK(stcb);
723218639Stuexen		}
724217760Stuexen		while (!TAILQ_EMPTY(&asoc->ss_data.out_list)) {
725218241Stuexen			TAILQ_REMOVE(&asoc->ss_data.out_list, TAILQ_FIRST(&asoc->ss_data.out_list), ss_next);
726217760Stuexen		}
727218639Stuexen		if (holds_lock == 0) {
728218639Stuexen			SCTP_TCB_SEND_UNLOCK(stcb);
729218639Stuexen		}
730217760Stuexen	}
731217760Stuexen	return;
732217760Stuexen}
733217760Stuexen
734217760Stuexenstatic void
735228653Stuexensctp_ss_fcfs_init_stream(struct sctp_stream_out *strq SCTP_UNUSED, struct sctp_stream_out *with_strq SCTP_UNUSED)
736217760Stuexen{
737217760Stuexen	/* Nothing to be done here */
738217760Stuexen	return;
739217760Stuexen}
740217760Stuexen
741217760Stuexenstatic void
742217760Stuexensctp_ss_fcfs_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
743228653Stuexen    struct sctp_stream_out *strq SCTP_UNUSED, struct sctp_stream_queue_pending *sp,
744217760Stuexen    int holds_lock)
745217760Stuexen{
746217760Stuexen	if (holds_lock == 0) {
747217760Stuexen		SCTP_TCB_SEND_LOCK(stcb);
748217760Stuexen	}
749218241Stuexen	if (sp && (sp->ss_next.tqe_next == NULL) &&
750218241Stuexen	    (sp->ss_next.tqe_prev == NULL)) {
751218241Stuexen		TAILQ_INSERT_TAIL(&asoc->ss_data.out_list, sp, ss_next);
752217760Stuexen	}
753217760Stuexen	if (holds_lock == 0) {
754217760Stuexen		SCTP_TCB_SEND_UNLOCK(stcb);
755217760Stuexen	}
756217760Stuexen	return;
757217760Stuexen}
758217760Stuexen
759217760Stuexenstatic int
760228653Stuexensctp_ss_fcfs_is_empty(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc)
761217760Stuexen{
762217760Stuexen	if (TAILQ_EMPTY(&asoc->ss_data.out_list)) {
763217760Stuexen		return (1);
764217760Stuexen	} else {
765217760Stuexen		return (0);
766217760Stuexen	}
767217760Stuexen}
768217760Stuexen
769217760Stuexenstatic void
770217760Stuexensctp_ss_fcfs_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
771228653Stuexen    struct sctp_stream_out *strq SCTP_UNUSED, struct sctp_stream_queue_pending *sp,
772217760Stuexen    int holds_lock)
773217760Stuexen{
774217760Stuexen	if (holds_lock == 0) {
775217760Stuexen		SCTP_TCB_SEND_LOCK(stcb);
776217760Stuexen	}
777217760Stuexen	if (sp &&
778218241Stuexen	    ((sp->ss_next.tqe_next != NULL) ||
779218241Stuexen	    (sp->ss_next.tqe_prev != NULL))) {
780218241Stuexen		TAILQ_REMOVE(&asoc->ss_data.out_list, sp, ss_next);
781217760Stuexen	}
782217760Stuexen	if (holds_lock == 0) {
783217760Stuexen		SCTP_TCB_SEND_UNLOCK(stcb);
784217760Stuexen	}
785217760Stuexen	return;
786217760Stuexen}
787217760Stuexen
788217760Stuexen
789217760Stuexenstatic struct sctp_stream_out *
790228653Stuexensctp_ss_fcfs_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
791217760Stuexen    struct sctp_association *asoc)
792217760Stuexen{
793217760Stuexen	struct sctp_stream_out *strq;
794217760Stuexen	struct sctp_stream_queue_pending *sp;
795217760Stuexen
796217760Stuexen	sp = TAILQ_FIRST(&asoc->ss_data.out_list);
797217760Stuexendefault_again:
798217760Stuexen	if (sp != NULL) {
799217760Stuexen		strq = &asoc->strmout[sp->stream];
800217760Stuexen	} else {
801217760Stuexen		strq = NULL;
802217760Stuexen	}
803217760Stuexen
804217760Stuexen	/*
805217760Stuexen	 * If CMT is off, we must validate that the stream in question has
806218319Srrs	 * the first item pointed towards are network destination requested
807217760Stuexen	 * by the caller. Note that if we turn out to be locked to a stream
808217760Stuexen	 * (assigning TSN's then we must stop, since we cannot look for
809217760Stuexen	 * another stream with data to send to that destination). In CMT's
810217760Stuexen	 * case, by skipping this check, we will send one data packet
811217760Stuexen	 * towards the requested net.
812217760Stuexen	 */
813217760Stuexen	if (net != NULL && strq != NULL &&
814217760Stuexen	    SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
815217760Stuexen		if (TAILQ_FIRST(&strq->outqueue) &&
816217760Stuexen		    TAILQ_FIRST(&strq->outqueue)->net != NULL &&
817217760Stuexen		    TAILQ_FIRST(&strq->outqueue)->net != net) {
818218241Stuexen			sp = TAILQ_NEXT(sp, ss_next);
819217760Stuexen			goto default_again;
820217760Stuexen		}
821217760Stuexen	}
822217760Stuexen	return (strq);
823217760Stuexen}
824217760Stuexen
825217760Stuexenstruct sctp_ss_functions sctp_ss_functions[] = {
826217760Stuexen/* SCTP_SS_DEFAULT */
827217760Stuexen	{
828217760Stuexen		.sctp_ss_init = sctp_ss_default_init,
829217760Stuexen		.sctp_ss_clear = sctp_ss_default_clear,
830217760Stuexen		.sctp_ss_init_stream = sctp_ss_default_init_stream,
831217760Stuexen		.sctp_ss_add_to_stream = sctp_ss_default_add,
832217760Stuexen		.sctp_ss_is_empty = sctp_ss_default_is_empty,
833217760Stuexen		.sctp_ss_remove_from_stream = sctp_ss_default_remove,
834217760Stuexen		.sctp_ss_select_stream = sctp_ss_default_select,
835217760Stuexen		.sctp_ss_scheduled = sctp_ss_default_scheduled,
836217760Stuexen		.sctp_ss_packet_done = sctp_ss_default_packet_done,
837217760Stuexen		.sctp_ss_get_value = sctp_ss_default_get_value,
838217760Stuexen		.sctp_ss_set_value = sctp_ss_default_set_value
839217760Stuexen	},
840217760Stuexen/* SCTP_SS_ROUND_ROBIN */
841217760Stuexen	{
842217760Stuexen		.sctp_ss_init = sctp_ss_default_init,
843217760Stuexen		.sctp_ss_clear = sctp_ss_default_clear,
844217760Stuexen		.sctp_ss_init_stream = sctp_ss_default_init_stream,
845217760Stuexen		.sctp_ss_add_to_stream = sctp_ss_rr_add,
846217760Stuexen		.sctp_ss_is_empty = sctp_ss_default_is_empty,
847217760Stuexen		.sctp_ss_remove_from_stream = sctp_ss_default_remove,
848217760Stuexen		.sctp_ss_select_stream = sctp_ss_default_select,
849217760Stuexen		.sctp_ss_scheduled = sctp_ss_default_scheduled,
850217760Stuexen		.sctp_ss_packet_done = sctp_ss_default_packet_done,
851217760Stuexen		.sctp_ss_get_value = sctp_ss_default_get_value,
852217760Stuexen		.sctp_ss_set_value = sctp_ss_default_set_value
853217760Stuexen	},
854217760Stuexen/* SCTP_SS_ROUND_ROBIN_PACKET */
855217760Stuexen	{
856217760Stuexen		.sctp_ss_init = sctp_ss_default_init,
857217760Stuexen		.sctp_ss_clear = sctp_ss_default_clear,
858217760Stuexen		.sctp_ss_init_stream = sctp_ss_default_init_stream,
859218639Stuexen		.sctp_ss_add_to_stream = sctp_ss_rr_add,
860217760Stuexen		.sctp_ss_is_empty = sctp_ss_default_is_empty,
861217760Stuexen		.sctp_ss_remove_from_stream = sctp_ss_default_remove,
862217760Stuexen		.sctp_ss_select_stream = sctp_ss_rrp_select,
863217760Stuexen		.sctp_ss_scheduled = sctp_ss_default_scheduled,
864217760Stuexen		.sctp_ss_packet_done = sctp_ss_rrp_packet_done,
865217760Stuexen		.sctp_ss_get_value = sctp_ss_default_get_value,
866217760Stuexen		.sctp_ss_set_value = sctp_ss_default_set_value
867217760Stuexen	},
868217760Stuexen/* SCTP_SS_PRIORITY */
869217760Stuexen	{
870217760Stuexen		.sctp_ss_init = sctp_ss_default_init,
871217760Stuexen		.sctp_ss_clear = sctp_ss_prio_clear,
872217760Stuexen		.sctp_ss_init_stream = sctp_ss_prio_init_stream,
873217760Stuexen		.sctp_ss_add_to_stream = sctp_ss_prio_add,
874217760Stuexen		.sctp_ss_is_empty = sctp_ss_default_is_empty,
875217760Stuexen		.sctp_ss_remove_from_stream = sctp_ss_prio_remove,
876217760Stuexen		.sctp_ss_select_stream = sctp_ss_prio_select,
877217760Stuexen		.sctp_ss_scheduled = sctp_ss_default_scheduled,
878217760Stuexen		.sctp_ss_packet_done = sctp_ss_default_packet_done,
879217760Stuexen		.sctp_ss_get_value = sctp_ss_prio_get_value,
880217760Stuexen		.sctp_ss_set_value = sctp_ss_prio_set_value
881217760Stuexen	},
882217760Stuexen/* SCTP_SS_FAIR_BANDWITH */
883217760Stuexen	{
884217760Stuexen		.sctp_ss_init = sctp_ss_default_init,
885217760Stuexen		.sctp_ss_clear = sctp_ss_fb_clear,
886217760Stuexen		.sctp_ss_init_stream = sctp_ss_fb_init_stream,
887217760Stuexen		.sctp_ss_add_to_stream = sctp_ss_fb_add,
888217760Stuexen		.sctp_ss_is_empty = sctp_ss_default_is_empty,
889217760Stuexen		.sctp_ss_remove_from_stream = sctp_ss_fb_remove,
890217760Stuexen		.sctp_ss_select_stream = sctp_ss_fb_select,
891217760Stuexen		.sctp_ss_scheduled = sctp_ss_fb_scheduled,
892217760Stuexen		.sctp_ss_packet_done = sctp_ss_default_packet_done,
893217760Stuexen		.sctp_ss_get_value = sctp_ss_default_get_value,
894217760Stuexen		.sctp_ss_set_value = sctp_ss_default_set_value
895217760Stuexen	},
896217760Stuexen/* SCTP_SS_FIRST_COME */
897217760Stuexen	{
898217760Stuexen		.sctp_ss_init = sctp_ss_fcfs_init,
899217760Stuexen		.sctp_ss_clear = sctp_ss_fcfs_clear,
900217760Stuexen		.sctp_ss_init_stream = sctp_ss_fcfs_init_stream,
901217760Stuexen		.sctp_ss_add_to_stream = sctp_ss_fcfs_add,
902217760Stuexen		.sctp_ss_is_empty = sctp_ss_fcfs_is_empty,
903217760Stuexen		.sctp_ss_remove_from_stream = sctp_ss_fcfs_remove,
904217760Stuexen		.sctp_ss_select_stream = sctp_ss_fcfs_select,
905217760Stuexen		.sctp_ss_scheduled = sctp_ss_default_scheduled,
906217760Stuexen		.sctp_ss_packet_done = sctp_ss_default_packet_done,
907217760Stuexen		.sctp_ss_get_value = sctp_ss_default_get_value,
908217760Stuexen		.sctp_ss_set_value = sctp_ss_default_set_value
909217760Stuexen	}
910217760Stuexen};
911