netwerk/sctp/src/netinet/sctp_ss_functions.c

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rwxr-xr-x

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial