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