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

Cache object: ba4a49fc3b44bcd81c96205650cc3439


[ 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.