1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/sctp/src/netinet/sctp_ss_functions.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,999 @@ 1.4 +/*- 1.5 + * Copyright (c) 2010-2012, by Michael Tuexen. All rights reserved. 1.6 + * Copyright (c) 2010-2012, by Randall Stewart. All rights reserved. 1.7 + * Copyright (c) 2010-2012, by Robin Seggelmann. All rights reserved. 1.8 + * 1.9 + * Redistribution and use in source and binary forms, with or without 1.10 + * modification, are permitted provided that the following conditions are met: 1.11 + * 1.12 + * a) Redistributions of source code must retain the above copyright notice, 1.13 + * this list of conditions and the following disclaimer. 1.14 + * 1.15 + * b) Redistributions in binary form must reproduce the above copyright 1.16 + * notice, this list of conditions and the following disclaimer in 1.17 + * the documentation and/or other materials provided with the distribution. 1.18 + * 1.19 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.20 + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 1.21 + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1.22 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 1.23 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 1.24 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 1.25 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 1.26 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 1.27 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 1.28 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 1.29 + * THE POSSIBILITY OF SUCH DAMAGE. 1.30 + */ 1.31 + 1.32 +#ifdef __FreeBSD__ 1.33 +#include <sys/cdefs.h> 1.34 +__FBSDID("$FreeBSD: head/sys/netinet/sctp_ss_functions.c 235828 2012-05-23 11:26:28Z tuexen $"); 1.35 +#endif 1.36 + 1.37 +#include <netinet/sctp_pcb.h> 1.38 +#if defined(__Userspace__) 1.39 +#include <netinet/sctp_os_userspace.h> 1.40 +#endif 1.41 + 1.42 +/* 1.43 + * Default simple round-robin algorithm. 1.44 + * Just interates the streams in the order they appear. 1.45 + */ 1.46 + 1.47 +static void 1.48 +sctp_ss_default_add(struct sctp_tcb *, struct sctp_association *, 1.49 + struct sctp_stream_out *, 1.50 + struct sctp_stream_queue_pending *, int); 1.51 + 1.52 +static void 1.53 +sctp_ss_default_remove(struct sctp_tcb *, struct sctp_association *, 1.54 + struct sctp_stream_out *, 1.55 + struct sctp_stream_queue_pending *, int); 1.56 + 1.57 +static void 1.58 +sctp_ss_default_init(struct sctp_tcb *stcb, struct sctp_association *asoc, 1.59 + int holds_lock) 1.60 +{ 1.61 + uint16_t i; 1.62 + 1.63 + TAILQ_INIT(&asoc->ss_data.out_wheel); 1.64 + /* 1.65 + * If there is data in the stream queues already, 1.66 + * the scheduler of an existing association has 1.67 + * been changed. We need to add all stream queues 1.68 + * to the wheel. 1.69 + */ 1.70 + for (i = 0; i < stcb->asoc.streamoutcnt; i++) { 1.71 + stcb->asoc.ss_functions.sctp_ss_add_to_stream(stcb, &stcb->asoc, 1.72 + &stcb->asoc.strmout[i], 1.73 + NULL, holds_lock); 1.74 + } 1.75 + return; 1.76 +} 1.77 + 1.78 +static void 1.79 +sctp_ss_default_clear(struct sctp_tcb *stcb, struct sctp_association *asoc, 1.80 + int clear_values SCTP_UNUSED, int holds_lock) 1.81 +{ 1.82 + if (holds_lock == 0) { 1.83 + SCTP_TCB_SEND_LOCK(stcb); 1.84 + } 1.85 + while (!TAILQ_EMPTY(&asoc->ss_data.out_wheel)) { 1.86 + struct sctp_stream_out *strq = TAILQ_FIRST(&asoc->ss_data.out_wheel); 1.87 + TAILQ_REMOVE(&asoc->ss_data.out_wheel, TAILQ_FIRST(&asoc->ss_data.out_wheel), ss_params.rr.next_spoke); 1.88 + strq->ss_params.rr.next_spoke.tqe_next = NULL; 1.89 + strq->ss_params.rr.next_spoke.tqe_prev = NULL; 1.90 + } 1.91 + asoc->last_out_stream = NULL; 1.92 + if (holds_lock == 0) { 1.93 + SCTP_TCB_SEND_UNLOCK(stcb); 1.94 + } 1.95 + return; 1.96 +} 1.97 + 1.98 +static void 1.99 +sctp_ss_default_init_stream(struct sctp_stream_out *strq, struct sctp_stream_out *with_strq SCTP_UNUSED) 1.100 +{ 1.101 + strq->ss_params.rr.next_spoke.tqe_next = NULL; 1.102 + strq->ss_params.rr.next_spoke.tqe_prev = NULL; 1.103 + return; 1.104 +} 1.105 + 1.106 +static void 1.107 +sctp_ss_default_add(struct sctp_tcb *stcb, struct sctp_association *asoc, 1.108 + struct sctp_stream_out *strq, 1.109 + struct sctp_stream_queue_pending *sp SCTP_UNUSED, int holds_lock) 1.110 +{ 1.111 + if (holds_lock == 0) { 1.112 + SCTP_TCB_SEND_LOCK(stcb); 1.113 + } 1.114 + /* Add to wheel if not already on it and stream queue not empty */ 1.115 + if (!TAILQ_EMPTY(&strq->outqueue) && 1.116 + (strq->ss_params.rr.next_spoke.tqe_next == NULL) && 1.117 + (strq->ss_params.rr.next_spoke.tqe_prev == NULL)) { 1.118 + TAILQ_INSERT_TAIL(&asoc->ss_data.out_wheel, 1.119 + strq, ss_params.rr.next_spoke); 1.120 + } 1.121 + if (holds_lock == 0) { 1.122 + SCTP_TCB_SEND_UNLOCK(stcb); 1.123 + } 1.124 + return; 1.125 +} 1.126 + 1.127 +static int 1.128 +sctp_ss_default_is_empty(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc) 1.129 +{ 1.130 + if (TAILQ_EMPTY(&asoc->ss_data.out_wheel)) { 1.131 + return (1); 1.132 + } else { 1.133 + return (0); 1.134 + } 1.135 +} 1.136 + 1.137 +static void 1.138 +sctp_ss_default_remove(struct sctp_tcb *stcb, struct sctp_association *asoc, 1.139 + struct sctp_stream_out *strq, 1.140 + struct sctp_stream_queue_pending *sp SCTP_UNUSED, int holds_lock) 1.141 +{ 1.142 + if (holds_lock == 0) { 1.143 + SCTP_TCB_SEND_LOCK(stcb); 1.144 + } 1.145 + /* Remove from wheel if stream queue is empty and actually is on the wheel */ 1.146 + if (TAILQ_EMPTY(&strq->outqueue) && 1.147 + (strq->ss_params.rr.next_spoke.tqe_next != NULL || 1.148 + strq->ss_params.rr.next_spoke.tqe_prev != NULL)) { 1.149 + if (asoc->last_out_stream == strq) { 1.150 + asoc->last_out_stream = TAILQ_PREV(asoc->last_out_stream, 1.151 + sctpwheel_listhead, 1.152 + ss_params.rr.next_spoke); 1.153 + if (asoc->last_out_stream == NULL) { 1.154 + asoc->last_out_stream = TAILQ_LAST(&asoc->ss_data.out_wheel, 1.155 + sctpwheel_listhead); 1.156 + } 1.157 + if (asoc->last_out_stream == strq) { 1.158 + asoc->last_out_stream = NULL; 1.159 + } 1.160 + } 1.161 + TAILQ_REMOVE(&asoc->ss_data.out_wheel, strq, ss_params.rr.next_spoke); 1.162 + strq->ss_params.rr.next_spoke.tqe_next = NULL; 1.163 + strq->ss_params.rr.next_spoke.tqe_prev = NULL; 1.164 + } 1.165 + if (holds_lock == 0) { 1.166 + SCTP_TCB_SEND_UNLOCK(stcb); 1.167 + } 1.168 + return; 1.169 +} 1.170 + 1.171 + 1.172 +static struct sctp_stream_out * 1.173 +sctp_ss_default_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net, 1.174 + struct sctp_association *asoc) 1.175 +{ 1.176 + struct sctp_stream_out *strq, *strqt; 1.177 + 1.178 + strqt = asoc->last_out_stream; 1.179 +default_again: 1.180 + /* Find the next stream to use */ 1.181 + if (strqt == NULL) { 1.182 + strq = TAILQ_FIRST(&asoc->ss_data.out_wheel); 1.183 + } else { 1.184 + strq = TAILQ_NEXT(strqt, ss_params.rr.next_spoke); 1.185 + if (strq == NULL) { 1.186 + strq = TAILQ_FIRST(&asoc->ss_data.out_wheel); 1.187 + } 1.188 + } 1.189 + 1.190 + /* If CMT is off, we must validate that 1.191 + * the stream in question has the first 1.192 + * item pointed towards are network destination 1.193 + * requested by the caller. Note that if we 1.194 + * turn out to be locked to a stream (assigning 1.195 + * TSN's then we must stop, since we cannot 1.196 + * look for another stream with data to send 1.197 + * to that destination). In CMT's case, by 1.198 + * skipping this check, we will send one 1.199 + * data packet towards the requested net. 1.200 + */ 1.201 + if (net != NULL && strq != NULL && 1.202 + SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) { 1.203 + if (TAILQ_FIRST(&strq->outqueue) && 1.204 + TAILQ_FIRST(&strq->outqueue)->net != NULL && 1.205 + TAILQ_FIRST(&strq->outqueue)->net != net) { 1.206 + if (strq == asoc->last_out_stream) { 1.207 + return (NULL); 1.208 + } else { 1.209 + strqt = strq; 1.210 + goto default_again; 1.211 + } 1.212 + } 1.213 + } 1.214 + return (strq); 1.215 +} 1.216 + 1.217 +static void 1.218 +sctp_ss_default_scheduled(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net SCTP_UNUSED, 1.219 + struct sctp_association *asoc SCTP_UNUSED, 1.220 + struct sctp_stream_out *strq, int moved_how_much SCTP_UNUSED) 1.221 +{ 1.222 + asoc->last_out_stream = strq; 1.223 + return; 1.224 +} 1.225 + 1.226 +static void 1.227 +sctp_ss_default_packet_done(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net SCTP_UNUSED, 1.228 + struct sctp_association *asoc SCTP_UNUSED) 1.229 +{ 1.230 + /* Nothing to be done here */ 1.231 + return; 1.232 +} 1.233 + 1.234 +static int 1.235 +sctp_ss_default_get_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED, 1.236 + struct sctp_stream_out *strq SCTP_UNUSED, uint16_t *value SCTP_UNUSED) 1.237 +{ 1.238 + /* Nothing to be done here */ 1.239 + return (-1); 1.240 +} 1.241 + 1.242 +static int 1.243 +sctp_ss_default_set_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED, 1.244 + struct sctp_stream_out *strq SCTP_UNUSED, uint16_t value SCTP_UNUSED) 1.245 +{ 1.246 + /* Nothing to be done here */ 1.247 + return (-1); 1.248 +} 1.249 + 1.250 +/* 1.251 + * Real round-robin algorithm. 1.252 + * Always interates the streams in ascending order. 1.253 + */ 1.254 +static void 1.255 +sctp_ss_rr_add(struct sctp_tcb *stcb, struct sctp_association *asoc, 1.256 + struct sctp_stream_out *strq, 1.257 + struct sctp_stream_queue_pending *sp SCTP_UNUSED, int holds_lock) 1.258 +{ 1.259 + struct sctp_stream_out *strqt; 1.260 + 1.261 + if (holds_lock == 0) { 1.262 + SCTP_TCB_SEND_LOCK(stcb); 1.263 + } 1.264 + if (!TAILQ_EMPTY(&strq->outqueue) && 1.265 + (strq->ss_params.rr.next_spoke.tqe_next == NULL) && 1.266 + (strq->ss_params.rr.next_spoke.tqe_prev == NULL)) { 1.267 + if (TAILQ_EMPTY(&asoc->ss_data.out_wheel)) { 1.268 + TAILQ_INSERT_HEAD(&asoc->ss_data.out_wheel, strq, ss_params.rr.next_spoke); 1.269 + } else { 1.270 + strqt = TAILQ_FIRST(&asoc->ss_data.out_wheel); 1.271 + while (strqt != NULL && (strqt->stream_no < strq->stream_no)) { 1.272 + strqt = TAILQ_NEXT(strqt, ss_params.rr.next_spoke); 1.273 + } 1.274 + if (strqt != NULL) { 1.275 + TAILQ_INSERT_BEFORE(strqt, strq, ss_params.rr.next_spoke); 1.276 + } else { 1.277 + TAILQ_INSERT_TAIL(&asoc->ss_data.out_wheel, strq, ss_params.rr.next_spoke); 1.278 + } 1.279 + } 1.280 + } 1.281 + if (holds_lock == 0) { 1.282 + SCTP_TCB_SEND_UNLOCK(stcb); 1.283 + } 1.284 + return; 1.285 +} 1.286 + 1.287 +/* 1.288 + * Real round-robin per packet algorithm. 1.289 + * Always interates the streams in ascending order and 1.290 + * only fills messages of the same stream in a packet. 1.291 + */ 1.292 +static struct sctp_stream_out * 1.293 +sctp_ss_rrp_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net SCTP_UNUSED, 1.294 + struct sctp_association *asoc) 1.295 +{ 1.296 + return (asoc->last_out_stream); 1.297 +} 1.298 + 1.299 +static void 1.300 +sctp_ss_rrp_packet_done(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net, 1.301 + struct sctp_association *asoc) 1.302 +{ 1.303 + struct sctp_stream_out *strq, *strqt; 1.304 + 1.305 + strqt = asoc->last_out_stream; 1.306 +rrp_again: 1.307 + /* Find the next stream to use */ 1.308 + if (strqt == NULL) { 1.309 + strq = TAILQ_FIRST(&asoc->ss_data.out_wheel); 1.310 + } else { 1.311 + strq = TAILQ_NEXT(strqt, ss_params.rr.next_spoke); 1.312 + if (strq == NULL) { 1.313 + strq = TAILQ_FIRST(&asoc->ss_data.out_wheel); 1.314 + } 1.315 + } 1.316 + 1.317 + /* If CMT is off, we must validate that 1.318 + * the stream in question has the first 1.319 + * item pointed towards are network destination 1.320 + * requested by the caller. Note that if we 1.321 + * turn out to be locked to a stream (assigning 1.322 + * TSN's then we must stop, since we cannot 1.323 + * look for another stream with data to send 1.324 + * to that destination). In CMT's case, by 1.325 + * skipping this check, we will send one 1.326 + * data packet towards the requested net. 1.327 + */ 1.328 + if (net != NULL && strq != NULL && 1.329 + SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) { 1.330 + if (TAILQ_FIRST(&strq->outqueue) && 1.331 + TAILQ_FIRST(&strq->outqueue)->net != NULL && 1.332 + TAILQ_FIRST(&strq->outqueue)->net != net) { 1.333 + if (strq == asoc->last_out_stream) { 1.334 + strq = NULL; 1.335 + } else { 1.336 + strqt = strq; 1.337 + goto rrp_again; 1.338 + } 1.339 + } 1.340 + } 1.341 + asoc->last_out_stream = strq; 1.342 + return; 1.343 +} 1.344 + 1.345 + 1.346 +/* 1.347 + * Priority algorithm. 1.348 + * Always prefers streams based on their priority id. 1.349 + */ 1.350 +static void 1.351 +sctp_ss_prio_clear(struct sctp_tcb *stcb, struct sctp_association *asoc, 1.352 + int clear_values, int holds_lock) 1.353 +{ 1.354 + if (holds_lock == 0) { 1.355 + SCTP_TCB_SEND_LOCK(stcb); 1.356 + } 1.357 + while (!TAILQ_EMPTY(&asoc->ss_data.out_wheel)) { 1.358 + struct sctp_stream_out *strq = TAILQ_FIRST(&asoc->ss_data.out_wheel); 1.359 + if (clear_values) { 1.360 + strq->ss_params.prio.priority = 0; 1.361 + } 1.362 + TAILQ_REMOVE(&asoc->ss_data.out_wheel, TAILQ_FIRST(&asoc->ss_data.out_wheel), ss_params.prio.next_spoke); 1.363 + strq->ss_params.prio.next_spoke.tqe_next = NULL; 1.364 + strq->ss_params.prio.next_spoke.tqe_prev = NULL; 1.365 + 1.366 + } 1.367 + asoc->last_out_stream = NULL; 1.368 + if (holds_lock == 0) { 1.369 + SCTP_TCB_SEND_UNLOCK(stcb); 1.370 + } 1.371 + return; 1.372 +} 1.373 + 1.374 +static void 1.375 +sctp_ss_prio_init_stream(struct sctp_stream_out *strq, struct sctp_stream_out *with_strq) 1.376 +{ 1.377 + strq->ss_params.prio.next_spoke.tqe_next = NULL; 1.378 + strq->ss_params.prio.next_spoke.tqe_prev = NULL; 1.379 + if (with_strq != NULL) { 1.380 + strq->ss_params.prio.priority = with_strq->ss_params.prio.priority; 1.381 + } else { 1.382 + strq->ss_params.prio.priority = 0; 1.383 + } 1.384 + return; 1.385 +} 1.386 + 1.387 +static void 1.388 +sctp_ss_prio_add(struct sctp_tcb *stcb, struct sctp_association *asoc, 1.389 + struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED, 1.390 + int holds_lock) 1.391 +{ 1.392 + struct sctp_stream_out *strqt; 1.393 + 1.394 + if (holds_lock == 0) { 1.395 + SCTP_TCB_SEND_LOCK(stcb); 1.396 + } 1.397 + /* Add to wheel if not already on it and stream queue not empty */ 1.398 + if (!TAILQ_EMPTY(&strq->outqueue) && 1.399 + (strq->ss_params.prio.next_spoke.tqe_next == NULL) && 1.400 + (strq->ss_params.prio.next_spoke.tqe_prev == NULL)) { 1.401 + if (TAILQ_EMPTY(&asoc->ss_data.out_wheel)) { 1.402 + TAILQ_INSERT_HEAD(&asoc->ss_data.out_wheel, strq, ss_params.prio.next_spoke); 1.403 + } else { 1.404 + strqt = TAILQ_FIRST(&asoc->ss_data.out_wheel); 1.405 + while (strqt != NULL && strqt->ss_params.prio.priority < strq->ss_params.prio.priority) { 1.406 + strqt = TAILQ_NEXT(strqt, ss_params.prio.next_spoke); 1.407 + } 1.408 + if (strqt != NULL) { 1.409 + TAILQ_INSERT_BEFORE(strqt, strq, ss_params.prio.next_spoke); 1.410 + } else { 1.411 + TAILQ_INSERT_TAIL(&asoc->ss_data.out_wheel, strq, ss_params.prio.next_spoke); 1.412 + } 1.413 + } 1.414 + } 1.415 + if (holds_lock == 0) { 1.416 + SCTP_TCB_SEND_UNLOCK(stcb); 1.417 + } 1.418 + return; 1.419 +} 1.420 + 1.421 +static void 1.422 +sctp_ss_prio_remove(struct sctp_tcb *stcb, struct sctp_association *asoc, 1.423 + struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED, 1.424 + int holds_lock) 1.425 +{ 1.426 + if (holds_lock == 0) { 1.427 + SCTP_TCB_SEND_LOCK(stcb); 1.428 + } 1.429 + /* Remove from wheel if stream queue is empty and actually is on the wheel */ 1.430 + if (TAILQ_EMPTY(&strq->outqueue) && 1.431 + (strq->ss_params.prio.next_spoke.tqe_next != NULL || 1.432 + strq->ss_params.prio.next_spoke.tqe_prev != NULL)) { 1.433 + if (asoc->last_out_stream == strq) { 1.434 + asoc->last_out_stream = TAILQ_PREV(asoc->last_out_stream, sctpwheel_listhead, 1.435 + ss_params.prio.next_spoke); 1.436 + if (asoc->last_out_stream == NULL) { 1.437 + asoc->last_out_stream = TAILQ_LAST(&asoc->ss_data.out_wheel, 1.438 + sctpwheel_listhead); 1.439 + } 1.440 + if (asoc->last_out_stream == strq) { 1.441 + asoc->last_out_stream = NULL; 1.442 + } 1.443 + } 1.444 + TAILQ_REMOVE(&asoc->ss_data.out_wheel, strq, ss_params.prio.next_spoke); 1.445 + strq->ss_params.prio.next_spoke.tqe_next = NULL; 1.446 + strq->ss_params.prio.next_spoke.tqe_prev = NULL; 1.447 + } 1.448 + if (holds_lock == 0) { 1.449 + SCTP_TCB_SEND_UNLOCK(stcb); 1.450 + } 1.451 + return; 1.452 +} 1.453 + 1.454 +static struct sctp_stream_out* 1.455 +sctp_ss_prio_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net, 1.456 + struct sctp_association *asoc) 1.457 +{ 1.458 + struct sctp_stream_out *strq, *strqt, *strqn; 1.459 + 1.460 + strqt = asoc->last_out_stream; 1.461 +prio_again: 1.462 + /* Find the next stream to use */ 1.463 + if (strqt == NULL) { 1.464 + strq = TAILQ_FIRST(&asoc->ss_data.out_wheel); 1.465 + } else { 1.466 + strqn = TAILQ_NEXT(strqt, ss_params.prio.next_spoke); 1.467 + if (strqn != NULL && 1.468 + strqn->ss_params.prio.priority == strqt->ss_params.prio.priority) { 1.469 + strq = strqn; 1.470 + } else { 1.471 + strq = TAILQ_FIRST(&asoc->ss_data.out_wheel); 1.472 + } 1.473 + } 1.474 + 1.475 + /* If CMT is off, we must validate that 1.476 + * the stream in question has the first 1.477 + * item pointed towards are network destination 1.478 + * requested by the caller. Note that if we 1.479 + * turn out to be locked to a stream (assigning 1.480 + * TSN's then we must stop, since we cannot 1.481 + * look for another stream with data to send 1.482 + * to that destination). In CMT's case, by 1.483 + * skipping this check, we will send one 1.484 + * data packet towards the requested net. 1.485 + */ 1.486 + if (net != NULL && strq != NULL && 1.487 + SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) { 1.488 + if (TAILQ_FIRST(&strq->outqueue) && 1.489 + TAILQ_FIRST(&strq->outqueue)->net != NULL && 1.490 + TAILQ_FIRST(&strq->outqueue)->net != net) { 1.491 + if (strq == asoc->last_out_stream) { 1.492 + return (NULL); 1.493 + } else { 1.494 + strqt = strq; 1.495 + goto prio_again; 1.496 + } 1.497 + } 1.498 + } 1.499 + return (strq); 1.500 +} 1.501 + 1.502 +static int 1.503 +sctp_ss_prio_get_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED, 1.504 + struct sctp_stream_out *strq, uint16_t *value) 1.505 +{ 1.506 + if (strq == NULL) { 1.507 + return (-1); 1.508 + } 1.509 + *value = strq->ss_params.prio.priority; 1.510 + return (1); 1.511 +} 1.512 + 1.513 +static int 1.514 +sctp_ss_prio_set_value(struct sctp_tcb *stcb, struct sctp_association *asoc, 1.515 + struct sctp_stream_out *strq, uint16_t value) 1.516 +{ 1.517 + if (strq == NULL) { 1.518 + return (-1); 1.519 + } 1.520 + strq->ss_params.prio.priority = value; 1.521 + sctp_ss_prio_remove(stcb, asoc, strq, NULL, 1); 1.522 + sctp_ss_prio_add(stcb, asoc, strq, NULL, 1); 1.523 + return (1); 1.524 +} 1.525 + 1.526 +/* 1.527 + * Fair bandwidth algorithm. 1.528 + * Maintains an equal troughput per stream. 1.529 + */ 1.530 +static void 1.531 +sctp_ss_fb_clear(struct sctp_tcb *stcb, struct sctp_association *asoc, 1.532 + int clear_values, int holds_lock) 1.533 +{ 1.534 + if (holds_lock == 0) { 1.535 + SCTP_TCB_SEND_LOCK(stcb); 1.536 + } 1.537 + while (!TAILQ_EMPTY(&asoc->ss_data.out_wheel)) { 1.538 + struct sctp_stream_out *strq = TAILQ_FIRST(&asoc->ss_data.out_wheel); 1.539 + if (clear_values) { 1.540 + strq->ss_params.fb.rounds = -1; 1.541 + } 1.542 + TAILQ_REMOVE(&asoc->ss_data.out_wheel, TAILQ_FIRST(&asoc->ss_data.out_wheel), ss_params.fb.next_spoke); 1.543 + strq->ss_params.fb.next_spoke.tqe_next = NULL; 1.544 + strq->ss_params.fb.next_spoke.tqe_prev = NULL; 1.545 + } 1.546 + asoc->last_out_stream = NULL; 1.547 + if (holds_lock == 0) { 1.548 + SCTP_TCB_SEND_UNLOCK(stcb); 1.549 + } 1.550 + return; 1.551 +} 1.552 + 1.553 +static void 1.554 +sctp_ss_fb_init_stream(struct sctp_stream_out *strq, struct sctp_stream_out *with_strq) 1.555 +{ 1.556 + strq->ss_params.fb.next_spoke.tqe_next = NULL; 1.557 + strq->ss_params.fb.next_spoke.tqe_prev = NULL; 1.558 + if (with_strq != NULL) { 1.559 + strq->ss_params.fb.rounds = with_strq->ss_params.fb.rounds; 1.560 + } else { 1.561 + strq->ss_params.fb.rounds = -1; 1.562 + } 1.563 + return; 1.564 +} 1.565 + 1.566 +static void 1.567 +sctp_ss_fb_add(struct sctp_tcb *stcb, struct sctp_association *asoc, 1.568 + struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED, 1.569 + int holds_lock) 1.570 +{ 1.571 + if (holds_lock == 0) { 1.572 + SCTP_TCB_SEND_LOCK(stcb); 1.573 + } 1.574 + if (!TAILQ_EMPTY(&strq->outqueue) && 1.575 + (strq->ss_params.fb.next_spoke.tqe_next == NULL) && 1.576 + (strq->ss_params.fb.next_spoke.tqe_prev == NULL)) { 1.577 + if (strq->ss_params.fb.rounds < 0) 1.578 + strq->ss_params.fb.rounds = TAILQ_FIRST(&strq->outqueue)->length; 1.579 + TAILQ_INSERT_TAIL(&asoc->ss_data.out_wheel, strq, ss_params.fb.next_spoke); 1.580 + } 1.581 + if (holds_lock == 0) { 1.582 + SCTP_TCB_SEND_UNLOCK(stcb); 1.583 + } 1.584 + return; 1.585 +} 1.586 + 1.587 +static void 1.588 +sctp_ss_fb_remove(struct sctp_tcb *stcb, struct sctp_association *asoc, 1.589 + struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED, 1.590 + int holds_lock) 1.591 +{ 1.592 + if (holds_lock == 0) { 1.593 + SCTP_TCB_SEND_LOCK(stcb); 1.594 + } 1.595 + /* Remove from wheel if stream queue is empty and actually is on the wheel */ 1.596 + if (TAILQ_EMPTY(&strq->outqueue) && 1.597 + (strq->ss_params.fb.next_spoke.tqe_next != NULL || 1.598 + strq->ss_params.fb.next_spoke.tqe_prev != NULL)) { 1.599 + if (asoc->last_out_stream == strq) { 1.600 + asoc->last_out_stream = TAILQ_PREV(asoc->last_out_stream, sctpwheel_listhead, 1.601 + ss_params.fb.next_spoke); 1.602 + if (asoc->last_out_stream == NULL) { 1.603 + asoc->last_out_stream = TAILQ_LAST(&asoc->ss_data.out_wheel, 1.604 + sctpwheel_listhead); 1.605 + } 1.606 + if (asoc->last_out_stream == strq) { 1.607 + asoc->last_out_stream = NULL; 1.608 + } 1.609 + } 1.610 + TAILQ_REMOVE(&asoc->ss_data.out_wheel, strq, ss_params.fb.next_spoke); 1.611 + strq->ss_params.fb.next_spoke.tqe_next = NULL; 1.612 + strq->ss_params.fb.next_spoke.tqe_prev = NULL; 1.613 + } 1.614 + if (holds_lock == 0) { 1.615 + SCTP_TCB_SEND_UNLOCK(stcb); 1.616 + } 1.617 + return; 1.618 +} 1.619 + 1.620 +static struct sctp_stream_out* 1.621 +sctp_ss_fb_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net, 1.622 + struct sctp_association *asoc) 1.623 +{ 1.624 + struct sctp_stream_out *strq = NULL, *strqt; 1.625 + 1.626 + if (asoc->last_out_stream == NULL || 1.627 + TAILQ_FIRST(&asoc->ss_data.out_wheel) == TAILQ_LAST(&asoc->ss_data.out_wheel, sctpwheel_listhead)) { 1.628 + strqt = TAILQ_FIRST(&asoc->ss_data.out_wheel); 1.629 + } else { 1.630 + strqt = TAILQ_NEXT(asoc->last_out_stream, ss_params.fb.next_spoke); 1.631 + } 1.632 + do { 1.633 + if ((strqt != NULL) && 1.634 + ((SCTP_BASE_SYSCTL(sctp_cmt_on_off) > 0) || 1.635 + (SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0 && 1.636 + (net == NULL || (TAILQ_FIRST(&strqt->outqueue) && TAILQ_FIRST(&strqt->outqueue)->net == NULL) || 1.637 + (net != NULL && TAILQ_FIRST(&strqt->outqueue) && TAILQ_FIRST(&strqt->outqueue)->net != NULL && 1.638 + TAILQ_FIRST(&strqt->outqueue)->net == net))))) { 1.639 + if ((strqt->ss_params.fb.rounds >= 0) && (strq == NULL || 1.640 + strqt->ss_params.fb.rounds < strq->ss_params.fb.rounds)) { 1.641 + strq = strqt; 1.642 + } 1.643 + } 1.644 + if (strqt != NULL) { 1.645 + strqt = TAILQ_NEXT(strqt, ss_params.fb.next_spoke); 1.646 + } else { 1.647 + strqt = TAILQ_FIRST(&asoc->ss_data.out_wheel); 1.648 + } 1.649 + } while (strqt != strq); 1.650 + return (strq); 1.651 +} 1.652 + 1.653 +static void 1.654 +sctp_ss_fb_scheduled(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net SCTP_UNUSED, 1.655 + struct sctp_association *asoc, struct sctp_stream_out *strq, 1.656 + int moved_how_much SCTP_UNUSED) 1.657 +{ 1.658 + struct sctp_stream_out *strqt; 1.659 + int subtract; 1.660 + 1.661 + subtract = strq->ss_params.fb.rounds; 1.662 + TAILQ_FOREACH(strqt, &asoc->ss_data.out_wheel, ss_params.fb.next_spoke) { 1.663 + strqt->ss_params.fb.rounds -= subtract; 1.664 + if (strqt->ss_params.fb.rounds < 0) 1.665 + strqt->ss_params.fb.rounds = 0; 1.666 + } 1.667 + if (TAILQ_FIRST(&strq->outqueue)) { 1.668 + strq->ss_params.fb.rounds = TAILQ_FIRST(&strq->outqueue)->length; 1.669 + } else { 1.670 + strq->ss_params.fb.rounds = -1; 1.671 + } 1.672 + asoc->last_out_stream = strq; 1.673 + return; 1.674 +} 1.675 + 1.676 +/* 1.677 + * First-come, first-serve algorithm. 1.678 + * Maintains the order provided by the application. 1.679 + */ 1.680 +static void 1.681 +sctp_ss_fcfs_add(struct sctp_tcb *stcb, struct sctp_association *asoc, 1.682 + struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp, 1.683 + int holds_lock); 1.684 + 1.685 +static void 1.686 +sctp_ss_fcfs_init(struct sctp_tcb *stcb, struct sctp_association *asoc, 1.687 + int holds_lock) 1.688 +{ 1.689 + uint32_t x, n = 0, add_more = 1; 1.690 + struct sctp_stream_queue_pending *sp; 1.691 + uint16_t i; 1.692 + 1.693 + TAILQ_INIT(&asoc->ss_data.out_list); 1.694 + /* 1.695 + * If there is data in the stream queues already, 1.696 + * the scheduler of an existing association has 1.697 + * been changed. We can only cycle through the 1.698 + * stream queues and add everything to the FCFS 1.699 + * queue. 1.700 + */ 1.701 + while (add_more) { 1.702 + add_more = 0; 1.703 + for (i = 0; i < stcb->asoc.streamoutcnt; i++) { 1.704 + sp = TAILQ_FIRST(&stcb->asoc.strmout[i].outqueue); 1.705 + x = 0; 1.706 + /* Find n. message in current stream queue */ 1.707 + while (sp != NULL && x < n) { 1.708 + sp = TAILQ_NEXT(sp, next); 1.709 + x++; 1.710 + } 1.711 + if (sp != NULL) { 1.712 + sctp_ss_fcfs_add(stcb, &stcb->asoc, &stcb->asoc.strmout[i], sp, holds_lock); 1.713 + add_more = 1; 1.714 + } 1.715 + } 1.716 + n++; 1.717 + } 1.718 + return; 1.719 +} 1.720 + 1.721 +static void 1.722 +sctp_ss_fcfs_clear(struct sctp_tcb *stcb, struct sctp_association *asoc, 1.723 + int clear_values, int holds_lock) 1.724 +{ 1.725 + if (clear_values) { 1.726 + if (holds_lock == 0) { 1.727 + SCTP_TCB_SEND_LOCK(stcb); 1.728 + } 1.729 + while (!TAILQ_EMPTY(&asoc->ss_data.out_list)) { 1.730 + TAILQ_REMOVE(&asoc->ss_data.out_list, TAILQ_FIRST(&asoc->ss_data.out_list), ss_next); 1.731 + } 1.732 + if (holds_lock == 0) { 1.733 + SCTP_TCB_SEND_UNLOCK(stcb); 1.734 + } 1.735 + } 1.736 + return; 1.737 +} 1.738 + 1.739 +static void 1.740 +sctp_ss_fcfs_init_stream(struct sctp_stream_out *strq SCTP_UNUSED, struct sctp_stream_out *with_strq SCTP_UNUSED) 1.741 +{ 1.742 + /* Nothing to be done here */ 1.743 + return; 1.744 +} 1.745 + 1.746 +static void 1.747 +sctp_ss_fcfs_add(struct sctp_tcb *stcb, struct sctp_association *asoc, 1.748 + struct sctp_stream_out *strq SCTP_UNUSED, struct sctp_stream_queue_pending *sp, 1.749 + int holds_lock) 1.750 +{ 1.751 + if (holds_lock == 0) { 1.752 + SCTP_TCB_SEND_LOCK(stcb); 1.753 + } 1.754 + if (sp && (sp->ss_next.tqe_next == NULL) && 1.755 + (sp->ss_next.tqe_prev == NULL)) { 1.756 + TAILQ_INSERT_TAIL(&asoc->ss_data.out_list, sp, ss_next); 1.757 + } 1.758 + if (holds_lock == 0) { 1.759 + SCTP_TCB_SEND_UNLOCK(stcb); 1.760 + } 1.761 + return; 1.762 +} 1.763 + 1.764 +static int 1.765 +sctp_ss_fcfs_is_empty(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc) 1.766 +{ 1.767 + if (TAILQ_EMPTY(&asoc->ss_data.out_list)) { 1.768 + return (1); 1.769 + } else { 1.770 + return (0); 1.771 + } 1.772 +} 1.773 + 1.774 +static void 1.775 +sctp_ss_fcfs_remove(struct sctp_tcb *stcb, struct sctp_association *asoc, 1.776 + struct sctp_stream_out *strq SCTP_UNUSED, struct sctp_stream_queue_pending *sp, 1.777 + int holds_lock) 1.778 +{ 1.779 + if (holds_lock == 0) { 1.780 + SCTP_TCB_SEND_LOCK(stcb); 1.781 + } 1.782 + if (sp && 1.783 + ((sp->ss_next.tqe_next != NULL) || 1.784 + (sp->ss_next.tqe_prev != NULL))) { 1.785 + TAILQ_REMOVE(&asoc->ss_data.out_list, sp, ss_next); 1.786 + } 1.787 + if (holds_lock == 0) { 1.788 + SCTP_TCB_SEND_UNLOCK(stcb); 1.789 + } 1.790 + return; 1.791 +} 1.792 + 1.793 + 1.794 +static struct sctp_stream_out * 1.795 +sctp_ss_fcfs_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net, 1.796 + struct sctp_association *asoc) 1.797 +{ 1.798 + struct sctp_stream_out *strq; 1.799 + struct sctp_stream_queue_pending *sp; 1.800 + 1.801 + sp = TAILQ_FIRST(&asoc->ss_data.out_list); 1.802 +default_again: 1.803 + if (sp != NULL) { 1.804 + strq = &asoc->strmout[sp->stream]; 1.805 + } else { 1.806 + strq = NULL; 1.807 + } 1.808 + 1.809 + /* 1.810 + * If CMT is off, we must validate that 1.811 + * the stream in question has the first 1.812 + * item pointed towards are network destination 1.813 + * requested by the caller. Note that if we 1.814 + * turn out to be locked to a stream (assigning 1.815 + * TSN's then we must stop, since we cannot 1.816 + * look for another stream with data to send 1.817 + * to that destination). In CMT's case, by 1.818 + * skipping this check, we will send one 1.819 + * data packet towards the requested net. 1.820 + */ 1.821 + if (net != NULL && strq != NULL && 1.822 + SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) { 1.823 + if (TAILQ_FIRST(&strq->outqueue) && 1.824 + TAILQ_FIRST(&strq->outqueue)->net != NULL && 1.825 + TAILQ_FIRST(&strq->outqueue)->net != net) { 1.826 + sp = TAILQ_NEXT(sp, ss_next); 1.827 + goto default_again; 1.828 + } 1.829 + } 1.830 + return (strq); 1.831 +} 1.832 + 1.833 +struct sctp_ss_functions sctp_ss_functions[] = { 1.834 +/* SCTP_SS_DEFAULT */ 1.835 +{ 1.836 +#if defined(__Windows__) || defined(__Userspace_os_Windows) 1.837 + sctp_ss_default_init, 1.838 + sctp_ss_default_clear, 1.839 + sctp_ss_default_init_stream, 1.840 + sctp_ss_default_add, 1.841 + sctp_ss_default_is_empty, 1.842 + sctp_ss_default_remove, 1.843 + sctp_ss_default_select, 1.844 + sctp_ss_default_scheduled, 1.845 + sctp_ss_default_packet_done, 1.846 + sctp_ss_default_get_value, 1.847 + sctp_ss_default_set_value 1.848 +#else 1.849 + .sctp_ss_init = sctp_ss_default_init, 1.850 + .sctp_ss_clear = sctp_ss_default_clear, 1.851 + .sctp_ss_init_stream = sctp_ss_default_init_stream, 1.852 + .sctp_ss_add_to_stream = sctp_ss_default_add, 1.853 + .sctp_ss_is_empty = sctp_ss_default_is_empty, 1.854 + .sctp_ss_remove_from_stream = sctp_ss_default_remove, 1.855 + .sctp_ss_select_stream = sctp_ss_default_select, 1.856 + .sctp_ss_scheduled = sctp_ss_default_scheduled, 1.857 + .sctp_ss_packet_done = sctp_ss_default_packet_done, 1.858 + .sctp_ss_get_value = sctp_ss_default_get_value, 1.859 + .sctp_ss_set_value = sctp_ss_default_set_value 1.860 +#endif 1.861 +}, 1.862 +/* SCTP_SS_ROUND_ROBIN */ 1.863 +{ 1.864 +#if defined(__Windows__) || defined(__Userspace_os_Windows) 1.865 + sctp_ss_default_init, 1.866 + sctp_ss_default_clear, 1.867 + sctp_ss_default_init_stream, 1.868 + sctp_ss_rr_add, 1.869 + sctp_ss_default_is_empty, 1.870 + sctp_ss_default_remove, 1.871 + sctp_ss_default_select, 1.872 + sctp_ss_default_scheduled, 1.873 + sctp_ss_default_packet_done, 1.874 + sctp_ss_default_get_value, 1.875 + sctp_ss_default_set_value 1.876 +#else 1.877 + .sctp_ss_init = sctp_ss_default_init, 1.878 + .sctp_ss_clear = sctp_ss_default_clear, 1.879 + .sctp_ss_init_stream = sctp_ss_default_init_stream, 1.880 + .sctp_ss_add_to_stream = sctp_ss_rr_add, 1.881 + .sctp_ss_is_empty = sctp_ss_default_is_empty, 1.882 + .sctp_ss_remove_from_stream = sctp_ss_default_remove, 1.883 + .sctp_ss_select_stream = sctp_ss_default_select, 1.884 + .sctp_ss_scheduled = sctp_ss_default_scheduled, 1.885 + .sctp_ss_packet_done = sctp_ss_default_packet_done, 1.886 + .sctp_ss_get_value = sctp_ss_default_get_value, 1.887 + .sctp_ss_set_value = sctp_ss_default_set_value 1.888 +#endif 1.889 +}, 1.890 +/* SCTP_SS_ROUND_ROBIN_PACKET */ 1.891 +{ 1.892 +#if defined(__Windows__) || defined(__Userspace_os_Windows) 1.893 + sctp_ss_default_init, 1.894 + sctp_ss_default_clear, 1.895 + sctp_ss_default_init_stream, 1.896 + sctp_ss_rr_add, 1.897 + sctp_ss_default_is_empty, 1.898 + sctp_ss_default_remove, 1.899 + sctp_ss_rrp_select, 1.900 + sctp_ss_default_scheduled, 1.901 + sctp_ss_rrp_packet_done, 1.902 + sctp_ss_default_get_value, 1.903 + sctp_ss_default_set_value 1.904 +#else 1.905 + .sctp_ss_init = sctp_ss_default_init, 1.906 + .sctp_ss_clear = sctp_ss_default_clear, 1.907 + .sctp_ss_init_stream = sctp_ss_default_init_stream, 1.908 + .sctp_ss_add_to_stream = sctp_ss_rr_add, 1.909 + .sctp_ss_is_empty = sctp_ss_default_is_empty, 1.910 + .sctp_ss_remove_from_stream = sctp_ss_default_remove, 1.911 + .sctp_ss_select_stream = sctp_ss_rrp_select, 1.912 + .sctp_ss_scheduled = sctp_ss_default_scheduled, 1.913 + .sctp_ss_packet_done = sctp_ss_rrp_packet_done, 1.914 + .sctp_ss_get_value = sctp_ss_default_get_value, 1.915 + .sctp_ss_set_value = sctp_ss_default_set_value 1.916 +#endif 1.917 +}, 1.918 +/* SCTP_SS_PRIORITY */ 1.919 +{ 1.920 +#if defined(__Windows__) || defined(__Userspace_os_Windows) 1.921 + sctp_ss_default_init, 1.922 + sctp_ss_prio_clear, 1.923 + sctp_ss_prio_init_stream, 1.924 + sctp_ss_prio_add, 1.925 + sctp_ss_default_is_empty, 1.926 + sctp_ss_prio_remove, 1.927 + sctp_ss_prio_select, 1.928 + sctp_ss_default_scheduled, 1.929 + sctp_ss_default_packet_done, 1.930 + sctp_ss_prio_get_value, 1.931 + sctp_ss_prio_set_value 1.932 +#else 1.933 + .sctp_ss_init = sctp_ss_default_init, 1.934 + .sctp_ss_clear = sctp_ss_prio_clear, 1.935 + .sctp_ss_init_stream = sctp_ss_prio_init_stream, 1.936 + .sctp_ss_add_to_stream = sctp_ss_prio_add, 1.937 + .sctp_ss_is_empty = sctp_ss_default_is_empty, 1.938 + .sctp_ss_remove_from_stream = sctp_ss_prio_remove, 1.939 + .sctp_ss_select_stream = sctp_ss_prio_select, 1.940 + .sctp_ss_scheduled = sctp_ss_default_scheduled, 1.941 + .sctp_ss_packet_done = sctp_ss_default_packet_done, 1.942 + .sctp_ss_get_value = sctp_ss_prio_get_value, 1.943 + .sctp_ss_set_value = sctp_ss_prio_set_value 1.944 +#endif 1.945 +}, 1.946 +/* SCTP_SS_FAIR_BANDWITH */ 1.947 +{ 1.948 +#if defined(__Windows__) || defined(__Userspace_os_Windows) 1.949 + sctp_ss_default_init, 1.950 + sctp_ss_fb_clear, 1.951 + sctp_ss_fb_init_stream, 1.952 + sctp_ss_fb_add, 1.953 + sctp_ss_default_is_empty, 1.954 + sctp_ss_fb_remove, 1.955 + sctp_ss_fb_select, 1.956 + sctp_ss_fb_scheduled, 1.957 + sctp_ss_default_packet_done, 1.958 + sctp_ss_default_get_value, 1.959 + sctp_ss_default_set_value 1.960 +#else 1.961 + .sctp_ss_init = sctp_ss_default_init, 1.962 + .sctp_ss_clear = sctp_ss_fb_clear, 1.963 + .sctp_ss_init_stream = sctp_ss_fb_init_stream, 1.964 + .sctp_ss_add_to_stream = sctp_ss_fb_add, 1.965 + .sctp_ss_is_empty = sctp_ss_default_is_empty, 1.966 + .sctp_ss_remove_from_stream = sctp_ss_fb_remove, 1.967 + .sctp_ss_select_stream = sctp_ss_fb_select, 1.968 + .sctp_ss_scheduled = sctp_ss_fb_scheduled, 1.969 + .sctp_ss_packet_done = sctp_ss_default_packet_done, 1.970 + .sctp_ss_get_value = sctp_ss_default_get_value, 1.971 + .sctp_ss_set_value = sctp_ss_default_set_value 1.972 +#endif 1.973 +}, 1.974 +/* SCTP_SS_FIRST_COME */ 1.975 +{ 1.976 +#if defined(__Windows__) || defined(__Userspace_os_Windows) 1.977 + sctp_ss_fcfs_init, 1.978 + sctp_ss_fcfs_clear, 1.979 + sctp_ss_fcfs_init_stream, 1.980 + sctp_ss_fcfs_add, 1.981 + sctp_ss_fcfs_is_empty, 1.982 + sctp_ss_fcfs_remove, 1.983 + sctp_ss_fcfs_select, 1.984 + sctp_ss_default_scheduled, 1.985 + sctp_ss_default_packet_done, 1.986 + sctp_ss_default_get_value, 1.987 + sctp_ss_default_set_value 1.988 +#else 1.989 + .sctp_ss_init = sctp_ss_fcfs_init, 1.990 + .sctp_ss_clear = sctp_ss_fcfs_clear, 1.991 + .sctp_ss_init_stream = sctp_ss_fcfs_init_stream, 1.992 + .sctp_ss_add_to_stream = sctp_ss_fcfs_add, 1.993 + .sctp_ss_is_empty = sctp_ss_fcfs_is_empty, 1.994 + .sctp_ss_remove_from_stream = sctp_ss_fcfs_remove, 1.995 + .sctp_ss_select_stream = sctp_ss_fcfs_select, 1.996 + .sctp_ss_scheduled = sctp_ss_default_scheduled, 1.997 + .sctp_ss_packet_done = sctp_ss_default_packet_done, 1.998 + .sctp_ss_get_value = sctp_ss_default_get_value, 1.999 + .sctp_ss_set_value = sctp_ss_default_set_value 1.1000 +#endif 1.1001 +} 1.1002 +};