sctp_ss_functions.c revision 217760
1217760Stuexen/*-
2217760Stuexen * Copyright (c) 2010, by Randall Stewart & Michael Tuexen,
3217760Stuexen * All rights reserved.
4217760Stuexen *
5217760Stuexen * Redistribution and use in source and binary forms, with or without
6217760Stuexen * modification, are permitted provided that the following conditions are met:
7217760Stuexen *
8217760Stuexen * a) Redistributions of source code must retain the above copyright notice,
9217760Stuexen *   this list of conditions and the following disclaimer.
10217760Stuexen *
11217760Stuexen * b) Redistributions in binary form must reproduce the above copyright
12217760Stuexen *    notice, this list of conditions and the following disclaimer in
13217760Stuexen *   the documentation and/or other materials provided with the distribution.
14217760Stuexen *
15217760Stuexen * c) Neither the name of Cisco Systems, Inc. nor the names of its
16217760Stuexen *    contributors may be used to endorse or promote products derived
17217760Stuexen *    from this software without specific prior written permission.
18217760Stuexen *
19217760Stuexen * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20217760Stuexen * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21217760Stuexen * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22217760Stuexen * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23217760Stuexen * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24217760Stuexen * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25217760Stuexen * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26217760Stuexen * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27217760Stuexen * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28217760Stuexen * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
29217760Stuexen * THE POSSIBILITY OF SUCH DAMAGE.
30217760Stuexen */
31217760Stuexen
32217760Stuexen#include <sys/cdefs.h>
33217760Stuexen__FBSDID("$FreeBSD: head/sys/netinet/sctp_ss_functions.c 217760 2011-01-23 19:36:28Z tuexen $");
34217760Stuexen
35217760Stuexen#include <netinet/sctp_pcb.h>
36217760Stuexen
37217760Stuexen/*
38217760Stuexen * Default simple round-robin algorithm.
39217760Stuexen * Just interates the streams in the order they appear.
40217760Stuexen */
41217760Stuexen
42217760Stuexenstatic void
43217760Stuexensctp_ss_default_add(struct sctp_tcb *, struct sctp_association *,
44217760Stuexen    struct sctp_stream_out *,
45217760Stuexen    struct sctp_stream_queue_pending *, int);
46217760Stuexen
47217760Stuexenstatic void
48217760Stuexensctp_ss_default_remove(struct sctp_tcb *, struct sctp_association *,
49217760Stuexen    struct sctp_stream_out *,
50217760Stuexen    struct sctp_stream_queue_pending *, int);
51217760Stuexen
52217760Stuexenstatic void
53217760Stuexensctp_ss_default_init(struct sctp_tcb *stcb, struct sctp_association *asoc,
54217760Stuexen    int holds_lock)
55217760Stuexen{
56217760Stuexen	uint16_t i;
57217760Stuexen
58217760Stuexen	TAILQ_INIT(&asoc->ss_data.out_wheel);
59217760Stuexen	for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
60217760Stuexen		if (!TAILQ_EMPTY(&stcb->asoc.strmout[i].outqueue)) {
61217760Stuexen			sctp_ss_default_add(stcb, &stcb->asoc,
62217760Stuexen			    &stcb->asoc.strmout[i],
63217760Stuexen			    NULL, holds_lock);
64217760Stuexen		}
65217760Stuexen	}
66217760Stuexen	return;
67217760Stuexen}
68217760Stuexen
69217760Stuexenstatic void
70217760Stuexensctp_ss_default_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
71217760Stuexen    int clear_values, int holds_lock)
72217760Stuexen{
73217760Stuexen	uint16_t i;
74217760Stuexen
75217760Stuexen	for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
76217760Stuexen		if (!TAILQ_EMPTY(&stcb->asoc.strmout[i].outqueue)) {
77217760Stuexen			sctp_ss_default_remove(stcb, &stcb->asoc,
78217760Stuexen			    &stcb->asoc.strmout[i],
79217760Stuexen			    NULL, holds_lock);
80217760Stuexen		}
81217760Stuexen	}
82217760Stuexen	return;
83217760Stuexen}
84217760Stuexen
85217760Stuexenstatic void
86217760Stuexensctp_ss_default_init_stream(struct sctp_stream_out *strq)
87217760Stuexen{
88217760Stuexen	strq->ss_params.rr.next_spoke.tqe_next = NULL;
89217760Stuexen	strq->ss_params.rr.next_spoke.tqe_prev = NULL;
90217760Stuexen	return;
91217760Stuexen}
92217760Stuexen
93217760Stuexenstatic void
94217760Stuexensctp_ss_default_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
95217760Stuexen    struct sctp_stream_out *strq,
96217760Stuexen    struct sctp_stream_queue_pending *sp, int holds_lock)
97217760Stuexen{
98217760Stuexen	if (holds_lock == 0) {
99217760Stuexen		SCTP_TCB_SEND_LOCK(stcb);
100217760Stuexen	}
101217760Stuexen	if ((strq->ss_params.rr.next_spoke.tqe_next == NULL) &&
102217760Stuexen	    (strq->ss_params.rr.next_spoke.tqe_prev == NULL)) {
103217760Stuexen		TAILQ_INSERT_TAIL(&asoc->ss_data.out_wheel,
104217760Stuexen		    strq, ss_params.rr.next_spoke);
105217760Stuexen	}
106217760Stuexen	if (holds_lock == 0) {
107217760Stuexen		SCTP_TCB_SEND_UNLOCK(stcb);
108217760Stuexen	}
109217760Stuexen	return;
110217760Stuexen}
111217760Stuexen
112217760Stuexenstatic int
113217760Stuexensctp_ss_default_is_empty(struct sctp_tcb *stcb, struct sctp_association *asoc)
114217760Stuexen{
115217760Stuexen	if (TAILQ_EMPTY(&asoc->ss_data.out_wheel)) {
116217760Stuexen		return (1);
117217760Stuexen	} else {
118217760Stuexen		return (0);
119217760Stuexen	}
120217760Stuexen}
121217760Stuexen
122217760Stuexenstatic void
123217760Stuexensctp_ss_default_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
124217760Stuexen    struct sctp_stream_out *strq,
125217760Stuexen    struct sctp_stream_queue_pending *sp, int holds_lock)
126217760Stuexen{
127217760Stuexen	/* take off and then setup so we know it is not on the wheel */
128217760Stuexen	if (holds_lock == 0) {
129217760Stuexen		SCTP_TCB_SEND_LOCK(stcb);
130217760Stuexen	}
131217760Stuexen	if (TAILQ_EMPTY(&strq->outqueue)) {
132217760Stuexen		if (asoc->last_out_stream == strq) {
133217760Stuexen			asoc->last_out_stream = TAILQ_PREV(asoc->last_out_stream,
134217760Stuexen			    sctpwheel_listhead,
135217760Stuexen			    ss_params.rr.next_spoke);
136217760Stuexen			if (asoc->last_out_stream == NULL) {
137217760Stuexen				asoc->last_out_stream = TAILQ_LAST(&asoc->ss_data.out_wheel,
138217760Stuexen				    sctpwheel_listhead);
139217760Stuexen			}
140217760Stuexen			if (asoc->last_out_stream == strq) {
141217760Stuexen				asoc->last_out_stream = NULL;
142217760Stuexen			}
143217760Stuexen		}
144217760Stuexen		TAILQ_REMOVE(&asoc->ss_data.out_wheel, strq, ss_params.rr.next_spoke);
145217760Stuexen		strq->ss_params.rr.next_spoke.tqe_next = NULL;
146217760Stuexen		strq->ss_params.rr.next_spoke.tqe_prev = NULL;
147217760Stuexen	}
148217760Stuexen	if (holds_lock == 0) {
149217760Stuexen		SCTP_TCB_SEND_UNLOCK(stcb);
150217760Stuexen	}
151217760Stuexen	return;
152217760Stuexen}
153217760Stuexen
154217760Stuexen
155217760Stuexenstatic struct sctp_stream_out *
156217760Stuexensctp_ss_default_select(struct sctp_tcb *stcb, struct sctp_nets *net,
157217760Stuexen    struct sctp_association *asoc)
158217760Stuexen{
159217760Stuexen	struct sctp_stream_out *strq, *strqt;
160217760Stuexen
161217760Stuexen	strqt = asoc->last_out_stream;
162217760Stuexendefault_again:
163217760Stuexen	/* Find the next stream to use */
164217760Stuexen	if (strqt == NULL) {
165217760Stuexen		strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
166217760Stuexen	} else {
167217760Stuexen		strq = TAILQ_NEXT(strqt, ss_params.rr.next_spoke);
168217760Stuexen		if (strq == NULL) {
169217760Stuexen			strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
170217760Stuexen		}
171217760Stuexen	}
172217760Stuexen
173217760Stuexen	/*
174217760Stuexen	 * If CMT is off, we must validate that the stream in question has
175217760Stuexen	 * the first item pointed towards are network destionation requested
176217760Stuexen	 * by the caller. Note that if we turn out to be locked to a stream
177217760Stuexen	 * (assigning TSN's then we must stop, since we cannot look for
178217760Stuexen	 * another stream with data to send to that destination). In CMT's
179217760Stuexen	 * case, by skipping this check, we will send one data packet
180217760Stuexen	 * towards the requested net.
181217760Stuexen	 */
182217760Stuexen	if (net != NULL && strq != NULL &&
183217760Stuexen	    SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
184217760Stuexen		if (TAILQ_FIRST(&strq->outqueue) &&
185217760Stuexen		    TAILQ_FIRST(&strq->outqueue)->net != NULL &&
186217760Stuexen		    TAILQ_FIRST(&strq->outqueue)->net != net) {
187217760Stuexen			if (strq == asoc->last_out_stream) {
188217760Stuexen				return (NULL);
189217760Stuexen			} else {
190217760Stuexen				strqt = strq;
191217760Stuexen				goto default_again;
192217760Stuexen			}
193217760Stuexen		}
194217760Stuexen	}
195217760Stuexen	return (strq);
196217760Stuexen}
197217760Stuexen
198217760Stuexenstatic void
199217760Stuexensctp_ss_default_scheduled(struct sctp_tcb *stcb, struct sctp_nets *net,
200217760Stuexen    struct sctp_association *asoc,
201217760Stuexen    struct sctp_stream_out *strq, int moved_how_much)
202217760Stuexen{
203217760Stuexen	asoc->last_out_stream = strq;
204217760Stuexen	return;
205217760Stuexen}
206217760Stuexen
207217760Stuexenstatic void
208217760Stuexensctp_ss_default_packet_done(struct sctp_tcb *stcb, struct sctp_nets *net,
209217760Stuexen    struct sctp_association *asoc)
210217760Stuexen{
211217760Stuexen	/* Nothing to be done here */
212217760Stuexen	return;
213217760Stuexen}
214217760Stuexen
215217760Stuexenstatic int
216217760Stuexensctp_ss_default_get_value(struct sctp_tcb *stcb, struct sctp_association *asoc,
217217760Stuexen    struct sctp_stream_out *strq, uint16_t * value)
218217760Stuexen{
219217760Stuexen	/* Nothing to be done here */
220217760Stuexen	return (-1);
221217760Stuexen}
222217760Stuexen
223217760Stuexenstatic int
224217760Stuexensctp_ss_default_set_value(struct sctp_tcb *stcb, struct sctp_association *asoc,
225217760Stuexen    struct sctp_stream_out *strq, uint16_t value)
226217760Stuexen{
227217760Stuexen	/* Nothing to be done here */
228217760Stuexen	return (-1);
229217760Stuexen}
230217760Stuexen
231217760Stuexen/*
232217760Stuexen * Real round-robin algorithm.
233217760Stuexen * Always interates the streams in ascending order.
234217760Stuexen */
235217760Stuexenstatic void
236217760Stuexensctp_ss_rr_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
237217760Stuexen    struct sctp_stream_out *strq,
238217760Stuexen    struct sctp_stream_queue_pending *sp, int holds_lock)
239217760Stuexen{
240217760Stuexen	struct sctp_stream_out *strqt;
241217760Stuexen
242217760Stuexen	if (holds_lock == 0) {
243217760Stuexen		SCTP_TCB_SEND_LOCK(stcb);
244217760Stuexen	}
245217760Stuexen	if ((strq->ss_params.rr.next_spoke.tqe_next == NULL) &&
246217760Stuexen	    (strq->ss_params.rr.next_spoke.tqe_prev == NULL)) {
247217760Stuexen		if (TAILQ_EMPTY(&asoc->ss_data.out_wheel)) {
248217760Stuexen			TAILQ_INSERT_HEAD(&asoc->ss_data.out_wheel, strq, ss_params.rr.next_spoke);
249217760Stuexen		} else {
250217760Stuexen			strqt = TAILQ_FIRST(&asoc->ss_data.out_wheel);
251217760Stuexen			while (strqt != NULL && (strqt->stream_no < strq->stream_no)) {
252217760Stuexen				strqt = TAILQ_NEXT(strqt, ss_params.rr.next_spoke);
253217760Stuexen			}
254217760Stuexen			if (strqt != NULL) {
255217760Stuexen				TAILQ_INSERT_BEFORE(strqt, strq, ss_params.rr.next_spoke);
256217760Stuexen			} else {
257217760Stuexen				TAILQ_INSERT_TAIL(&asoc->ss_data.out_wheel, strq, ss_params.rr.next_spoke);
258217760Stuexen			}
259217760Stuexen		}
260217760Stuexen	}
261217760Stuexen	if (holds_lock == 0) {
262217760Stuexen		SCTP_TCB_SEND_UNLOCK(stcb);
263217760Stuexen	}
264217760Stuexen	return;
265217760Stuexen}
266217760Stuexen
267217760Stuexen/*
268217760Stuexen * Real round-robin per packet algorithm.
269217760Stuexen * Always interates the streams in ascending order and
270217760Stuexen * only fills messages of the same stream in a packet.
271217760Stuexen */
272217760Stuexenstatic void
273217760Stuexensctp_ss_rrp_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
274217760Stuexen    struct sctp_stream_out *strq,
275217760Stuexen    struct sctp_stream_queue_pending *sp, int holds_lock)
276217760Stuexen{
277217760Stuexen	struct sctp_stream_out *strqt;
278217760Stuexen
279217760Stuexen	if (holds_lock == 0) {
280217760Stuexen		SCTP_TCB_SEND_LOCK(stcb);
281217760Stuexen	}
282217760Stuexen	if ((strq->ss_params.rr.next_spoke.tqe_next == NULL) &&
283217760Stuexen	    (strq->ss_params.rr.next_spoke.tqe_prev == NULL)) {
284217760Stuexen
285217760Stuexen		if (TAILQ_EMPTY(&asoc->ss_data.out_wheel)) {
286217760Stuexen			TAILQ_INSERT_HEAD(&asoc->ss_data.out_wheel, strq, ss_params.rr.next_spoke);
287217760Stuexen		} else {
288217760Stuexen			strqt = TAILQ_FIRST(&asoc->ss_data.out_wheel);
289217760Stuexen			while (strqt != NULL && strqt->stream_no < strq->stream_no) {
290217760Stuexen				strqt = TAILQ_NEXT(strqt, ss_params.rr.next_spoke);
291217760Stuexen			}
292217760Stuexen			if (strqt != NULL) {
293217760Stuexen				TAILQ_INSERT_BEFORE(strqt, strq, ss_params.rr.next_spoke);
294217760Stuexen			} else {
295217760Stuexen				TAILQ_INSERT_TAIL(&asoc->ss_data.out_wheel, strq, ss_params.rr.next_spoke);
296217760Stuexen			}
297217760Stuexen		}
298217760Stuexen	}
299217760Stuexen	if (holds_lock == 0) {
300217760Stuexen		SCTP_TCB_SEND_UNLOCK(stcb);
301217760Stuexen	}
302217760Stuexen	return;
303217760Stuexen}
304217760Stuexen
305217760Stuexenstatic struct sctp_stream_out *
306217760Stuexensctp_ss_rrp_select(struct sctp_tcb *stcb, struct sctp_nets *net,
307217760Stuexen    struct sctp_association *asoc)
308217760Stuexen{
309217760Stuexen	struct sctp_stream_out *strq, *strqt;
310217760Stuexen
311217760Stuexen	strqt = asoc->last_out_stream;
312217760Stuexen	if (strqt != NULL && !TAILQ_EMPTY(&strqt->outqueue)) {
313217760Stuexen		return (strqt);
314217760Stuexen	}
315217760Stuexenrrp_again:
316217760Stuexen	/* Find the next stream to use */
317217760Stuexen	if (strqt == NULL) {
318217760Stuexen		strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
319217760Stuexen	} else {
320217760Stuexen		strq = TAILQ_NEXT(strqt, ss_params.rr.next_spoke);
321217760Stuexen		if (strq == NULL) {
322217760Stuexen			strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
323217760Stuexen		}
324217760Stuexen	}
325217760Stuexen
326217760Stuexen	/*
327217760Stuexen	 * If CMT is off, we must validate that the stream in question has
328217760Stuexen	 * the first item pointed towards are network destionation requested
329217760Stuexen	 * by the caller. Note that if we turn out to be locked to a stream
330217760Stuexen	 * (assigning TSN's then we must stop, since we cannot look for
331217760Stuexen	 * another stream with data to send to that destination). In CMT's
332217760Stuexen	 * case, by skipping this check, we will send one data packet
333217760Stuexen	 * towards the requested net.
334217760Stuexen	 */
335217760Stuexen	if (net != NULL && strq != NULL &&
336217760Stuexen	    SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
337217760Stuexen		if (TAILQ_FIRST(&strq->outqueue) &&
338217760Stuexen		    TAILQ_FIRST(&strq->outqueue)->net != NULL &&
339217760Stuexen		    TAILQ_FIRST(&strq->outqueue)->net != net) {
340217760Stuexen			if (strq == asoc->last_out_stream) {
341217760Stuexen				return (NULL);
342217760Stuexen			} else {
343217760Stuexen				strqt = strq;
344217760Stuexen				goto rrp_again;
345217760Stuexen			}
346217760Stuexen		}
347217760Stuexen	}
348217760Stuexen	return (strq);
349217760Stuexen}
350217760Stuexen
351217760Stuexenstatic void
352217760Stuexensctp_ss_rrp_packet_done(struct sctp_tcb *stcb, struct sctp_nets *net,
353217760Stuexen    struct sctp_association *asoc)
354217760Stuexen{
355217760Stuexen	struct sctp_stream_out *strq, *strqt;
356217760Stuexen
357217760Stuexen	strqt = asoc->last_out_stream;
358217760Stuexenrrp_pd_again:
359217760Stuexen	/* Find the next stream to use */
360217760Stuexen	if (strqt == NULL) {
361217760Stuexen		strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
362217760Stuexen	} else {
363217760Stuexen		strq = TAILQ_NEXT(strqt, ss_params.rr.next_spoke);
364217760Stuexen		if (strq == NULL) {
365217760Stuexen			strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
366217760Stuexen		}
367217760Stuexen	}
368217760Stuexen
369217760Stuexen	/*
370217760Stuexen	 * If CMT is off, we must validate that the stream in question has
371217760Stuexen	 * the first item pointed towards are network destionation requested
372217760Stuexen	 * by the caller. Note that if we turn out to be locked to a stream
373217760Stuexen	 * (assigning TSN's then we must stop, since we cannot look for
374217760Stuexen	 * another stream with data to send to that destination). In CMT's
375217760Stuexen	 * case, by skipping this check, we will send one data packet
376217760Stuexen	 * towards the requested net.
377217760Stuexen	 */
378217760Stuexen	if ((strq != NULL) && TAILQ_FIRST(&strq->outqueue) &&
379217760Stuexen	    (net != NULL && TAILQ_FIRST(&strq->outqueue)->net != net) &&
380217760Stuexen	    (SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0)) {
381217760Stuexen		if (strq == asoc->last_out_stream) {
382217760Stuexen			strq = NULL;
383217760Stuexen		} else {
384217760Stuexen			strqt = strq;
385217760Stuexen			goto rrp_pd_again;
386217760Stuexen		}
387217760Stuexen	}
388217760Stuexen	asoc->last_out_stream = strq;
389217760Stuexen	return;
390217760Stuexen}
391217760Stuexen
392217760Stuexen
393217760Stuexen/*
394217760Stuexen * Priority algorithm.
395217760Stuexen * Always prefers streams based on their priority id.
396217760Stuexen */
397217760Stuexenstatic void
398217760Stuexensctp_ss_prio_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
399217760Stuexen    int clear_values, int holds_lock)
400217760Stuexen{
401217760Stuexen	uint16_t i;
402217760Stuexen
403217760Stuexen	for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
404217760Stuexen		if (!TAILQ_EMPTY(&stcb->asoc.strmout[i].outqueue)) {
405217760Stuexen			if (clear_values)
406217760Stuexen				stcb->asoc.strmout[i].ss_params.prio.priority = 0;
407217760Stuexen			sctp_ss_default_remove(stcb, &stcb->asoc, &stcb->asoc.strmout[i], NULL, holds_lock);
408217760Stuexen		}
409217760Stuexen	}
410217760Stuexen	return;
411217760Stuexen}
412217760Stuexen
413217760Stuexenstatic void
414217760Stuexensctp_ss_prio_init_stream(struct sctp_stream_out *strq)
415217760Stuexen{
416217760Stuexen	strq->ss_params.prio.next_spoke.tqe_next = NULL;
417217760Stuexen	strq->ss_params.prio.next_spoke.tqe_prev = NULL;
418217760Stuexen	strq->ss_params.prio.priority = 0;
419217760Stuexen	return;
420217760Stuexen}
421217760Stuexen
422217760Stuexenstatic void
423217760Stuexensctp_ss_prio_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
424217760Stuexen    struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp,
425217760Stuexen    int holds_lock)
426217760Stuexen{
427217760Stuexen	struct sctp_stream_out *strqt;
428217760Stuexen
429217760Stuexen	if (holds_lock == 0) {
430217760Stuexen		SCTP_TCB_SEND_LOCK(stcb);
431217760Stuexen	}
432217760Stuexen	if ((strq->ss_params.prio.next_spoke.tqe_next == NULL) &&
433217760Stuexen	    (strq->ss_params.prio.next_spoke.tqe_prev == NULL)) {
434217760Stuexen
435217760Stuexen		if (TAILQ_EMPTY(&asoc->ss_data.out_wheel)) {
436217760Stuexen			TAILQ_INSERT_HEAD(&asoc->ss_data.out_wheel, strq, ss_params.prio.next_spoke);
437217760Stuexen		} else {
438217760Stuexen			strqt = TAILQ_FIRST(&asoc->ss_data.out_wheel);
439217760Stuexen			while (strqt != NULL && strqt->ss_params.prio.priority < strq->ss_params.prio.priority) {
440217760Stuexen				strqt = TAILQ_NEXT(strqt, ss_params.prio.next_spoke);
441217760Stuexen			}
442217760Stuexen			if (strqt != NULL) {
443217760Stuexen				TAILQ_INSERT_BEFORE(strqt, strq, ss_params.prio.next_spoke);
444217760Stuexen			} else {
445217760Stuexen				TAILQ_INSERT_TAIL(&asoc->ss_data.out_wheel, strq, ss_params.prio.next_spoke);
446217760Stuexen			}
447217760Stuexen		}
448217760Stuexen	}
449217760Stuexen	if (holds_lock == 0) {
450217760Stuexen		SCTP_TCB_SEND_UNLOCK(stcb);
451217760Stuexen	}
452217760Stuexen	return;
453217760Stuexen}
454217760Stuexen
455217760Stuexenstatic void
456217760Stuexensctp_ss_prio_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
457217760Stuexen    struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp,
458217760Stuexen    int holds_lock)
459217760Stuexen{
460217760Stuexen	/* take off and then setup so we know it is not on the wheel */
461217760Stuexen	if (holds_lock == 0) {
462217760Stuexen		SCTP_TCB_SEND_LOCK(stcb);
463217760Stuexen	}
464217760Stuexen	if (TAILQ_EMPTY(&strq->outqueue)) {
465217760Stuexen		if (asoc->last_out_stream == strq) {
466217760Stuexen			asoc->last_out_stream = TAILQ_PREV(asoc->last_out_stream, sctpwheel_listhead,
467217760Stuexen			    ss_params.prio.next_spoke);
468217760Stuexen			if (asoc->last_out_stream == NULL) {
469217760Stuexen				asoc->last_out_stream = TAILQ_LAST(&asoc->ss_data.out_wheel,
470217760Stuexen				    sctpwheel_listhead);
471217760Stuexen			}
472217760Stuexen			if (asoc->last_out_stream == strq) {
473217760Stuexen				asoc->last_out_stream = NULL;
474217760Stuexen			}
475217760Stuexen		}
476217760Stuexen		TAILQ_REMOVE(&asoc->ss_data.out_wheel, strq, ss_params.rr.next_spoke);
477217760Stuexen		strq->ss_params.prio.next_spoke.tqe_next = NULL;
478217760Stuexen		strq->ss_params.prio.next_spoke.tqe_prev = NULL;
479217760Stuexen	}
480217760Stuexen	if (holds_lock == 0) {
481217760Stuexen		SCTP_TCB_SEND_UNLOCK(stcb);
482217760Stuexen	}
483217760Stuexen	return;
484217760Stuexen}
485217760Stuexen
486217760Stuexenstatic struct sctp_stream_out *
487217760Stuexensctp_ss_prio_select(struct sctp_tcb *stcb, struct sctp_nets *net,
488217760Stuexen    struct sctp_association *asoc)
489217760Stuexen{
490217760Stuexen	struct sctp_stream_out *strq, *strqt, *strqn;
491217760Stuexen
492217760Stuexen	strqt = asoc->last_out_stream;
493217760Stuexenprio_again:
494217760Stuexen	/* Find the next stream to use */
495217760Stuexen	if (strqt == NULL) {
496217760Stuexen		strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
497217760Stuexen	} else {
498217760Stuexen		strqn = TAILQ_NEXT(strqt, ss_params.prio.next_spoke);
499217760Stuexen		if (strqn != NULL &&
500217760Stuexen		    strqn->ss_params.prio.priority == strqt->ss_params.prio.priority) {
501217760Stuexen			strq = TAILQ_NEXT(strqt, ss_params.prio.next_spoke);
502217760Stuexen		} else {
503217760Stuexen			strq = TAILQ_FIRST(&asoc->ss_data.out_wheel);
504217760Stuexen		}
505217760Stuexen	}
506217760Stuexen
507217760Stuexen	/*
508217760Stuexen	 * If CMT is off, we must validate that the stream in question has
509217760Stuexen	 * the first item pointed towards are network destionation requested
510217760Stuexen	 * by the caller. Note that if we turn out to be locked to a stream
511217760Stuexen	 * (assigning TSN's then we must stop, since we cannot look for
512217760Stuexen	 * another stream with data to send to that destination). In CMT's
513217760Stuexen	 * case, by skipping this check, we will send one data packet
514217760Stuexen	 * towards the requested net.
515217760Stuexen	 */
516217760Stuexen	if (net != NULL && strq != NULL &&
517217760Stuexen	    SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
518217760Stuexen		if (TAILQ_FIRST(&strq->outqueue) &&
519217760Stuexen		    TAILQ_FIRST(&strq->outqueue)->net != NULL &&
520217760Stuexen		    TAILQ_FIRST(&strq->outqueue)->net != net) {
521217760Stuexen			if (strq == asoc->last_out_stream) {
522217760Stuexen				return (NULL);
523217760Stuexen			} else {
524217760Stuexen				strqt = strq;
525217760Stuexen				goto prio_again;
526217760Stuexen			}
527217760Stuexen		}
528217760Stuexen	}
529217760Stuexen	return (strq);
530217760Stuexen}
531217760Stuexen
532217760Stuexenstatic int
533217760Stuexensctp_ss_prio_get_value(struct sctp_tcb *stcb, struct sctp_association *asoc,
534217760Stuexen    struct sctp_stream_out *strq, uint16_t * value)
535217760Stuexen{
536217760Stuexen	if (strq == NULL) {
537217760Stuexen		return (-1);
538217760Stuexen	}
539217760Stuexen	*value = strq->ss_params.prio.priority;
540217760Stuexen	return (1);
541217760Stuexen}
542217760Stuexen
543217760Stuexenstatic int
544217760Stuexensctp_ss_prio_set_value(struct sctp_tcb *stcb, struct sctp_association *asoc,
545217760Stuexen    struct sctp_stream_out *strq, uint16_t value)
546217760Stuexen{
547217760Stuexen	if (strq == NULL) {
548217760Stuexen		return (-1);
549217760Stuexen	}
550217760Stuexen	strq->ss_params.prio.priority = value;
551217760Stuexen	sctp_ss_prio_remove(stcb, asoc, strq, NULL, 1);
552217760Stuexen	sctp_ss_prio_add(stcb, asoc, strq, NULL, 1);
553217760Stuexen	return (1);
554217760Stuexen}
555217760Stuexen
556217760Stuexen/*
557217760Stuexen * Fair bandwidth algorithm.
558217760Stuexen * Maintains an equal troughput per stream.
559217760Stuexen */
560217760Stuexenstatic void
561217760Stuexensctp_ss_fb_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
562217760Stuexen    int clear_values, int holds_lock)
563217760Stuexen{
564217760Stuexen	uint16_t i;
565217760Stuexen
566217760Stuexen	for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
567217760Stuexen		if (!TAILQ_EMPTY(&stcb->asoc.strmout[i].outqueue)) {
568217760Stuexen			if (clear_values) {
569217760Stuexen				stcb->asoc.strmout[i].ss_params.fb.rounds = -1;
570217760Stuexen			}
571217760Stuexen			sctp_ss_default_remove(stcb, &stcb->asoc, &stcb->asoc.strmout[i], NULL, holds_lock);
572217760Stuexen		}
573217760Stuexen	}
574217760Stuexen	return;
575217760Stuexen}
576217760Stuexen
577217760Stuexenstatic void
578217760Stuexensctp_ss_fb_init_stream(struct sctp_stream_out *strq)
579217760Stuexen{
580217760Stuexen	strq->ss_params.fb.next_spoke.tqe_next = NULL;
581217760Stuexen	strq->ss_params.fb.next_spoke.tqe_prev = NULL;
582217760Stuexen	strq->ss_params.fb.rounds = -1;
583217760Stuexen	return;
584217760Stuexen}
585217760Stuexen
586217760Stuexenstatic void
587217760Stuexensctp_ss_fb_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
588217760Stuexen    struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp,
589217760Stuexen    int holds_lock)
590217760Stuexen{
591217760Stuexen	if (holds_lock == 0) {
592217760Stuexen		SCTP_TCB_SEND_LOCK(stcb);
593217760Stuexen	}
594217760Stuexen	if ((strq->ss_params.rr.next_spoke.tqe_next == NULL) &&
595217760Stuexen	    (strq->ss_params.rr.next_spoke.tqe_prev == NULL)) {
596217760Stuexen		if (!TAILQ_EMPTY(&strq->outqueue) && strq->ss_params.fb.rounds < 0)
597217760Stuexen			strq->ss_params.fb.rounds = TAILQ_FIRST(&strq->outqueue)->length;
598217760Stuexen		TAILQ_INSERT_TAIL(&asoc->ss_data.out_wheel, strq, ss_params.rr.next_spoke);
599217760Stuexen	}
600217760Stuexen	if (holds_lock == 0) {
601217760Stuexen		SCTP_TCB_SEND_UNLOCK(stcb);
602217760Stuexen	}
603217760Stuexen	return;
604217760Stuexen}
605217760Stuexen
606217760Stuexenstatic void
607217760Stuexensctp_ss_fb_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
608217760Stuexen    struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp,
609217760Stuexen    int holds_lock)
610217760Stuexen{
611217760Stuexen	/* take off and then setup so we know it is not on the wheel */
612217760Stuexen	if (holds_lock == 0) {
613217760Stuexen		SCTP_TCB_SEND_LOCK(stcb);
614217760Stuexen	}
615217760Stuexen	if (TAILQ_EMPTY(&strq->outqueue)) {
616217760Stuexen		if (asoc->last_out_stream == strq) {
617217760Stuexen			asoc->last_out_stream = TAILQ_PREV(asoc->last_out_stream, sctpwheel_listhead,
618217760Stuexen			    ss_params.fb.next_spoke);
619217760Stuexen			if (asoc->last_out_stream == NULL) {
620217760Stuexen				asoc->last_out_stream = TAILQ_LAST(&asoc->ss_data.out_wheel,
621217760Stuexen				    sctpwheel_listhead);
622217760Stuexen			}
623217760Stuexen			if (asoc->last_out_stream == strq) {
624217760Stuexen				asoc->last_out_stream = NULL;
625217760Stuexen			}
626217760Stuexen		}
627217760Stuexen		strq->ss_params.fb.rounds = -1;
628217760Stuexen		TAILQ_REMOVE(&asoc->ss_data.out_wheel, strq, ss_params.fb.next_spoke);
629217760Stuexen		strq->ss_params.fb.next_spoke.tqe_next = NULL;
630217760Stuexen		strq->ss_params.fb.next_spoke.tqe_prev = NULL;
631217760Stuexen	}
632217760Stuexen	if (holds_lock == 0) {
633217760Stuexen		SCTP_TCB_SEND_UNLOCK(stcb);
634217760Stuexen	}
635217760Stuexen	return;
636217760Stuexen}
637217760Stuexen
638217760Stuexenstatic struct sctp_stream_out *
639217760Stuexensctp_ss_fb_select(struct sctp_tcb *stcb, struct sctp_nets *net,
640217760Stuexen    struct sctp_association *asoc)
641217760Stuexen{
642217760Stuexen	struct sctp_stream_out *strq = NULL, *strqt;
643217760Stuexen
644217760Stuexen	if (TAILQ_FIRST(&asoc->ss_data.out_wheel) == TAILQ_LAST(&asoc->ss_data.out_wheel, sctpwheel_listhead)) {
645217760Stuexen		strqt = TAILQ_FIRST(&asoc->ss_data.out_wheel);
646217760Stuexen	} else {
647217760Stuexen		if (asoc->last_out_stream != NULL) {
648217760Stuexen			strqt = TAILQ_NEXT(asoc->last_out_stream, ss_params.fb.next_spoke);
649217760Stuexen		} else {
650217760Stuexen			strqt = TAILQ_FIRST(&asoc->ss_data.out_wheel);
651217760Stuexen		}
652217760Stuexen	}
653217760Stuexen	do {
654217760Stuexen		if ((strqt != NULL) && TAILQ_FIRST(&strqt->outqueue) &&
655217760Stuexen		    TAILQ_FIRST(&strqt->outqueue)->net != NULL &&
656217760Stuexen		    ((net == NULL || TAILQ_FIRST(&strqt->outqueue)->net == net) ||
657217760Stuexen		    (SCTP_BASE_SYSCTL(sctp_cmt_on_off) > 0))) {
658217760Stuexen			if ((strqt->ss_params.fb.rounds >= 0) && (strq == NULL ||
659217760Stuexen			    strqt->ss_params.fb.rounds < strq->ss_params.fb.rounds)) {
660217760Stuexen				strq = strqt;
661217760Stuexen			}
662217760Stuexen		}
663217760Stuexen		if (strqt != NULL) {
664217760Stuexen			strqt = TAILQ_NEXT(strqt, ss_params.fb.next_spoke);
665217760Stuexen		} else {
666217760Stuexen			strqt = TAILQ_FIRST(&asoc->ss_data.out_wheel);
667217760Stuexen		}
668217760Stuexen	} while (strqt != strq);
669217760Stuexen	return (strq);
670217760Stuexen}
671217760Stuexen
672217760Stuexenstatic void
673217760Stuexensctp_ss_fb_scheduled(struct sctp_tcb *stcb, struct sctp_nets *net,
674217760Stuexen    struct sctp_association *asoc, struct sctp_stream_out *strq,
675217760Stuexen    int moved_how_much)
676217760Stuexen{
677217760Stuexen	struct sctp_stream_out *strqt;
678217760Stuexen	int subtract;
679217760Stuexen
680217760Stuexen	subtract = strq->ss_params.fb.rounds;
681217760Stuexen	TAILQ_FOREACH(strqt, &asoc->ss_data.out_wheel, ss_params.fb.next_spoke) {
682217760Stuexen		strqt->ss_params.fb.rounds -= subtract;
683217760Stuexen		if (strqt->ss_params.fb.rounds < 0)
684217760Stuexen			strqt->ss_params.fb.rounds = 0;
685217760Stuexen	}
686217760Stuexen	if (TAILQ_FIRST(&strq->outqueue)) {
687217760Stuexen		strq->ss_params.fb.rounds = TAILQ_FIRST(&strq->outqueue)->length;
688217760Stuexen	} else {
689217760Stuexen		strq->ss_params.fb.rounds = -1;
690217760Stuexen	}
691217760Stuexen	asoc->last_out_stream = strq;
692217760Stuexen	return;
693217760Stuexen}
694217760Stuexen
695217760Stuexen/*
696217760Stuexen * First-come, first-serve algorithm.
697217760Stuexen * Maintains the order provided by the application.
698217760Stuexen */
699217760Stuexenstatic void
700217760Stuexensctp_ss_fcfs_init(struct sctp_tcb *stcb, struct sctp_association *asoc,
701217760Stuexen    int holds_lock)
702217760Stuexen{
703217760Stuexen	int x, element = 0, add_more = 1;
704217760Stuexen	struct sctp_stream_queue_pending *sp;
705217760Stuexen	uint16_t i;
706217760Stuexen
707217760Stuexen	TAILQ_INIT(&asoc->ss_data.out_list);
708217760Stuexen	while (add_more) {
709217760Stuexen		add_more = 0;
710217760Stuexen		for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
711217760Stuexen			sp = TAILQ_FIRST(&asoc->ss_data.out_list);
712217760Stuexen			x = element;
713217760Stuexen			while (sp != NULL && x > 0) {
714217760Stuexen				sp = TAILQ_NEXT(sp, next);
715217760Stuexen			}
716217760Stuexen			if (sp != NULL) {
717217760Stuexen				sctp_ss_default_add(stcb, &stcb->asoc, &stcb->asoc.strmout[i], NULL, holds_lock);
718217760Stuexen				add_more = 1;
719217760Stuexen			}
720217760Stuexen		}
721217760Stuexen		element++;
722217760Stuexen	}
723217760Stuexen	return;
724217760Stuexen}
725217760Stuexen
726217760Stuexenstatic void
727217760Stuexensctp_ss_fcfs_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
728217760Stuexen    int clear_values, int holds_lock)
729217760Stuexen{
730217760Stuexen	if (clear_values) {
731217760Stuexen		while (!TAILQ_EMPTY(&asoc->ss_data.out_list)) {
732217760Stuexen			TAILQ_REMOVE(&asoc->ss_data.out_list, TAILQ_FIRST(&asoc->ss_data.out_list), next);
733217760Stuexen		}
734217760Stuexen	}
735217760Stuexen	return;
736217760Stuexen}
737217760Stuexen
738217760Stuexenstatic void
739217760Stuexensctp_ss_fcfs_init_stream(struct sctp_stream_out *strq)
740217760Stuexen{
741217760Stuexen	/* Nothing to be done here */
742217760Stuexen	return;
743217760Stuexen}
744217760Stuexen
745217760Stuexenstatic void
746217760Stuexensctp_ss_fcfs_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
747217760Stuexen    struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp,
748217760Stuexen    int holds_lock)
749217760Stuexen{
750217760Stuexen	if (holds_lock == 0) {
751217760Stuexen		SCTP_TCB_SEND_LOCK(stcb);
752217760Stuexen	}
753217760Stuexen	if (sp && (sp->next.tqe_next == NULL) &&
754217760Stuexen	    (sp->next.tqe_prev == NULL)) {
755217760Stuexen		TAILQ_INSERT_TAIL(&asoc->ss_data.out_list, sp, next);
756217760Stuexen	}
757217760Stuexen	if (holds_lock == 0) {
758217760Stuexen		SCTP_TCB_SEND_UNLOCK(stcb);
759217760Stuexen	}
760217760Stuexen	return;
761217760Stuexen}
762217760Stuexen
763217760Stuexenstatic int
764217760Stuexensctp_ss_fcfs_is_empty(struct sctp_tcb *stcb, struct sctp_association *asoc)
765217760Stuexen{
766217760Stuexen	if (TAILQ_EMPTY(&asoc->ss_data.out_list)) {
767217760Stuexen		return (1);
768217760Stuexen	} else {
769217760Stuexen		return (0);
770217760Stuexen	}
771217760Stuexen}
772217760Stuexen
773217760Stuexenstatic void
774217760Stuexensctp_ss_fcfs_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
775217760Stuexen    struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp,
776217760Stuexen    int holds_lock)
777217760Stuexen{
778217760Stuexen	if (holds_lock == 0) {
779217760Stuexen		SCTP_TCB_SEND_LOCK(stcb);
780217760Stuexen	}
781217760Stuexen	if (sp &&
782217760Stuexen	    ((sp->next.tqe_next != NULL) ||
783217760Stuexen	    (sp->next.tqe_prev != NULL))) {
784217760Stuexen		TAILQ_REMOVE(&asoc->ss_data.out_list, sp, next);
785217760Stuexen	}
786217760Stuexen	if (holds_lock == 0) {
787217760Stuexen		SCTP_TCB_SEND_UNLOCK(stcb);
788217760Stuexen	}
789217760Stuexen	return;
790217760Stuexen}
791217760Stuexen
792217760Stuexen
793217760Stuexenstatic struct sctp_stream_out *
794217760Stuexensctp_ss_fcfs_select(struct sctp_tcb *stcb, struct sctp_nets *net,
795217760Stuexen    struct sctp_association *asoc)
796217760Stuexen{
797217760Stuexen	struct sctp_stream_out *strq;
798217760Stuexen	struct sctp_stream_queue_pending *sp;
799217760Stuexen
800217760Stuexen	sp = TAILQ_FIRST(&asoc->ss_data.out_list);
801217760Stuexendefault_again:
802217760Stuexen	if (sp != NULL) {
803217760Stuexen		strq = &asoc->strmout[sp->stream];
804217760Stuexen	} else {
805217760Stuexen		strq = NULL;
806217760Stuexen	}
807217760Stuexen
808217760Stuexen	/*
809217760Stuexen	 * If CMT is off, we must validate that the stream in question has
810217760Stuexen	 * the first item pointed towards are network destionation requested
811217760Stuexen	 * by the caller. Note that if we turn out to be locked to a stream
812217760Stuexen	 * (assigning TSN's then we must stop, since we cannot look for
813217760Stuexen	 * another stream with data to send to that destination). In CMT's
814217760Stuexen	 * case, by skipping this check, we will send one data packet
815217760Stuexen	 * towards the requested net.
816217760Stuexen	 */
817217760Stuexen	if (net != NULL && strq != NULL &&
818217760Stuexen	    SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
819217760Stuexen		if (TAILQ_FIRST(&strq->outqueue) &&
820217760Stuexen		    TAILQ_FIRST(&strq->outqueue)->net != NULL &&
821217760Stuexen		    TAILQ_FIRST(&strq->outqueue)->net != net) {
822217760Stuexen			sp = TAILQ_NEXT(sp, next);
823217760Stuexen			goto default_again;
824217760Stuexen		}
825217760Stuexen	}
826217760Stuexen	return (strq);
827217760Stuexen}
828217760Stuexen
829217760Stuexenstruct sctp_ss_functions sctp_ss_functions[] = {
830217760Stuexen/* SCTP_SS_DEFAULT */
831217760Stuexen	{
832217760Stuexen		.sctp_ss_init = sctp_ss_default_init,
833217760Stuexen		.sctp_ss_clear = sctp_ss_default_clear,
834217760Stuexen		.sctp_ss_init_stream = sctp_ss_default_init_stream,
835217760Stuexen		.sctp_ss_add_to_stream = sctp_ss_default_add,
836217760Stuexen		.sctp_ss_is_empty = sctp_ss_default_is_empty,
837217760Stuexen		.sctp_ss_remove_from_stream = sctp_ss_default_remove,
838217760Stuexen		.sctp_ss_select_stream = sctp_ss_default_select,
839217760Stuexen		.sctp_ss_scheduled = sctp_ss_default_scheduled,
840217760Stuexen		.sctp_ss_packet_done = sctp_ss_default_packet_done,
841217760Stuexen		.sctp_ss_get_value = sctp_ss_default_get_value,
842217760Stuexen		.sctp_ss_set_value = sctp_ss_default_set_value
843217760Stuexen	},
844217760Stuexen/* SCTP_SS_ROUND_ROBIN */
845217760Stuexen	{
846217760Stuexen		.sctp_ss_init = sctp_ss_default_init,
847217760Stuexen		.sctp_ss_clear = sctp_ss_default_clear,
848217760Stuexen		.sctp_ss_init_stream = sctp_ss_default_init_stream,
849217760Stuexen		.sctp_ss_add_to_stream = sctp_ss_rr_add,
850217760Stuexen		.sctp_ss_is_empty = sctp_ss_default_is_empty,
851217760Stuexen		.sctp_ss_remove_from_stream = sctp_ss_default_remove,
852217760Stuexen		.sctp_ss_select_stream = sctp_ss_default_select,
853217760Stuexen		.sctp_ss_scheduled = sctp_ss_default_scheduled,
854217760Stuexen		.sctp_ss_packet_done = sctp_ss_default_packet_done,
855217760Stuexen		.sctp_ss_get_value = sctp_ss_default_get_value,
856217760Stuexen		.sctp_ss_set_value = sctp_ss_default_set_value
857217760Stuexen	},
858217760Stuexen/* SCTP_SS_ROUND_ROBIN_PACKET */
859217760Stuexen	{
860217760Stuexen		.sctp_ss_init = sctp_ss_default_init,
861217760Stuexen		.sctp_ss_clear = sctp_ss_default_clear,
862217760Stuexen		.sctp_ss_init_stream = sctp_ss_default_init_stream,
863217760Stuexen		.sctp_ss_add_to_stream = sctp_ss_rrp_add,
864217760Stuexen		.sctp_ss_is_empty = sctp_ss_default_is_empty,
865217760Stuexen		.sctp_ss_remove_from_stream = sctp_ss_default_remove,
866217760Stuexen		.sctp_ss_select_stream = sctp_ss_rrp_select,
867217760Stuexen		.sctp_ss_scheduled = sctp_ss_default_scheduled,
868217760Stuexen		.sctp_ss_packet_done = sctp_ss_rrp_packet_done,
869217760Stuexen		.sctp_ss_get_value = sctp_ss_default_get_value,
870217760Stuexen		.sctp_ss_set_value = sctp_ss_default_set_value
871217760Stuexen	},
872217760Stuexen/* SCTP_SS_PRIORITY */
873217760Stuexen	{
874217760Stuexen		.sctp_ss_init = sctp_ss_default_init,
875217760Stuexen		.sctp_ss_clear = sctp_ss_prio_clear,
876217760Stuexen		.sctp_ss_init_stream = sctp_ss_prio_init_stream,
877217760Stuexen		.sctp_ss_add_to_stream = sctp_ss_prio_add,
878217760Stuexen		.sctp_ss_is_empty = sctp_ss_default_is_empty,
879217760Stuexen		.sctp_ss_remove_from_stream = sctp_ss_prio_remove,
880217760Stuexen		.sctp_ss_select_stream = sctp_ss_prio_select,
881217760Stuexen		.sctp_ss_scheduled = sctp_ss_default_scheduled,
882217760Stuexen		.sctp_ss_packet_done = sctp_ss_default_packet_done,
883217760Stuexen		.sctp_ss_get_value = sctp_ss_prio_get_value,
884217760Stuexen		.sctp_ss_set_value = sctp_ss_prio_set_value
885217760Stuexen	},
886217760Stuexen/* SCTP_SS_FAIR_BANDWITH */
887217760Stuexen	{
888217760Stuexen		.sctp_ss_init = sctp_ss_default_init,
889217760Stuexen		.sctp_ss_clear = sctp_ss_fb_clear,
890217760Stuexen		.sctp_ss_init_stream = sctp_ss_fb_init_stream,
891217760Stuexen		.sctp_ss_add_to_stream = sctp_ss_fb_add,
892217760Stuexen		.sctp_ss_is_empty = sctp_ss_default_is_empty,
893217760Stuexen		.sctp_ss_remove_from_stream = sctp_ss_fb_remove,
894217760Stuexen		.sctp_ss_select_stream = sctp_ss_fb_select,
895217760Stuexen		.sctp_ss_scheduled = sctp_ss_fb_scheduled,
896217760Stuexen		.sctp_ss_packet_done = sctp_ss_default_packet_done,
897217760Stuexen		.sctp_ss_get_value = sctp_ss_default_get_value,
898217760Stuexen		.sctp_ss_set_value = sctp_ss_default_set_value
899217760Stuexen	},
900217760Stuexen/* SCTP_SS_FIRST_COME */
901217760Stuexen	{
902217760Stuexen		.sctp_ss_init = sctp_ss_fcfs_init,
903217760Stuexen		.sctp_ss_clear = sctp_ss_fcfs_clear,
904217760Stuexen		.sctp_ss_init_stream = sctp_ss_fcfs_init_stream,
905217760Stuexen		.sctp_ss_add_to_stream = sctp_ss_fcfs_add,
906217760Stuexen		.sctp_ss_is_empty = sctp_ss_fcfs_is_empty,
907217760Stuexen		.sctp_ss_remove_from_stream = sctp_ss_fcfs_remove,
908217760Stuexen		.sctp_ss_select_stream = sctp_ss_fcfs_select,
909217760Stuexen		.sctp_ss_scheduled = sctp_ss_default_scheduled,
910217760Stuexen		.sctp_ss_packet_done = sctp_ss_default_packet_done,
911217760Stuexen		.sctp_ss_get_value = sctp_ss_default_get_value,
912217760Stuexen		.sctp_ss_set_value = sctp_ss_default_set_value
913217760Stuexen	}
914217760Stuexen};
915