#include <netinet/sctp_os.h>
#include <netinet/sctp_pcb.h>
static void
sctp_ss_default_add(struct sctp_tcb *, struct sctp_association *,
struct sctp_stream_out *,
struct sctp_stream_queue_pending *);
static void
sctp_ss_default_remove(struct sctp_tcb *, struct sctp_association *,
struct sctp_stream_out *,
struct sctp_stream_queue_pending *);
static void
sctp_ss_default_init(struct sctp_tcb *stcb, struct sctp_association *asoc)
{
uint16_t i;
SCTP_TCB_LOCK_ASSERT(stcb);
asoc->ss_data.locked_on_sending = NULL;
asoc->ss_data.last_out_stream = NULL;
TAILQ_INIT(&asoc->ss_data.out.wheel);
for (i = 0; i < asoc->streamoutcnt; i++) {
stcb->asoc.ss_functions.sctp_ss_add_to_stream(stcb, asoc,
&asoc->strmout[i],
NULL);
}
return;
}
static void
sctp_ss_default_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
bool clear_values SCTP_UNUSED)
{
SCTP_TCB_LOCK_ASSERT(stcb);
while (!TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
struct sctp_stream_out *strq;
strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
KASSERT(strq->ss_params.scheduled, ("strq %p not scheduled", (void *)strq));
TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.ss.rr.next_spoke);
strq->ss_params.scheduled = false;
}
asoc->ss_data.last_out_stream = NULL;
return;
}
static void
sctp_ss_default_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
{
SCTP_TCB_LOCK_ASSERT(stcb);
if (with_strq != NULL) {
if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
stcb->asoc.ss_data.locked_on_sending = strq;
}
if (stcb->asoc.ss_data.last_out_stream == with_strq) {
stcb->asoc.ss_data.last_out_stream = strq;
}
}
strq->ss_params.scheduled = false;
return;
}
static void
sctp_ss_default_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
struct sctp_stream_out *strq,
struct sctp_stream_queue_pending *sp SCTP_UNUSED)
{
SCTP_TCB_LOCK_ASSERT(stcb);
if (!TAILQ_EMPTY(&strq->outqueue) && !strq->ss_params.scheduled) {
TAILQ_INSERT_TAIL(&asoc->ss_data.out.wheel,
strq, ss_params.ss.rr.next_spoke);
strq->ss_params.scheduled = true;
}
return;
}
static bool
sctp_ss_default_is_empty(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc)
{
SCTP_TCB_LOCK_ASSERT(stcb);
return (TAILQ_EMPTY(&asoc->ss_data.out.wheel));
}
static void
sctp_ss_default_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
struct sctp_stream_out *strq,
struct sctp_stream_queue_pending *sp SCTP_UNUSED)
{
SCTP_TCB_LOCK_ASSERT(stcb);
if (TAILQ_EMPTY(&strq->outqueue) && strq->ss_params.scheduled) {
if (asoc->ss_data.last_out_stream == strq) {
asoc->ss_data.last_out_stream = TAILQ_PREV(asoc->ss_data.last_out_stream,
sctpwheel_listhead,
ss_params.ss.rr.next_spoke);
if (asoc->ss_data.last_out_stream == NULL) {
asoc->ss_data.last_out_stream = TAILQ_LAST(&asoc->ss_data.out.wheel,
sctpwheel_listhead);
}
if (asoc->ss_data.last_out_stream == strq) {
asoc->ss_data.last_out_stream = NULL;
}
}
if (asoc->ss_data.locked_on_sending == strq) {
asoc->ss_data.locked_on_sending = NULL;
}
TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.ss.rr.next_spoke);
strq->ss_params.scheduled = false;
}
return;
}
static struct sctp_stream_out *
sctp_ss_default_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
struct sctp_association *asoc)
{
struct sctp_stream_out *strq, *strqt;
SCTP_TCB_LOCK_ASSERT(stcb);
if (asoc->ss_data.locked_on_sending != NULL) {
KASSERT(asoc->ss_data.locked_on_sending->ss_params.scheduled,
("locked_on_sending %p not scheduled",
(void *)asoc->ss_data.locked_on_sending));
return (asoc->ss_data.locked_on_sending);
}
strqt = asoc->ss_data.last_out_stream;
KASSERT(strqt == NULL || strqt->ss_params.scheduled,
("last_out_stream %p not scheduled", (void *)strqt));
default_again:
if (strqt == NULL) {
strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
} else {
strq = TAILQ_NEXT(strqt, ss_params.ss.rr.next_spoke);
if (strq == NULL) {
strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
}
}
KASSERT(strq == NULL || strq->ss_params.scheduled,
("strq %p not scheduled", (void *)strq));
if (net != NULL && strq != NULL &&
SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
if (TAILQ_FIRST(&strq->outqueue) &&
TAILQ_FIRST(&strq->outqueue)->net != NULL &&
TAILQ_FIRST(&strq->outqueue)->net != net) {
if (strq == asoc->ss_data.last_out_stream) {
return (NULL);
} else {
strqt = strq;
goto default_again;
}
}
}
return (strq);
}
static void
sctp_ss_default_scheduled(struct sctp_tcb *stcb,
struct sctp_nets *net SCTP_UNUSED,
struct sctp_association *asoc,
struct sctp_stream_out *strq,
int moved_how_much SCTP_UNUSED)
{
struct sctp_stream_queue_pending *sp;
KASSERT(strq != NULL, ("strq is NULL"));
KASSERT(strq->ss_params.scheduled, ("strq %p is not scheduled", (void *)strq));
SCTP_TCB_LOCK_ASSERT(stcb);
asoc->ss_data.last_out_stream = strq;
if (asoc->idata_supported == 0) {
sp = TAILQ_FIRST(&strq->outqueue);
if ((sp != NULL) && (sp->some_taken == 1)) {
asoc->ss_data.locked_on_sending = strq;
} else {
asoc->ss_data.locked_on_sending = NULL;
}
} else {
asoc->ss_data.locked_on_sending = NULL;
}
return;
}
static void
sctp_ss_default_packet_done(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net SCTP_UNUSED,
struct sctp_association *asoc SCTP_UNUSED)
{
SCTP_TCB_LOCK_ASSERT(stcb);
return;
}
static int
sctp_ss_default_get_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED,
struct sctp_stream_out *strq SCTP_UNUSED, uint16_t *value SCTP_UNUSED)
{
SCTP_TCB_LOCK_ASSERT(stcb);
return (-1);
}
static int
sctp_ss_default_set_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED,
struct sctp_stream_out *strq SCTP_UNUSED, uint16_t value SCTP_UNUSED)
{
SCTP_TCB_LOCK_ASSERT(stcb);
return (-1);
}
static bool
sctp_ss_default_is_user_msgs_incomplete(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc)
{
struct sctp_stream_out *strq;
struct sctp_stream_queue_pending *sp;
SCTP_TCB_LOCK_ASSERT(stcb);
if (asoc->stream_queue_cnt != 1) {
return (false);
}
strq = asoc->ss_data.locked_on_sending;
if (strq == NULL) {
return (false);
}
sp = TAILQ_FIRST(&strq->outqueue);
if (sp == NULL) {
return (false);
}
return (sp->msg_is_complete == 0);
}
static void
sctp_ss_rr_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
struct sctp_stream_out *strq,
struct sctp_stream_queue_pending *sp SCTP_UNUSED)
{
struct sctp_stream_out *strqt;
SCTP_TCB_LOCK_ASSERT(stcb);
if (!TAILQ_EMPTY(&strq->outqueue) && !strq->ss_params.scheduled) {
if (TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
TAILQ_INSERT_HEAD(&asoc->ss_data.out.wheel, strq, ss_params.ss.rr.next_spoke);
} else {
strqt = TAILQ_FIRST(&asoc->ss_data.out.wheel);
while (strqt != NULL && (strqt->sid < strq->sid)) {
strqt = TAILQ_NEXT(strqt, ss_params.ss.rr.next_spoke);
}
if (strqt != NULL) {
TAILQ_INSERT_BEFORE(strqt, strq, ss_params.ss.rr.next_spoke);
} else {
TAILQ_INSERT_TAIL(&asoc->ss_data.out.wheel, strq, ss_params.ss.rr.next_spoke);
}
}
strq->ss_params.scheduled = true;
}
return;
}
static struct sctp_stream_out *
sctp_ss_rrp_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net SCTP_UNUSED,
struct sctp_association *asoc)
{
SCTP_TCB_LOCK_ASSERT(stcb);
return (asoc->ss_data.last_out_stream);
}
static void
sctp_ss_rrp_packet_done(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
struct sctp_association *asoc)
{
struct sctp_stream_out *strq, *strqt;
SCTP_TCB_LOCK_ASSERT(stcb);
strqt = asoc->ss_data.last_out_stream;
KASSERT(strqt == NULL || strqt->ss_params.scheduled,
("last_out_stream %p not scheduled", (void *)strqt));
rrp_again:
if (strqt == NULL) {
strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
} else {
strq = TAILQ_NEXT(strqt, ss_params.ss.rr.next_spoke);
if (strq == NULL) {
strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
}
}
KASSERT(strq == NULL || strq->ss_params.scheduled,
("strq %p not scheduled", (void *)strq));
if (net != NULL && strq != NULL &&
SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
if (TAILQ_FIRST(&strq->outqueue) &&
TAILQ_FIRST(&strq->outqueue)->net != NULL &&
TAILQ_FIRST(&strq->outqueue)->net != net) {
if (strq == asoc->ss_data.last_out_stream) {
strq = NULL;
} else {
strqt = strq;
goto rrp_again;
}
}
}
asoc->ss_data.last_out_stream = strq;
return;
}
static void
sctp_ss_prio_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
bool clear_values)
{
SCTP_TCB_LOCK_ASSERT(stcb);
while (!TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
struct sctp_stream_out *strq;
strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
KASSERT(strq->ss_params.scheduled, ("strq %p not scheduled", (void *)strq));
if (clear_values) {
strq->ss_params.ss.prio.priority = 0;
}
TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.ss.prio.next_spoke);
strq->ss_params.scheduled = false;
}
asoc->ss_data.last_out_stream = NULL;
return;
}
static void
sctp_ss_prio_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
{
SCTP_TCB_LOCK_ASSERT(stcb);
if (with_strq != NULL) {
if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
stcb->asoc.ss_data.locked_on_sending = strq;
}
if (stcb->asoc.ss_data.last_out_stream == with_strq) {
stcb->asoc.ss_data.last_out_stream = strq;
}
}
strq->ss_params.scheduled = false;
if (with_strq != NULL) {
strq->ss_params.ss.prio.priority = with_strq->ss_params.ss.prio.priority;
} else {
strq->ss_params.ss.prio.priority = 0;
}
return;
}
static void
sctp_ss_prio_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED)
{
struct sctp_stream_out *strqt;
SCTP_TCB_LOCK_ASSERT(stcb);
if (!TAILQ_EMPTY(&strq->outqueue) && !strq->ss_params.scheduled) {
if (TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
TAILQ_INSERT_HEAD(&asoc->ss_data.out.wheel, strq, ss_params.ss.prio.next_spoke);
} else {
strqt = TAILQ_FIRST(&asoc->ss_data.out.wheel);
while (strqt != NULL && strqt->ss_params.ss.prio.priority < strq->ss_params.ss.prio.priority) {
strqt = TAILQ_NEXT(strqt, ss_params.ss.prio.next_spoke);
}
if (strqt != NULL) {
TAILQ_INSERT_BEFORE(strqt, strq, ss_params.ss.prio.next_spoke);
} else {
TAILQ_INSERT_TAIL(&asoc->ss_data.out.wheel, strq, ss_params.ss.prio.next_spoke);
}
}
strq->ss_params.scheduled = true;
}
return;
}
static void
sctp_ss_prio_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED)
{
SCTP_TCB_LOCK_ASSERT(stcb);
if (TAILQ_EMPTY(&strq->outqueue) && strq->ss_params.scheduled) {
if (asoc->ss_data.last_out_stream == strq) {
asoc->ss_data.last_out_stream = TAILQ_PREV(asoc->ss_data.last_out_stream,
sctpwheel_listhead,
ss_params.ss.prio.next_spoke);
if (asoc->ss_data.last_out_stream == NULL) {
asoc->ss_data.last_out_stream = TAILQ_LAST(&asoc->ss_data.out.wheel,
sctpwheel_listhead);
}
if (asoc->ss_data.last_out_stream == strq) {
asoc->ss_data.last_out_stream = NULL;
}
}
if (asoc->ss_data.locked_on_sending == strq) {
asoc->ss_data.locked_on_sending = NULL;
}
TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.ss.prio.next_spoke);
strq->ss_params.scheduled = false;
}
return;
}
static struct sctp_stream_out *
sctp_ss_prio_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
struct sctp_association *asoc)
{
struct sctp_stream_out *strq, *strqt, *strqn;
SCTP_TCB_LOCK_ASSERT(stcb);
if (asoc->ss_data.locked_on_sending != NULL) {
KASSERT(asoc->ss_data.locked_on_sending->ss_params.scheduled,
("locked_on_sending %p not scheduled",
(void *)asoc->ss_data.locked_on_sending));
return (asoc->ss_data.locked_on_sending);
}
strqt = asoc->ss_data.last_out_stream;
KASSERT(strqt == NULL || strqt->ss_params.scheduled,
("last_out_stream %p not scheduled", (void *)strqt));
prio_again:
if (strqt == NULL) {
strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
} else {
strqn = TAILQ_NEXT(strqt, ss_params.ss.prio.next_spoke);
if (strqn != NULL &&
strqn->ss_params.ss.prio.priority == strqt->ss_params.ss.prio.priority) {
strq = strqn;
} else {
strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
}
}
KASSERT(strq == NULL || strq->ss_params.scheduled,
("strq %p not scheduled", (void *)strq));
if (net != NULL && strq != NULL &&
SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
if (TAILQ_FIRST(&strq->outqueue) &&
TAILQ_FIRST(&strq->outqueue)->net != NULL &&
TAILQ_FIRST(&strq->outqueue)->net != net) {
if (strq == asoc->ss_data.last_out_stream) {
return (NULL);
} else {
strqt = strq;
goto prio_again;
}
}
}
return (strq);
}
static int
sctp_ss_prio_get_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED,
struct sctp_stream_out *strq, uint16_t *value)
{
SCTP_TCB_LOCK_ASSERT(stcb);
if (strq == NULL) {
return (-1);
}
*value = strq->ss_params.ss.prio.priority;
return (1);
}
static int
sctp_ss_prio_set_value(struct sctp_tcb *stcb, struct sctp_association *asoc,
struct sctp_stream_out *strq, uint16_t value)
{
SCTP_TCB_LOCK_ASSERT(stcb);
if (strq == NULL) {
return (-1);
}
strq->ss_params.ss.prio.priority = value;
sctp_ss_prio_remove(stcb, asoc, strq, NULL);
sctp_ss_prio_add(stcb, asoc, strq, NULL);
return (1);
}
static void
sctp_ss_fb_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
bool clear_values)
{
SCTP_TCB_LOCK_ASSERT(stcb);
while (!TAILQ_EMPTY(&asoc->ss_data.out.wheel)) {
struct sctp_stream_out *strq;
strq = TAILQ_FIRST(&asoc->ss_data.out.wheel);
KASSERT(strq->ss_params.scheduled, ("strq %p not scheduled", (void *)strq));
if (clear_values) {
strq->ss_params.ss.fb.rounds = -1;
}
TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.ss.fb.next_spoke);
strq->ss_params.scheduled = false;
}
asoc->ss_data.last_out_stream = NULL;
return;
}
static void
sctp_ss_fb_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
{
SCTP_TCB_LOCK_ASSERT(stcb);
if (with_strq != NULL) {
if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
stcb->asoc.ss_data.locked_on_sending = strq;
}
if (stcb->asoc.ss_data.last_out_stream == with_strq) {
stcb->asoc.ss_data.last_out_stream = strq;
}
}
strq->ss_params.scheduled = false;
if (with_strq != NULL) {
strq->ss_params.ss.fb.rounds = with_strq->ss_params.ss.fb.rounds;
} else {
strq->ss_params.ss.fb.rounds = -1;
}
return;
}
static void
sctp_ss_fb_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED)
{
SCTP_TCB_LOCK_ASSERT(stcb);
if (!TAILQ_EMPTY(&strq->outqueue) && !strq->ss_params.scheduled) {
if (strq->ss_params.ss.fb.rounds < 0)
strq->ss_params.ss.fb.rounds = TAILQ_FIRST(&strq->outqueue)->length;
TAILQ_INSERT_TAIL(&asoc->ss_data.out.wheel, strq, ss_params.ss.fb.next_spoke);
strq->ss_params.scheduled = true;
}
return;
}
static void
sctp_ss_fb_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp SCTP_UNUSED)
{
SCTP_TCB_LOCK_ASSERT(stcb);
if (TAILQ_EMPTY(&strq->outqueue) && strq->ss_params.scheduled) {
if (asoc->ss_data.last_out_stream == strq) {
asoc->ss_data.last_out_stream = TAILQ_PREV(asoc->ss_data.last_out_stream,
sctpwheel_listhead,
ss_params.ss.fb.next_spoke);
if (asoc->ss_data.last_out_stream == NULL) {
asoc->ss_data.last_out_stream = TAILQ_LAST(&asoc->ss_data.out.wheel,
sctpwheel_listhead);
}
if (asoc->ss_data.last_out_stream == strq) {
asoc->ss_data.last_out_stream = NULL;
}
}
if (asoc->ss_data.locked_on_sending == strq) {
asoc->ss_data.locked_on_sending = NULL;
}
TAILQ_REMOVE(&asoc->ss_data.out.wheel, strq, ss_params.ss.fb.next_spoke);
strq->ss_params.scheduled = false;
}
return;
}
static struct sctp_stream_out *
sctp_ss_fb_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
struct sctp_association *asoc)
{
struct sctp_stream_out *strq = NULL, *strqt;
SCTP_TCB_LOCK_ASSERT(stcb);
if (asoc->ss_data.locked_on_sending != NULL) {
KASSERT(asoc->ss_data.locked_on_sending->ss_params.scheduled,
("locked_on_sending %p not scheduled",
(void *)asoc->ss_data.locked_on_sending));
return (asoc->ss_data.locked_on_sending);
}
if (asoc->ss_data.last_out_stream == NULL ||
TAILQ_FIRST(&asoc->ss_data.out.wheel) == TAILQ_LAST(&asoc->ss_data.out.wheel, sctpwheel_listhead)) {
strqt = TAILQ_FIRST(&asoc->ss_data.out.wheel);
} else {
strqt = TAILQ_NEXT(asoc->ss_data.last_out_stream, ss_params.ss.fb.next_spoke);
}
do {
if ((strqt != NULL) &&
((SCTP_BASE_SYSCTL(sctp_cmt_on_off) > 0) ||
(SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0 &&
(net == NULL || (TAILQ_FIRST(&strqt->outqueue) && TAILQ_FIRST(&strqt->outqueue)->net == NULL) ||
(net != NULL && TAILQ_FIRST(&strqt->outqueue) && TAILQ_FIRST(&strqt->outqueue)->net != NULL &&
TAILQ_FIRST(&strqt->outqueue)->net == net))))) {
if ((strqt->ss_params.ss.fb.rounds >= 0) &&
((strq == NULL) ||
(strqt->ss_params.ss.fb.rounds < strq->ss_params.ss.fb.rounds))) {
strq = strqt;
}
}
if (strqt != NULL) {
strqt = TAILQ_NEXT(strqt, ss_params.ss.fb.next_spoke);
} else {
strqt = TAILQ_FIRST(&asoc->ss_data.out.wheel);
}
} while (strqt != strq);
return (strq);
}
static void
sctp_ss_fb_scheduled(struct sctp_tcb *stcb, struct sctp_nets *net SCTP_UNUSED,
struct sctp_association *asoc, struct sctp_stream_out *strq,
int moved_how_much SCTP_UNUSED)
{
struct sctp_stream_queue_pending *sp;
struct sctp_stream_out *strqt;
int subtract;
SCTP_TCB_LOCK_ASSERT(stcb);
if (asoc->idata_supported == 0) {
sp = TAILQ_FIRST(&strq->outqueue);
if ((sp != NULL) && (sp->some_taken == 1)) {
asoc->ss_data.locked_on_sending = strq;
} else {
asoc->ss_data.locked_on_sending = NULL;
}
} else {
asoc->ss_data.locked_on_sending = NULL;
}
subtract = strq->ss_params.ss.fb.rounds;
TAILQ_FOREACH(strqt, &asoc->ss_data.out.wheel, ss_params.ss.fb.next_spoke) {
strqt->ss_params.ss.fb.rounds -= subtract;
if (strqt->ss_params.ss.fb.rounds < 0)
strqt->ss_params.ss.fb.rounds = 0;
}
if (TAILQ_FIRST(&strq->outqueue)) {
strq->ss_params.ss.fb.rounds = TAILQ_FIRST(&strq->outqueue)->length;
} else {
strq->ss_params.ss.fb.rounds = -1;
}
asoc->ss_data.last_out_stream = strq;
return;
}
static void
sctp_ss_fcfs_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
struct sctp_stream_out *strq SCTP_UNUSED,
struct sctp_stream_queue_pending *sp);
static void
sctp_ss_fcfs_init(struct sctp_tcb *stcb, struct sctp_association *asoc)
{
uint32_t x, n = 0, add_more = 1;
struct sctp_stream_queue_pending *sp;
uint16_t i;
SCTP_TCB_LOCK_ASSERT(stcb);
TAILQ_INIT(&asoc->ss_data.out.list);
while (add_more) {
add_more = 0;
for (i = 0; i < asoc->streamoutcnt; i++) {
sp = TAILQ_FIRST(&asoc->strmout[i].outqueue);
x = 0;
while (sp != NULL && x < n) {
sp = TAILQ_NEXT(sp, next);
x++;
}
if (sp != NULL) {
sctp_ss_fcfs_add(stcb, asoc, &asoc->strmout[i], sp);
add_more = 1;
}
}
n++;
}
return;
}
static void
sctp_ss_fcfs_clear(struct sctp_tcb *stcb, struct sctp_association *asoc,
bool clear_values SCTP_UNUSED)
{
struct sctp_stream_queue_pending *sp;
SCTP_TCB_LOCK_ASSERT(stcb);
while (!TAILQ_EMPTY(&asoc->ss_data.out.list)) {
sp = TAILQ_FIRST(&asoc->ss_data.out.list);
KASSERT(sp->scheduled, ("sp %p not scheduled", (void *)sp));
TAILQ_REMOVE(&asoc->ss_data.out.list, sp, ss_next);
sp->scheduled = false;
}
asoc->ss_data.last_out_stream = NULL;
return;
}
static void
sctp_ss_fcfs_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq)
{
SCTP_TCB_LOCK_ASSERT(stcb);
if (with_strq != NULL) {
if (stcb->asoc.ss_data.locked_on_sending == with_strq) {
stcb->asoc.ss_data.locked_on_sending = strq;
}
if (stcb->asoc.ss_data.last_out_stream == with_strq) {
stcb->asoc.ss_data.last_out_stream = strq;
}
}
strq->ss_params.scheduled = false;
return;
}
static void
sctp_ss_fcfs_add(struct sctp_tcb *stcb, struct sctp_association *asoc,
struct sctp_stream_out *strq SCTP_UNUSED, struct sctp_stream_queue_pending *sp)
{
SCTP_TCB_LOCK_ASSERT(stcb);
if (!sp->scheduled) {
TAILQ_INSERT_TAIL(&asoc->ss_data.out.list, sp, ss_next);
sp->scheduled = true;
}
return;
}
static bool
sctp_ss_fcfs_is_empty(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc)
{
SCTP_TCB_LOCK_ASSERT(stcb);
return (TAILQ_EMPTY(&asoc->ss_data.out.list));
}
static void
sctp_ss_fcfs_remove(struct sctp_tcb *stcb, struct sctp_association *asoc,
struct sctp_stream_out *strq SCTP_UNUSED, struct sctp_stream_queue_pending *sp)
{
SCTP_TCB_LOCK_ASSERT(stcb);
if (sp->scheduled) {
TAILQ_REMOVE(&asoc->ss_data.out.list, sp, ss_next);
sp->scheduled = false;
}
return;
}
static struct sctp_stream_out *
sctp_ss_fcfs_select(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_nets *net,
struct sctp_association *asoc)
{
struct sctp_stream_out *strq;
struct sctp_stream_queue_pending *sp;
SCTP_TCB_LOCK_ASSERT(stcb);
if (asoc->ss_data.locked_on_sending) {
return (asoc->ss_data.locked_on_sending);
}
sp = TAILQ_FIRST(&asoc->ss_data.out.list);
default_again:
if (sp != NULL) {
strq = &asoc->strmout[sp->sid];
} else {
strq = NULL;
}
if (net != NULL && strq != NULL &&
SCTP_BASE_SYSCTL(sctp_cmt_on_off) == 0) {
if (TAILQ_FIRST(&strq->outqueue) &&
TAILQ_FIRST(&strq->outqueue)->net != NULL &&
TAILQ_FIRST(&strq->outqueue)->net != net) {
sp = TAILQ_NEXT(sp, ss_next);
goto default_again;
}
}
return (strq);
}
static void
sctp_ss_fcfs_scheduled(struct sctp_tcb *stcb,
struct sctp_nets *net SCTP_UNUSED,
struct sctp_association *asoc,
struct sctp_stream_out *strq,
int moved_how_much SCTP_UNUSED)
{
struct sctp_stream_queue_pending *sp;
KASSERT(strq != NULL, ("strq is NULL"));
asoc->ss_data.last_out_stream = strq;
if (asoc->idata_supported == 0) {
sp = TAILQ_FIRST(&strq->outqueue);
if ((sp != NULL) && (sp->some_taken == 1)) {
asoc->ss_data.locked_on_sending = strq;
} else {
asoc->ss_data.locked_on_sending = NULL;
}
} else {
asoc->ss_data.locked_on_sending = NULL;
}
return;
}
const struct sctp_ss_functions sctp_ss_functions[] = {
{
.sctp_ss_init = sctp_ss_default_init,
.sctp_ss_clear = sctp_ss_default_clear,
.sctp_ss_init_stream = sctp_ss_default_init_stream,
.sctp_ss_add_to_stream = sctp_ss_default_add,
.sctp_ss_is_empty = sctp_ss_default_is_empty,
.sctp_ss_remove_from_stream = sctp_ss_default_remove,
.sctp_ss_select_stream = sctp_ss_default_select,
.sctp_ss_scheduled = sctp_ss_default_scheduled,
.sctp_ss_packet_done = sctp_ss_default_packet_done,
.sctp_ss_get_value = sctp_ss_default_get_value,
.sctp_ss_set_value = sctp_ss_default_set_value,
.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
},
{
.sctp_ss_init = sctp_ss_default_init,
.sctp_ss_clear = sctp_ss_default_clear,
.sctp_ss_init_stream = sctp_ss_default_init_stream,
.sctp_ss_add_to_stream = sctp_ss_rr_add,
.sctp_ss_is_empty = sctp_ss_default_is_empty,
.sctp_ss_remove_from_stream = sctp_ss_default_remove,
.sctp_ss_select_stream = sctp_ss_default_select,
.sctp_ss_scheduled = sctp_ss_default_scheduled,
.sctp_ss_packet_done = sctp_ss_default_packet_done,
.sctp_ss_get_value = sctp_ss_default_get_value,
.sctp_ss_set_value = sctp_ss_default_set_value,
.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
},
{
.sctp_ss_init = sctp_ss_default_init,
.sctp_ss_clear = sctp_ss_default_clear,
.sctp_ss_init_stream = sctp_ss_default_init_stream,
.sctp_ss_add_to_stream = sctp_ss_rr_add,
.sctp_ss_is_empty = sctp_ss_default_is_empty,
.sctp_ss_remove_from_stream = sctp_ss_default_remove,
.sctp_ss_select_stream = sctp_ss_rrp_select,
.sctp_ss_scheduled = sctp_ss_default_scheduled,
.sctp_ss_packet_done = sctp_ss_rrp_packet_done,
.sctp_ss_get_value = sctp_ss_default_get_value,
.sctp_ss_set_value = sctp_ss_default_set_value,
.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
},
{
.sctp_ss_init = sctp_ss_default_init,
.sctp_ss_clear = sctp_ss_prio_clear,
.sctp_ss_init_stream = sctp_ss_prio_init_stream,
.sctp_ss_add_to_stream = sctp_ss_prio_add,
.sctp_ss_is_empty = sctp_ss_default_is_empty,
.sctp_ss_remove_from_stream = sctp_ss_prio_remove,
.sctp_ss_select_stream = sctp_ss_prio_select,
.sctp_ss_scheduled = sctp_ss_default_scheduled,
.sctp_ss_packet_done = sctp_ss_default_packet_done,
.sctp_ss_get_value = sctp_ss_prio_get_value,
.sctp_ss_set_value = sctp_ss_prio_set_value,
.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
},
{
.sctp_ss_init = sctp_ss_default_init,
.sctp_ss_clear = sctp_ss_fb_clear,
.sctp_ss_init_stream = sctp_ss_fb_init_stream,
.sctp_ss_add_to_stream = sctp_ss_fb_add,
.sctp_ss_is_empty = sctp_ss_default_is_empty,
.sctp_ss_remove_from_stream = sctp_ss_fb_remove,
.sctp_ss_select_stream = sctp_ss_fb_select,
.sctp_ss_scheduled = sctp_ss_fb_scheduled,
.sctp_ss_packet_done = sctp_ss_default_packet_done,
.sctp_ss_get_value = sctp_ss_default_get_value,
.sctp_ss_set_value = sctp_ss_default_set_value,
.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
},
{
.sctp_ss_init = sctp_ss_fcfs_init,
.sctp_ss_clear = sctp_ss_fcfs_clear,
.sctp_ss_init_stream = sctp_ss_fcfs_init_stream,
.sctp_ss_add_to_stream = sctp_ss_fcfs_add,
.sctp_ss_is_empty = sctp_ss_fcfs_is_empty,
.sctp_ss_remove_from_stream = sctp_ss_fcfs_remove,
.sctp_ss_select_stream = sctp_ss_fcfs_select,
.sctp_ss_scheduled = sctp_ss_fcfs_scheduled,
.sctp_ss_packet_done = sctp_ss_default_packet_done,
.sctp_ss_get_value = sctp_ss_default_get_value,
.sctp_ss_set_value = sctp_ss_default_set_value,
.sctp_ss_is_user_msgs_incomplete = sctp_ss_default_is_user_msgs_incomplete
}
};