The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/netinet/sctp_ss_functions.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2010-2012, by Michael Tuexen. All rights reserved.
    3  * Copyright (c) 2010-2012, by Randall Stewart. All rights reserved.
    4  * Copyright (c) 2010-2012, by Robin Seggelmann. All rights reserved.
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions are met:
    8  *
    9  * a) Redistributions of source code must retain the above copyright notice,
   10  *    this list of conditions and the following disclaimer.
   11  *
   12  * b) Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in
   14  *    the documentation and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
   18  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
   20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
   26  * THE POSSIBILITY OF SUCH DAMAGE.
   27  */
   28 
   29 #include <sys/cdefs.h>
   30 __FBSDID("$FreeBSD$");
   31 
   32 #include <netinet/sctp_pcb.h>
   33 
   34 /*
   35  * Default simple round-robin algorithm.
   36  * Just interates the streams in the order they appear.
   37  */
   38 
   39 static void
   40 sctp_ss_default_add(struct sctp_tcb *, struct sctp_association *,
   41     struct sctp_stream_out *,
   42     struct sctp_stream_queue_pending *, int);
   43 
   44 static void
   45 sctp_ss_default_remove(struct sctp_tcb *, struct sctp_association *,
   46     struct sctp_stream_out *,
   47     struct sctp_stream_queue_pending *, int);
   48 
   49 static void
   50 sctp_ss_default_init(struct sctp_tcb *stcb, struct sctp_association *asoc,
   51     int holds_lock)
   52 {
   53         uint16_t i;
   54 
   55         if (holds_lock == 0) {
   56                 SCTP_TCB_SEND_LOCK(stcb);
   57         }
   58         asoc->ss_data.locked_on_sending = NULL;
   59         asoc->ss_data.last_out_stream = NULL;
   60         TAILQ_INIT(&asoc->ss_data.out.wheel);
   61         /*
   62          * If there is data in the stream queues already, the scheduler of
   63          * an existing association has been changed. We need to add all
   64          * stream queues to the wheel.
   65          */
   66         for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
   67                 stcb->asoc.ss_functions.sctp_ss_add_to_stream(stcb, &stcb->asoc,
   68                     &stcb->asoc.strmout[i],
   69                     NULL, 1);
   70         }
   71         if (holds_lock == 0) {
   72                 SCTP_TCB_SEND_UNLOCK(stcb);
   73         }
   74         return;
   75 }
   76 
   77 static void
   78 sctp_ss_default_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
   79     int clear_values SCTP_UNUSED, int holds_lock)
   80 {
   81         if (holds_lock == 0) {
   82                 SCTP_TCB_SEND_LOCK(stcb);
   83         }
   84         while (!TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
   85                 struct sctp_stream_out *strq;
   86 
   87                 strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
   88                 TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.rr.next_spoke);
   89                 strq->ss_params.rr.next_spoke.tqe_next = NULL;
   90                 strq->ss_params.rr.next_spoke.tqe_prev = NULL;
   91         }
   92         asoc->ss_data.last_out_stream = NULL;
   93         if (holds_lock == 0) {
   94                 SCTP_TCB_SEND_UNLOCK(stcb);
   95         }
   96         return;
   97 }
   98 
   99 static void
  100 sctp_ss_default_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
  101 {
  102         if (with_strq != NULL) {
  103                 if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
  104                         stcb->asoc.ss_data.locked_on_sending = strq;
  105                 }
  106                 if (stcb->asoc.ss_data.last_out_stream == with_strq) {
  107                         stcb->asoc.ss_data.last_out_stream = strq;
  108                 }
  109         }
  110         strq->ss_params.rr.next_spoke.tqe_next = NULL;
  111         strq->ss_params.rr.next_spoke.tqe_prev = NULL;
  112         return;
  113 }
  114 
  115 static void
  116 sctp_ss_default_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
  117     struct sctp_stream_out *strq,
  118     struct sctp_stream_queue_pending *sp SCTP_UNUSED, int holds_lock)
  119 {
  120         if (holds_lock == 0) {
  121                 SCTP_TCB_SEND_LOCK(stcb);
  122         }
  123         /* Add to wheel if not already on it and stream queue not empty */
  124         if (!TAILQ_EMPTY(&strq->outqueue) &&
  125             (strq->ss_params.rr.next_spoke.tqe_next == NULL) &&
  126             (strq->ss_params.rr.next_spoke.tqe_prev == NULL)) {
  127                 TAILQ_INSERT_TAIL(&asoc->ss_data.out.wheel,
  128                     strq, ss_params.rr.next_spoke);
  129         }
  130         if (holds_lock == 0) {
  131                 SCTP_TCB_SEND_UNLOCK(stcb);
  132         }
  133         return;
  134 }
  135 
  136 static int
  137 sctp_ss_default_is_empty(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc)
  138 {
  139         if (TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
  140                 return (1);
  141         } else {
  142                 return (0);
  143         }
  144 }
  145 
  146 static void
  147 sctp_ss_default_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
  148     struct sctp_stream_out *strq,
  149     struct sctp_stream_queue_pending *sp SCTP_UNUSED, int holds_lock)
  150 {
  151         if (holds_lock == 0) {
  152                 SCTP_TCB_SEND_LOCK(stcb);
  153         }
  154         /*
  155          * Remove from wheel if stream queue is empty and actually is on the
  156          * wheel
  157          */
  158         if (TAILQ_EMPTY(&strq->outqueue) &&
  159             (strq->ss_params.rr.next_spoke.tqe_next != NULL ||
  160             strq->ss_params.rr.next_spoke.tqe_prev != NULL)) {
  161                 if (asoc->ss_data.last_out_stream == strq) {
  162                         asoc->ss_data.last_out_stream = TAILQ_PREV(asoc->ss_data.last_out_stream,
  163                             sctpwheel_listhead,
  164                             ss_params.rr.next_spoke);
  165                         if (asoc->ss_data.last_out_stream == NULL) {
  166                                 asoc->ss_data.last_out_stream = TAILQ_LAST(&asoc->ss_data.out.wheel,
  167                                     sctpwheel_listhead);
  168                         }
  169                         if (asoc->ss_data.last_out_stream == strq) {
  170                                 asoc->ss_data.last_out_stream = NULL;
  171                         }
  172                 }
  173                 TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.rr.next_spoke);
  174                 strq->ss_params.rr.next_spoke.tqe_next = NULL;
  175                 strq->ss_params.rr.next_spoke.tqe_prev = NULL;
  176         }
  177         if (holds_lock == 0) {
  178                 SCTP_TCB_SEND_UNLOCK(stcb);
  179         }
  180         return;
  181 }
  182 
  183 
  184 static struct sctp_stream_out *
  185 sctp_ss_default_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
  186     struct sctp_association *asoc)
  187 {
  188         struct sctp_stream_out *strq, *strqt;
  189 
  190         if (asoc->ss_data.locked_on_sending) {
  191                 return (asoc->ss_data.locked_on_sending);
  192         }
  193         strqt = asoc->ss_data.last_out_stream;
  194 default_again:
  195         /* Find the next stream to use */
  196         if (strqt == NULL) {
  197                 strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
  198         } else {
  199                 strq = TAILQ_NEXT(strqt, ss_params.rr.next_spoke);
  200                 if (strq == NULL) {
  201                         strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
  202                 }
  203         }
  204 
  205         /*
  206          * If CMT is off, we must validate that the stream in question has
  207          * the first item pointed towards are network destination requested
  208          * by the caller. Note that if we turn out to be locked to a stream
  209          * (assigning TSN's then we must stop, since we cannot look for
  210          * another stream with data to send to that destination). In CMT's
  211          * case, by skipping this check, we will send one data packet
  212          * towards the requested net.
  213          */
  214         if (net != NULL && strq != NULL &&
  215             SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
  216                 if (TAILQ_FIRST(&strq->outqueue) &&
  217                     TAILQ_FIRST(&strq->outqueue)->net != NULL &&
  218                     TAILQ_FIRST(&strq->outqueue)->net != net) {
  219                         if (strq == asoc->ss_data.last_out_stream) {
  220                                 return (NULL);
  221                         } else {
  222                                 strqt = strq;
  223                                 goto default_again;
  224                         }
  225                 }
  226         }
  227         return (strq);
  228 }
  229 
  230 static void
  231 sctp_ss_default_scheduled(struct sctp_tcb *stcb,
  232     struct sctp_nets *net SCTP_UNUSED,
  233     struct sctp_association *asoc,
  234     struct sctp_stream_out *strq,
  235     int moved_how_much SCTP_UNUSED)
  236 {
  237         struct sctp_stream_queue_pending *sp;
  238 
  239         asoc->ss_data.last_out_stream = strq;
  240         if (stcb->asoc.idata_supported == 0) {
  241                 sp = TAILQ_FIRST(&strq->outqueue);
  242                 if ((sp != NULL) && (sp->some_taken == 1)) {
  243                         stcb->asoc.ss_data.locked_on_sending = strq;
  244                 } else {
  245                         stcb->asoc.ss_data.locked_on_sending = NULL;
  246                 }
  247         } else {
  248                 stcb->asoc.ss_data.locked_on_sending = NULL;
  249         }
  250         return;
  251 }
  252 
  253 static void
  254 sctp_ss_default_packet_done(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net SCTP_UNUSED,
  255     struct sctp_association *asoc SCTP_UNUSED)
  256 {
  257         /* Nothing to be done here */
  258         return;
  259 }
  260 
  261 static int
  262 sctp_ss_default_get_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED,
  263     struct sctp_stream_out *strq SCTP_UNUSED, uint16_t *value SCTP_UNUSED)
  264 {
  265         /* Nothing to be done here */
  266         return (-1);
  267 }
  268 
  269 static int
  270 sctp_ss_default_set_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED,
  271     struct sctp_stream_out *strq SCTP_UNUSED, uint16_t value SCTP_UNUSED)
  272 {
  273         /* Nothing to be done here */
  274         return (-1);
  275 }
  276 
  277 static int
  278 sctp_ss_default_is_user_msgs_incomplete(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc)
  279 {
  280         struct sctp_stream_out *strq;
  281         struct sctp_stream_queue_pending *sp;
  282 
  283         if (asoc->stream_queue_cnt != 1) {
  284                 return (0);
  285         }
  286         strq = asoc->ss_data.locked_on_sending;
  287         if (strq == NULL) {
  288                 return (0);
  289         }
  290         sp = TAILQ_FIRST(&strq->outqueue);
  291         if (sp == NULL) {
  292                 return (0);
  293         }
  294         return (!sp->msg_is_complete);
  295 }
  296 
  297 /*
  298  * Real round-robin algorithm.
  299  * Always interates the streams in ascending order.
  300  */
  301 static void
  302 sctp_ss_rr_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
  303     struct sctp_stream_out *strq,
  304     struct sctp_stream_queue_pending *sp SCTP_UNUSED, int holds_lock)
  305 {
  306         struct sctp_stream_out *strqt;
  307 
  308         if (holds_lock == 0) {
  309                 SCTP_TCB_SEND_LOCK(stcb);
  310         }
  311         if (!TAILQ_EMPTY(&strq->outqueue) &&
  312             (strq->ss_params.rr.next_spoke.tqe_next == NULL) &&
  313             (strq->ss_params.rr.next_spoke.tqe_prev == NULL)) {
  314                 if (TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
  315                         TAILQ_INSERT_HEAD(&asoc->ss_data.out.wheel, strq, ss_params.rr.next_spoke);
  316                 } else {
  317                         strqt = TAILQ_FIRST(&asoc->ss_data.out.wheel);
  318                         while (strqt != NULL && (strqt->sid < strq->sid)) {
  319                                 strqt = TAILQ_NEXT(strqt, ss_params.rr.next_spoke);
  320                         }
  321                         if (strqt != NULL) {
  322                                 TAILQ_INSERT_BEFORE(strqt, strq, ss_params.rr.next_spoke);
  323                         } else {
  324                                 TAILQ_INSERT_TAIL(&asoc->ss_data.out.wheel, strq, ss_params.rr.next_spoke);
  325                         }
  326                 }
  327         }
  328         if (holds_lock == 0) {
  329                 SCTP_TCB_SEND_UNLOCK(stcb);
  330         }
  331         return;
  332 }
  333 
  334 /*
  335  * Real round-robin per packet algorithm.
  336  * Always interates the streams in ascending order and
  337  * only fills messages of the same stream in a packet.
  338  */
  339 static struct sctp_stream_out *
  340 sctp_ss_rrp_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net SCTP_UNUSED,
  341     struct sctp_association *asoc)
  342 {
  343         return (asoc->ss_data.last_out_stream);
  344 }
  345 
  346 static void
  347 sctp_ss_rrp_packet_done(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
  348     struct sctp_association *asoc)
  349 {
  350         struct sctp_stream_out *strq, *strqt;
  351 
  352         strqt = asoc->ss_data.last_out_stream;
  353 rrp_again:
  354         /* Find the next stream to use */
  355         if (strqt == NULL) {
  356                 strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
  357         } else {
  358                 strq = TAILQ_NEXT(strqt, ss_params.rr.next_spoke);
  359                 if (strq == NULL) {
  360                         strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
  361                 }
  362         }
  363 
  364         /*
  365          * If CMT is off, we must validate that the stream in question has
  366          * the first item pointed towards are network destination requested
  367          * by the caller. Note that if we turn out to be locked to a stream
  368          * (assigning TSN's then we must stop, since we cannot look for
  369          * another stream with data to send to that destination). In CMT's
  370          * case, by skipping this check, we will send one data packet
  371          * towards the requested net.
  372          */
  373         if (net != NULL && strq != NULL &&
  374             SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
  375                 if (TAILQ_FIRST(&strq->outqueue) &&
  376                     TAILQ_FIRST(&strq->outqueue)->net != NULL &&
  377                     TAILQ_FIRST(&strq->outqueue)->net != net) {
  378                         if (strq == asoc->ss_data.last_out_stream) {
  379                                 strq = NULL;
  380                         } else {
  381                                 strqt = strq;
  382                                 goto rrp_again;
  383                         }
  384                 }
  385         }
  386         asoc->ss_data.last_out_stream = strq;
  387         return;
  388 }
  389 
  390 
  391 /*
  392  * Priority algorithm.
  393  * Always prefers streams based on their priority id.
  394  */
  395 static void
  396 sctp_ss_prio_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
  397     int clear_values, int holds_lock)
  398 {
  399         if (holds_lock == 0) {
  400                 SCTP_TCB_SEND_LOCK(stcb);
  401         }
  402         while (!TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
  403                 struct sctp_stream_out *strq;
  404 
  405                 strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
  406                 if (clear_values) {
  407                         strq->ss_params.prio.priority = 0;
  408                 }
  409                 TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.prio.next_spoke);
  410                 strq->ss_params.prio.next_spoke.tqe_next = NULL;
  411                 strq->ss_params.prio.next_spoke.tqe_prev = NULL;
  412 
  413         }
  414         asoc->ss_data.last_out_stream = NULL;
  415         if (holds_lock == 0) {
  416                 SCTP_TCB_SEND_UNLOCK(stcb);
  417         }
  418         return;
  419 }
  420 
  421 static void
  422 sctp_ss_prio_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
  423 {
  424         if (with_strq != NULL) {
  425                 if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
  426                         stcb->asoc.ss_data.locked_on_sending = strq;
  427                 }
  428                 if (stcb->asoc.ss_data.last_out_stream == with_strq) {
  429                         stcb->asoc.ss_data.last_out_stream = strq;
  430                 }
  431         }
  432         strq->ss_params.prio.next_spoke.tqe_next = NULL;
  433         strq->ss_params.prio.next_spoke.tqe_prev = NULL;
  434         if (with_strq != NULL) {
  435                 strq->ss_params.prio.priority = with_strq->ss_params.prio.priority;
  436         } else {
  437                 strq->ss_params.prio.priority = 0;
  438         }
  439         return;
  440 }
  441 
  442 static void
  443 sctp_ss_prio_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
  444     struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED,
  445     int holds_lock)
  446 {
  447         struct sctp_stream_out *strqt;
  448 
  449         if (holds_lock == 0) {
  450                 SCTP_TCB_SEND_LOCK(stcb);
  451         }
  452         /* Add to wheel if not already on it and stream queue not empty */
  453         if (!TAILQ_EMPTY(&strq->outqueue) &&
  454             (strq->ss_params.prio.next_spoke.tqe_next == NULL) &&
  455             (strq->ss_params.prio.next_spoke.tqe_prev == NULL)) {
  456                 if (TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
  457                         TAILQ_INSERT_HEAD(&asoc->ss_data.out.wheel, strq, ss_params.prio.next_spoke);
  458                 } else {
  459                         strqt = TAILQ_FIRST(&asoc->ss_data.out.wheel);
  460                         while (strqt != NULL && strqt->ss_params.prio.priority < strq->ss_params.prio.priority) {
  461                                 strqt = TAILQ_NEXT(strqt, ss_params.prio.next_spoke);
  462                         }
  463                         if (strqt != NULL) {
  464                                 TAILQ_INSERT_BEFORE(strqt, strq, ss_params.prio.next_spoke);
  465                         } else {
  466                                 TAILQ_INSERT_TAIL(&asoc->ss_data.out.wheel, strq, ss_params.prio.next_spoke);
  467                         }
  468                 }
  469         }
  470         if (holds_lock == 0) {
  471                 SCTP_TCB_SEND_UNLOCK(stcb);
  472         }
  473         return;
  474 }
  475 
  476 static void
  477 sctp_ss_prio_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
  478     struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED,
  479     int holds_lock)
  480 {
  481         if (holds_lock == 0) {
  482                 SCTP_TCB_SEND_LOCK(stcb);
  483         }
  484         /*
  485          * Remove from wheel if stream queue is empty and actually is on the
  486          * wheel
  487          */
  488         if (TAILQ_EMPTY(&strq->outqueue) &&
  489             (strq->ss_params.prio.next_spoke.tqe_next != NULL ||
  490             strq->ss_params.prio.next_spoke.tqe_prev != NULL)) {
  491                 if (asoc->ss_data.last_out_stream == strq) {
  492                         asoc->ss_data.last_out_stream = TAILQ_PREV(asoc->ss_data.last_out_stream, sctpwheel_listhead,
  493                             ss_params.prio.next_spoke);
  494                         if (asoc->ss_data.last_out_stream == NULL) {
  495                                 asoc->ss_data.last_out_stream = TAILQ_LAST(&asoc->ss_data.out.wheel,
  496                                     sctpwheel_listhead);
  497                         }
  498                         if (asoc->ss_data.last_out_stream == strq) {
  499                                 asoc->ss_data.last_out_stream = NULL;
  500                         }
  501                 }
  502                 TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.prio.next_spoke);
  503                 strq->ss_params.prio.next_spoke.tqe_next = NULL;
  504                 strq->ss_params.prio.next_spoke.tqe_prev = NULL;
  505         }
  506         if (holds_lock == 0) {
  507                 SCTP_TCB_SEND_UNLOCK(stcb);
  508         }
  509         return;
  510 }
  511 
  512 static struct sctp_stream_out *
  513 sctp_ss_prio_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
  514     struct sctp_association *asoc)
  515 {
  516         struct sctp_stream_out *strq, *strqt, *strqn;
  517 
  518         if (asoc->ss_data.locked_on_sending) {
  519                 return (asoc->ss_data.locked_on_sending);
  520         }
  521         strqt = asoc->ss_data.last_out_stream;
  522 prio_again:
  523         /* Find the next stream to use */
  524         if (strqt == NULL) {
  525                 strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
  526         } else {
  527                 strqn = TAILQ_NEXT(strqt, ss_params.prio.next_spoke);
  528                 if (strqn != NULL &&
  529                     strqn->ss_params.prio.priority == strqt->ss_params.prio.priority) {
  530                         strq = strqn;
  531                 } else {
  532                         strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
  533                 }
  534         }
  535 
  536         /*
  537          * If CMT is off, we must validate that the stream in question has
  538          * the first item pointed towards are network destination requested
  539          * by the caller. Note that if we turn out to be locked to a stream
  540          * (assigning TSN's then we must stop, since we cannot look for
  541          * another stream with data to send to that destination). In CMT's
  542          * case, by skipping this check, we will send one data packet
  543          * towards the requested net.
  544          */
  545         if (net != NULL && strq != NULL &&
  546             SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
  547                 if (TAILQ_FIRST(&strq->outqueue) &&
  548                     TAILQ_FIRST(&strq->outqueue)->net != NULL &&
  549                     TAILQ_FIRST(&strq->outqueue)->net != net) {
  550                         if (strq == asoc->ss_data.last_out_stream) {
  551                                 return (NULL);
  552                         } else {
  553                                 strqt = strq;
  554                                 goto prio_again;
  555                         }
  556                 }
  557         }
  558         return (strq);
  559 }
  560 
  561 static int
  562 sctp_ss_prio_get_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED,
  563     struct sctp_stream_out *strq, uint16_t *value)
  564 {
  565         if (strq == NULL) {
  566                 return (-1);
  567         }
  568         *value = strq->ss_params.prio.priority;
  569         return (1);
  570 }
  571 
  572 static int
  573 sctp_ss_prio_set_value(struct sctp_tcb *stcb, struct sctp_association *asoc,
  574     struct sctp_stream_out *strq, uint16_t value)
  575 {
  576         if (strq == NULL) {
  577                 return (-1);
  578         }
  579         strq->ss_params.prio.priority = value;
  580         sctp_ss_prio_remove(stcb, asoc, strq, NULL, 1);
  581         sctp_ss_prio_add(stcb, asoc, strq, NULL, 1);
  582         return (1);
  583 }
  584 
  585 /*
  586  * Fair bandwidth algorithm.
  587  * Maintains an equal troughput per stream.
  588  */
  589 static void
  590 sctp_ss_fb_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
  591     int clear_values, int holds_lock)
  592 {
  593         if (holds_lock == 0) {
  594                 SCTP_TCB_SEND_LOCK(stcb);
  595         }
  596         while (!TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
  597                 struct sctp_stream_out *strq;
  598 
  599                 strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
  600                 if (clear_values) {
  601                         strq->ss_params.fb.rounds = -1;
  602                 }
  603                 TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.fb.next_spoke);
  604                 strq->ss_params.fb.next_spoke.tqe_next = NULL;
  605                 strq->ss_params.fb.next_spoke.tqe_prev = NULL;
  606         }
  607         asoc->ss_data.last_out_stream = NULL;
  608         if (holds_lock == 0) {
  609                 SCTP_TCB_SEND_UNLOCK(stcb);
  610         }
  611         return;
  612 }
  613 
  614 static void
  615 sctp_ss_fb_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
  616 {
  617         if (with_strq != NULL) {
  618                 if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
  619                         stcb->asoc.ss_data.locked_on_sending = strq;
  620                 }
  621                 if (stcb->asoc.ss_data.last_out_stream == with_strq) {
  622                         stcb->asoc.ss_data.last_out_stream = strq;
  623                 }
  624         }
  625         strq->ss_params.fb.next_spoke.tqe_next = NULL;
  626         strq->ss_params.fb.next_spoke.tqe_prev = NULL;
  627         if (with_strq != NULL) {
  628                 strq->ss_params.fb.rounds = with_strq->ss_params.fb.rounds;
  629         } else {
  630                 strq->ss_params.fb.rounds = -1;
  631         }
  632         return;
  633 }
  634 
  635 static void
  636 sctp_ss_fb_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
  637     struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED,
  638     int holds_lock)
  639 {
  640         if (holds_lock == 0) {
  641                 SCTP_TCB_SEND_LOCK(stcb);
  642         }
  643         if (!TAILQ_EMPTY(&strq->outqueue) &&
  644             (strq->ss_params.fb.next_spoke.tqe_next == NULL) &&
  645             (strq->ss_params.fb.next_spoke.tqe_prev == NULL)) {
  646                 if (strq->ss_params.fb.rounds < 0)
  647                         strq->ss_params.fb.rounds = TAILQ_FIRST(&strq->outqueue)->length;
  648                 TAILQ_INSERT_TAIL(&asoc->ss_data.out.wheel, strq, ss_params.fb.next_spoke);
  649         }
  650         if (holds_lock == 0) {
  651                 SCTP_TCB_SEND_UNLOCK(stcb);
  652         }
  653         return;
  654 }
  655 
  656 static void
  657 sctp_ss_fb_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
  658     struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED,
  659     int holds_lock)
  660 {
  661         if (holds_lock == 0) {
  662                 SCTP_TCB_SEND_LOCK(stcb);
  663         }
  664         /*
  665          * Remove from wheel if stream queue is empty and actually is on the
  666          * wheel
  667          */
  668         if (TAILQ_EMPTY(&strq->outqueue) &&
  669             (strq->ss_params.fb.next_spoke.tqe_next != NULL ||
  670             strq->ss_params.fb.next_spoke.tqe_prev != NULL)) {
  671                 if (asoc->ss_data.last_out_stream == strq) {
  672                         asoc->ss_data.last_out_stream = TAILQ_PREV(asoc->ss_data.last_out_stream, sctpwheel_listhead,
  673                             ss_params.fb.next_spoke);
  674                         if (asoc->ss_data.last_out_stream == NULL) {
  675                                 asoc->ss_data.last_out_stream = TAILQ_LAST(&asoc->ss_data.out.wheel,
  676                                     sctpwheel_listhead);
  677                         }
  678                         if (asoc->ss_data.last_out_stream == strq) {
  679                                 asoc->ss_data.last_out_stream = NULL;
  680                         }
  681                 }
  682                 TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.fb.next_spoke);
  683                 strq->ss_params.fb.next_spoke.tqe_next = NULL;
  684                 strq->ss_params.fb.next_spoke.tqe_prev = NULL;
  685         }
  686         if (holds_lock == 0) {
  687                 SCTP_TCB_SEND_UNLOCK(stcb);
  688         }
  689         return;
  690 }
  691 
  692 static struct sctp_stream_out *
  693 sctp_ss_fb_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
  694     struct sctp_association *asoc)
  695 {
  696         struct sctp_stream_out *strq = NULL, *strqt;
  697 
  698         if (asoc->ss_data.locked_on_sending) {
  699                 return (asoc->ss_data.locked_on_sending);
  700         }
  701         if (asoc->ss_data.last_out_stream == NULL ||
  702             TAILQ_FIRST(&asoc->ss_data.out.wheel) == TAILQ_LAST(&asoc->ss_data.out.wheel, sctpwheel_listhead)) {
  703                 strqt = TAILQ_FIRST(&asoc->ss_data.out.wheel);
  704         } else {
  705                 strqt = TAILQ_NEXT(asoc->ss_data.last_out_stream, ss_params.fb.next_spoke);
  706         }
  707         do {
  708                 if ((strqt != NULL) &&
  709                     ((SCTP_BASE_SYSCTL(sctp_cmt_on_off) > 0) ||
  710                     (SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0 &&
  711                     (net == NULL || (TAILQ_FIRST(&strqt->outqueue) && TAILQ_FIRST(&strqt->outqueue)->net == NULL) ||
  712                     (net != NULL && TAILQ_FIRST(&strqt->outqueue) && TAILQ_FIRST(&strqt->outqueue)->net != NULL &&
  713                     TAILQ_FIRST(&strqt->outqueue)->net == net))))) {
  714                         if ((strqt->ss_params.fb.rounds >= 0) && (strq == NULL ||
  715                             strqt->ss_params.fb.rounds < strq->ss_params.fb.rounds)) {
  716                                 strq = strqt;
  717                         }
  718                 }
  719                 if (strqt != NULL) {
  720                         strqt = TAILQ_NEXT(strqt, ss_params.fb.next_spoke);
  721                 } else {
  722                         strqt = TAILQ_FIRST(&asoc->ss_data.out.wheel);
  723                 }
  724         } while (strqt != strq);
  725         return (strq);
  726 }
  727 
  728 static void
  729 sctp_ss_fb_scheduled(struct sctp_tcb *stcb, struct sctp_nets *net SCTP_UNUSED,
  730     struct sctp_association *asoc, struct sctp_stream_out *strq,
  731     int moved_how_much SCTP_UNUSED)
  732 {
  733         struct sctp_stream_queue_pending *sp;
  734         struct sctp_stream_out *strqt;
  735         int subtract;
  736 
  737         if (stcb->asoc.idata_supported == 0) {
  738                 sp = TAILQ_FIRST(&strq->outqueue);
  739                 if ((sp != NULL) && (sp->some_taken == 1)) {
  740                         stcb->asoc.ss_data.locked_on_sending = strq;
  741                 } else {
  742                         stcb->asoc.ss_data.locked_on_sending = NULL;
  743                 }
  744         } else {
  745                 stcb->asoc.ss_data.locked_on_sending = NULL;
  746         }
  747         subtract = strq->ss_params.fb.rounds;
  748         TAILQ_FOREACH(strqt, &asoc->ss_data.out.wheel, ss_params.fb.next_spoke) {
  749                 strqt->ss_params.fb.rounds -= subtract;
  750                 if (strqt->ss_params.fb.rounds < 0)
  751                         strqt->ss_params.fb.rounds = 0;
  752         }
  753         if (TAILQ_FIRST(&strq->outqueue)) {
  754                 strq->ss_params.fb.rounds = TAILQ_FIRST(&strq->outqueue)->length;
  755         } else {
  756                 strq->ss_params.fb.rounds = -1;
  757         }
  758         asoc->ss_data.last_out_stream = strq;
  759         return;
  760 }
  761 
  762 /*
  763  * First-come, first-serve algorithm.
  764  * Maintains the order provided by the application.
  765  */
  766 static void
  767 sctp_ss_fcfs_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
  768     struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp,
  769     int holds_lock);
  770 
  771 static void
  772 sctp_ss_fcfs_init(struct sctp_tcb *stcb, struct sctp_association *asoc,
  773     int holds_lock)
  774 {
  775         uint32_t x, n = 0, add_more = 1;
  776         struct sctp_stream_queue_pending *sp;
  777         uint16_t i;
  778 
  779         if (holds_lock == 0) {
  780                 SCTP_TCB_SEND_LOCK(stcb);
  781         }
  782         TAILQ_INIT(&asoc->ss_data.out.list);
  783         /*
  784          * If there is data in the stream queues already, the scheduler of
  785          * an existing association has been changed. We can only cycle
  786          * through the stream queues and add everything to the FCFS queue.
  787          */
  788         while (add_more) {
  789                 add_more = 0;
  790                 for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
  791                         sp = TAILQ_FIRST(&stcb->asoc.strmout[i].outqueue);
  792                         x = 0;
  793                         /* Find n. message in current stream queue */
  794                         while (sp != NULL && x < n) {
  795                                 sp = TAILQ_NEXT(sp, next);
  796                                 x++;
  797                         }
  798                         if (sp != NULL) {
  799                                 sctp_ss_fcfs_add(stcb, &stcb->asoc, &stcb->asoc.strmout[i], sp, 1);
  800                                 add_more = 1;
  801                         }
  802                 }
  803                 n++;
  804         }
  805         if (holds_lock == 0) {
  806                 SCTP_TCB_SEND_UNLOCK(stcb);
  807         }
  808         return;
  809 }
  810 
  811 static void
  812 sctp_ss_fcfs_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
  813     int clear_values, int holds_lock)
  814 {
  815         struct sctp_stream_queue_pending *sp;
  816 
  817         if (clear_values) {
  818                 if (holds_lock == 0) {
  819                         SCTP_TCB_SEND_LOCK(stcb);
  820                 }
  821                 while (!TAILQ_EMPTY(&asoc->ss_data.out.list)) {
  822                         sp = TAILQ_FIRST(&asoc->ss_data.out.list);
  823                         TAILQ_REMOVE(&asoc->ss_data.out.list, sp, ss_next);
  824                         sp->ss_next.tqe_next = NULL;
  825                         sp->ss_next.tqe_prev = NULL;
  826                 }
  827                 if (holds_lock == 0) {
  828                         SCTP_TCB_SEND_UNLOCK(stcb);
  829                 }
  830         }
  831         return;
  832 }
  833 
  834 static void
  835 sctp_ss_fcfs_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
  836 {
  837         if (with_strq != NULL) {
  838                 if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
  839                         stcb->asoc.ss_data.locked_on_sending = strq;
  840                 }
  841                 if (stcb->asoc.ss_data.last_out_stream == with_strq) {
  842                         stcb->asoc.ss_data.last_out_stream = strq;
  843                 }
  844         }
  845         strq->ss_params.fb.next_spoke.tqe_next = NULL;
  846         strq->ss_params.fb.next_spoke.tqe_prev = NULL;
  847         return;
  848 }
  849 
  850 static void
  851 sctp_ss_fcfs_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
  852     struct sctp_stream_out *strq SCTP_UNUSED, struct sctp_stream_queue_pending *sp,
  853     int holds_lock)
  854 {
  855         if (holds_lock == 0) {
  856                 SCTP_TCB_SEND_LOCK(stcb);
  857         }
  858         if (sp && (sp->ss_next.tqe_next == NULL) &&
  859             (sp->ss_next.tqe_prev == NULL)) {
  860                 TAILQ_INSERT_TAIL(&asoc->ss_data.out.list, sp, ss_next);
  861         }
  862         if (holds_lock == 0) {
  863                 SCTP_TCB_SEND_UNLOCK(stcb);
  864         }
  865         return;
  866 }
  867 
  868 static int
  869 sctp_ss_fcfs_is_empty(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc)
  870 {
  871         if (TAILQ_EMPTY(&asoc->ss_data.out.list)) {
  872                 return (1);
  873         } else {
  874                 return (0);
  875         }
  876 }
  877 
  878 static void
  879 sctp_ss_fcfs_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
  880     struct sctp_stream_out *strq SCTP_UNUSED, struct sctp_stream_queue_pending *sp,
  881     int holds_lock)
  882 {
  883         if (holds_lock == 0) {
  884                 SCTP_TCB_SEND_LOCK(stcb);
  885         }
  886         if (sp &&
  887             ((sp->ss_next.tqe_next != NULL) ||
  888             (sp->ss_next.tqe_prev != NULL))) {
  889                 TAILQ_REMOVE(&asoc->ss_data.out.list, sp, ss_next);
  890                 sp->ss_next.tqe_next = NULL;
  891                 sp->ss_next.tqe_prev = NULL;
  892         }
  893         if (holds_lock == 0) {
  894                 SCTP_TCB_SEND_UNLOCK(stcb);
  895         }
  896         return;
  897 }
  898 
  899 
  900 static struct sctp_stream_out *
  901 sctp_ss_fcfs_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
  902     struct sctp_association *asoc)
  903 {
  904         struct sctp_stream_out *strq;
  905         struct sctp_stream_queue_pending *sp;
  906 
  907         if (asoc->ss_data.locked_on_sending) {
  908                 return (asoc->ss_data.locked_on_sending);
  909         }
  910         sp = TAILQ_FIRST(&asoc->ss_data.out.list);
  911 default_again:
  912         if (sp != NULL) {
  913                 strq = &asoc->strmout[sp->sid];
  914         } else {
  915                 strq = NULL;
  916         }
  917 
  918         /*
  919          * If CMT is off, we must validate that the stream in question has
  920          * the first item pointed towards are network destination requested
  921          * by the caller. Note that if we turn out to be locked to a stream
  922          * (assigning TSN's then we must stop, since we cannot look for
  923          * another stream with data to send to that destination). In CMT's
  924          * case, by skipping this check, we will send one data packet
  925          * towards the requested net.
  926          */
  927         if (net != NULL && strq != NULL &&
  928             SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
  929                 if (TAILQ_FIRST(&strq->outqueue) &&
  930                     TAILQ_FIRST(&strq->outqueue)->net != NULL &&
  931                     TAILQ_FIRST(&strq->outqueue)->net != net) {
  932                         sp = TAILQ_NEXT(sp, ss_next);
  933                         goto default_again;
  934                 }
  935         }
  936         return (strq);
  937 }
  938 
  939 const struct sctp_ss_functions sctp_ss_functions[] = {
  940 /* SCTP_SS_DEFAULT */
  941         {
  942                 .sctp_ss_init = sctp_ss_default_init,
  943                 .sctp_ss_clear = sctp_ss_default_clear,
  944                 .sctp_ss_init_stream = sctp_ss_default_init_stream,
  945                 .sctp_ss_add_to_stream = sctp_ss_default_add,
  946                 .sctp_ss_is_empty = sctp_ss_default_is_empty,
  947                 .sctp_ss_remove_from_stream = sctp_ss_default_remove,
  948                 .sctp_ss_select_stream = sctp_ss_default_select,
  949                 .sctp_ss_scheduled = sctp_ss_default_scheduled,
  950                 .sctp_ss_packet_done = sctp_ss_default_packet_done,
  951                 .sctp_ss_get_value = sctp_ss_default_get_value,
  952                 .sctp_ss_set_value = sctp_ss_default_set_value,
  953                 .sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
  954         },
  955 /* SCTP_SS_ROUND_ROBIN */
  956         {
  957                 .sctp_ss_init = sctp_ss_default_init,
  958                 .sctp_ss_clear = sctp_ss_default_clear,
  959                 .sctp_ss_init_stream = sctp_ss_default_init_stream,
  960                 .sctp_ss_add_to_stream = sctp_ss_rr_add,
  961                 .sctp_ss_is_empty = sctp_ss_default_is_empty,
  962                 .sctp_ss_remove_from_stream = sctp_ss_default_remove,
  963                 .sctp_ss_select_stream = sctp_ss_default_select,
  964                 .sctp_ss_scheduled = sctp_ss_default_scheduled,
  965                 .sctp_ss_packet_done = sctp_ss_default_packet_done,
  966                 .sctp_ss_get_value = sctp_ss_default_get_value,
  967                 .sctp_ss_set_value = sctp_ss_default_set_value,
  968                 .sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
  969         },
  970 /* SCTP_SS_ROUND_ROBIN_PACKET */
  971         {
  972                 .sctp_ss_init = sctp_ss_default_init,
  973                 .sctp_ss_clear = sctp_ss_default_clear,
  974                 .sctp_ss_init_stream = sctp_ss_default_init_stream,
  975                 .sctp_ss_add_to_stream = sctp_ss_rr_add,
  976                 .sctp_ss_is_empty = sctp_ss_default_is_empty,
  977                 .sctp_ss_remove_from_stream = sctp_ss_default_remove,
  978                 .sctp_ss_select_stream = sctp_ss_rrp_select,
  979                 .sctp_ss_scheduled = sctp_ss_default_scheduled,
  980                 .sctp_ss_packet_done = sctp_ss_rrp_packet_done,
  981                 .sctp_ss_get_value = sctp_ss_default_get_value,
  982                 .sctp_ss_set_value = sctp_ss_default_set_value,
  983                 .sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
  984         },
  985 /* SCTP_SS_PRIORITY */
  986         {
  987                 .sctp_ss_init = sctp_ss_default_init,
  988                 .sctp_ss_clear = sctp_ss_prio_clear,
  989                 .sctp_ss_init_stream = sctp_ss_prio_init_stream,
  990                 .sctp_ss_add_to_stream = sctp_ss_prio_add,
  991                 .sctp_ss_is_empty = sctp_ss_default_is_empty,
  992                 .sctp_ss_remove_from_stream = sctp_ss_prio_remove,
  993                 .sctp_ss_select_stream = sctp_ss_prio_select,
  994                 .sctp_ss_scheduled = sctp_ss_default_scheduled,
  995                 .sctp_ss_packet_done = sctp_ss_default_packet_done,
  996                 .sctp_ss_get_value = sctp_ss_prio_get_value,
  997                 .sctp_ss_set_value = sctp_ss_prio_set_value,
  998                 .sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
  999         },
 1000 /* SCTP_SS_FAIR_BANDWITH */
 1001         {
 1002                 .sctp_ss_init = sctp_ss_default_init,
 1003                 .sctp_ss_clear = sctp_ss_fb_clear,
 1004                 .sctp_ss_init_stream = sctp_ss_fb_init_stream,
 1005                 .sctp_ss_add_to_stream = sctp_ss_fb_add,
 1006                 .sctp_ss_is_empty = sctp_ss_default_is_empty,
 1007                 .sctp_ss_remove_from_stream = sctp_ss_fb_remove,
 1008                 .sctp_ss_select_stream = sctp_ss_fb_select,
 1009                 .sctp_ss_scheduled = sctp_ss_fb_scheduled,
 1010                 .sctp_ss_packet_done = sctp_ss_default_packet_done,
 1011                 .sctp_ss_get_value = sctp_ss_default_get_value,
 1012                 .sctp_ss_set_value = sctp_ss_default_set_value,
 1013                 .sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
 1014         },
 1015 /* SCTP_SS_FIRST_COME */
 1016         {
 1017                 .sctp_ss_init = sctp_ss_fcfs_init,
 1018                 .sctp_ss_clear = sctp_ss_fcfs_clear,
 1019                 .sctp_ss_init_stream = sctp_ss_fcfs_init_stream,
 1020                 .sctp_ss_add_to_stream = sctp_ss_fcfs_add,
 1021                 .sctp_ss_is_empty = sctp_ss_fcfs_is_empty,
 1022                 .sctp_ss_remove_from_stream = sctp_ss_fcfs_remove,
 1023                 .sctp_ss_select_stream = sctp_ss_fcfs_select,
 1024                 .sctp_ss_scheduled = sctp_ss_default_scheduled,
 1025                 .sctp_ss_packet_done = sctp_ss_default_packet_done,
 1026                 .sctp_ss_get_value = sctp_ss_default_get_value,
 1027                 .sctp_ss_set_value = sctp_ss_default_set_value,
 1028                 .sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
 1029         }
 1030 };

Cache object: 48de23ed19a854f412f8c76f389c4a55


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.