147505b8bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
25bbbbe32SMarcelo Ricardo Leitner /* SCTP kernel implementation
35bbbbe32SMarcelo Ricardo Leitner * (C) Copyright Red Hat Inc. 2017
45bbbbe32SMarcelo Ricardo Leitner *
55bbbbe32SMarcelo Ricardo Leitner * This file is part of the SCTP kernel implementation
65bbbbe32SMarcelo Ricardo Leitner *
75bbbbe32SMarcelo Ricardo Leitner * These functions manipulate sctp stream queue/scheduling.
85bbbbe32SMarcelo Ricardo Leitner *
95bbbbe32SMarcelo Ricardo Leitner * Please send any bug reports or fixes you make to the
105bbbbe32SMarcelo Ricardo Leitner * email addresched(es):
115bbbbe32SMarcelo Ricardo Leitner * lksctp developers <linux-sctp@vger.kernel.org>
125bbbbe32SMarcelo Ricardo Leitner *
135bbbbe32SMarcelo Ricardo Leitner * Written or modified by:
145bbbbe32SMarcelo Ricardo Leitner * Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
155bbbbe32SMarcelo Ricardo Leitner */
165bbbbe32SMarcelo Ricardo Leitner
175bbbbe32SMarcelo Ricardo Leitner #include <linux/list.h>
185bbbbe32SMarcelo Ricardo Leitner #include <net/sctp/sctp.h>
195bbbbe32SMarcelo Ricardo Leitner #include <net/sctp/sm.h>
205bbbbe32SMarcelo Ricardo Leitner #include <net/sctp/stream_sched.h>
215bbbbe32SMarcelo Ricardo Leitner
225bbbbe32SMarcelo Ricardo Leitner /* First Come First Serve (a.k.a. FIFO)
235bbbbe32SMarcelo Ricardo Leitner * RFC DRAFT ndata Section 3.1
245bbbbe32SMarcelo Ricardo Leitner */
sctp_sched_fcfs_set(struct sctp_stream * stream,__u16 sid,__u16 value,gfp_t gfp)255bbbbe32SMarcelo Ricardo Leitner static int sctp_sched_fcfs_set(struct sctp_stream *stream, __u16 sid,
265bbbbe32SMarcelo Ricardo Leitner __u16 value, gfp_t gfp)
275bbbbe32SMarcelo Ricardo Leitner {
285bbbbe32SMarcelo Ricardo Leitner return 0;
295bbbbe32SMarcelo Ricardo Leitner }
305bbbbe32SMarcelo Ricardo Leitner
sctp_sched_fcfs_get(struct sctp_stream * stream,__u16 sid,__u16 * value)315bbbbe32SMarcelo Ricardo Leitner static int sctp_sched_fcfs_get(struct sctp_stream *stream, __u16 sid,
325bbbbe32SMarcelo Ricardo Leitner __u16 *value)
335bbbbe32SMarcelo Ricardo Leitner {
345bbbbe32SMarcelo Ricardo Leitner *value = 0;
355bbbbe32SMarcelo Ricardo Leitner return 0;
365bbbbe32SMarcelo Ricardo Leitner }
375bbbbe32SMarcelo Ricardo Leitner
sctp_sched_fcfs_init(struct sctp_stream * stream)385bbbbe32SMarcelo Ricardo Leitner static int sctp_sched_fcfs_init(struct sctp_stream *stream)
395bbbbe32SMarcelo Ricardo Leitner {
405bbbbe32SMarcelo Ricardo Leitner return 0;
415bbbbe32SMarcelo Ricardo Leitner }
425bbbbe32SMarcelo Ricardo Leitner
sctp_sched_fcfs_init_sid(struct sctp_stream * stream,__u16 sid,gfp_t gfp)435bbbbe32SMarcelo Ricardo Leitner static int sctp_sched_fcfs_init_sid(struct sctp_stream *stream, __u16 sid,
445bbbbe32SMarcelo Ricardo Leitner gfp_t gfp)
455bbbbe32SMarcelo Ricardo Leitner {
465bbbbe32SMarcelo Ricardo Leitner return 0;
475bbbbe32SMarcelo Ricardo Leitner }
485bbbbe32SMarcelo Ricardo Leitner
sctp_sched_fcfs_free_sid(struct sctp_stream * stream,__u16 sid)499ed7bfc7SZhengchao Shao static void sctp_sched_fcfs_free_sid(struct sctp_stream *stream, __u16 sid)
509ed7bfc7SZhengchao Shao {
519ed7bfc7SZhengchao Shao }
529ed7bfc7SZhengchao Shao
sctp_sched_fcfs_enqueue(struct sctp_outq * q,struct sctp_datamsg * msg)535bbbbe32SMarcelo Ricardo Leitner static void sctp_sched_fcfs_enqueue(struct sctp_outq *q,
545bbbbe32SMarcelo Ricardo Leitner struct sctp_datamsg *msg)
555bbbbe32SMarcelo Ricardo Leitner {
565bbbbe32SMarcelo Ricardo Leitner }
575bbbbe32SMarcelo Ricardo Leitner
sctp_sched_fcfs_dequeue(struct sctp_outq * q)585bbbbe32SMarcelo Ricardo Leitner static struct sctp_chunk *sctp_sched_fcfs_dequeue(struct sctp_outq *q)
595bbbbe32SMarcelo Ricardo Leitner {
605bbbbe32SMarcelo Ricardo Leitner struct sctp_stream *stream = &q->asoc->stream;
615bbbbe32SMarcelo Ricardo Leitner struct sctp_chunk *ch = NULL;
625bbbbe32SMarcelo Ricardo Leitner struct list_head *entry;
635bbbbe32SMarcelo Ricardo Leitner
645bbbbe32SMarcelo Ricardo Leitner if (list_empty(&q->out_chunk_list))
655bbbbe32SMarcelo Ricardo Leitner goto out;
665bbbbe32SMarcelo Ricardo Leitner
675bbbbe32SMarcelo Ricardo Leitner if (stream->out_curr) {
685bbbbe32SMarcelo Ricardo Leitner ch = list_entry(stream->out_curr->ext->outq.next,
695bbbbe32SMarcelo Ricardo Leitner struct sctp_chunk, stream_list);
705bbbbe32SMarcelo Ricardo Leitner } else {
715bbbbe32SMarcelo Ricardo Leitner entry = q->out_chunk_list.next;
725bbbbe32SMarcelo Ricardo Leitner ch = list_entry(entry, struct sctp_chunk, list);
735bbbbe32SMarcelo Ricardo Leitner }
745bbbbe32SMarcelo Ricardo Leitner
755bbbbe32SMarcelo Ricardo Leitner sctp_sched_dequeue_common(q, ch);
765bbbbe32SMarcelo Ricardo Leitner
775bbbbe32SMarcelo Ricardo Leitner out:
785bbbbe32SMarcelo Ricardo Leitner return ch;
795bbbbe32SMarcelo Ricardo Leitner }
805bbbbe32SMarcelo Ricardo Leitner
sctp_sched_fcfs_dequeue_done(struct sctp_outq * q,struct sctp_chunk * chunk)815bbbbe32SMarcelo Ricardo Leitner static void sctp_sched_fcfs_dequeue_done(struct sctp_outq *q,
825bbbbe32SMarcelo Ricardo Leitner struct sctp_chunk *chunk)
835bbbbe32SMarcelo Ricardo Leitner {
845bbbbe32SMarcelo Ricardo Leitner }
855bbbbe32SMarcelo Ricardo Leitner
sctp_sched_fcfs_sched_all(struct sctp_stream * stream)865bbbbe32SMarcelo Ricardo Leitner static void sctp_sched_fcfs_sched_all(struct sctp_stream *stream)
875bbbbe32SMarcelo Ricardo Leitner {
885bbbbe32SMarcelo Ricardo Leitner }
895bbbbe32SMarcelo Ricardo Leitner
sctp_sched_fcfs_unsched_all(struct sctp_stream * stream)905bbbbe32SMarcelo Ricardo Leitner static void sctp_sched_fcfs_unsched_all(struct sctp_stream *stream)
915bbbbe32SMarcelo Ricardo Leitner {
925bbbbe32SMarcelo Ricardo Leitner }
935bbbbe32SMarcelo Ricardo Leitner
945bbbbe32SMarcelo Ricardo Leitner static struct sctp_sched_ops sctp_sched_fcfs = {
955bbbbe32SMarcelo Ricardo Leitner .set = sctp_sched_fcfs_set,
965bbbbe32SMarcelo Ricardo Leitner .get = sctp_sched_fcfs_get,
975bbbbe32SMarcelo Ricardo Leitner .init = sctp_sched_fcfs_init,
985bbbbe32SMarcelo Ricardo Leitner .init_sid = sctp_sched_fcfs_init_sid,
999ed7bfc7SZhengchao Shao .free_sid = sctp_sched_fcfs_free_sid,
1005bbbbe32SMarcelo Ricardo Leitner .enqueue = sctp_sched_fcfs_enqueue,
1015bbbbe32SMarcelo Ricardo Leitner .dequeue = sctp_sched_fcfs_dequeue,
1025bbbbe32SMarcelo Ricardo Leitner .dequeue_done = sctp_sched_fcfs_dequeue_done,
1035bbbbe32SMarcelo Ricardo Leitner .sched_all = sctp_sched_fcfs_sched_all,
1045bbbbe32SMarcelo Ricardo Leitner .unsched_all = sctp_sched_fcfs_unsched_all,
1055bbbbe32SMarcelo Ricardo Leitner };
1065bbbbe32SMarcelo Ricardo Leitner
sctp_sched_ops_fcfs_init(void)1071ba896f6SXin Long static void sctp_sched_ops_fcfs_init(void)
1081ba896f6SXin Long {
1091ba896f6SXin Long sctp_sched_ops_register(SCTP_SS_FCFS, &sctp_sched_fcfs);
1101ba896f6SXin Long }
1111ba896f6SXin Long
1125bbbbe32SMarcelo Ricardo Leitner /* API to other parts of the stack */
1135bbbbe32SMarcelo Ricardo Leitner
1141ba896f6SXin Long static struct sctp_sched_ops *sctp_sched_ops[SCTP_SS_MAX + 1];
115637784adSMarcelo Ricardo Leitner
sctp_sched_ops_register(enum sctp_sched_type sched,struct sctp_sched_ops * sched_ops)1161ba896f6SXin Long void sctp_sched_ops_register(enum sctp_sched_type sched,
1171ba896f6SXin Long struct sctp_sched_ops *sched_ops)
1181ba896f6SXin Long {
1191ba896f6SXin Long sctp_sched_ops[sched] = sched_ops;
1201ba896f6SXin Long }
1211ba896f6SXin Long
sctp_sched_ops_init(void)1221ba896f6SXin Long void sctp_sched_ops_init(void)
1231ba896f6SXin Long {
1241ba896f6SXin Long sctp_sched_ops_fcfs_init();
1251ba896f6SXin Long sctp_sched_ops_prio_init();
1261ba896f6SXin Long sctp_sched_ops_rr_init();
1274821a076SXin Long sctp_sched_ops_fc_init();
128*42d452e7SXin Long sctp_sched_ops_wfq_init();
1291ba896f6SXin Long }
1305bbbbe32SMarcelo Ricardo Leitner
sctp_sched_free_sched(struct sctp_stream * stream)1317d802c80SXin Long static void sctp_sched_free_sched(struct sctp_stream *stream)
1327d802c80SXin Long {
1337d802c80SXin Long struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream);
1347d802c80SXin Long struct sctp_stream_out_ext *soute;
1357d802c80SXin Long int i;
1367d802c80SXin Long
1377d802c80SXin Long sched->unsched_all(stream);
1387d802c80SXin Long for (i = 0; i < stream->outcnt; i++) {
1397d802c80SXin Long soute = SCTP_SO(stream, i)->ext;
1407d802c80SXin Long if (!soute)
1417d802c80SXin Long continue;
1427d802c80SXin Long sched->free_sid(stream, i);
1437d802c80SXin Long /* Give the next scheduler a clean slate. */
1447d802c80SXin Long memset_after(soute, 0, outq);
1457d802c80SXin Long }
1467d802c80SXin Long }
1477d802c80SXin Long
sctp_sched_set_sched(struct sctp_association * asoc,enum sctp_sched_type sched)1485bbbbe32SMarcelo Ricardo Leitner int sctp_sched_set_sched(struct sctp_association *asoc,
1495bbbbe32SMarcelo Ricardo Leitner enum sctp_sched_type sched)
1505bbbbe32SMarcelo Ricardo Leitner {
1515bbbbe32SMarcelo Ricardo Leitner struct sctp_sched_ops *old = asoc->outqueue.sched;
1525bbbbe32SMarcelo Ricardo Leitner struct sctp_datamsg *msg = NULL;
1535bbbbe32SMarcelo Ricardo Leitner struct sctp_sched_ops *n;
1545bbbbe32SMarcelo Ricardo Leitner struct sctp_chunk *ch;
1555bbbbe32SMarcelo Ricardo Leitner int i, ret = 0;
1565bbbbe32SMarcelo Ricardo Leitner
1575bbbbe32SMarcelo Ricardo Leitner if (sched > SCTP_SS_MAX)
1585bbbbe32SMarcelo Ricardo Leitner return -EINVAL;
1595bbbbe32SMarcelo Ricardo Leitner
1605bbbbe32SMarcelo Ricardo Leitner n = sctp_sched_ops[sched];
1615bbbbe32SMarcelo Ricardo Leitner if (old == n)
1625bbbbe32SMarcelo Ricardo Leitner return ret;
1637d802c80SXin Long
1647d802c80SXin Long if (old)
1655bbbbe32SMarcelo Ricardo Leitner sctp_sched_free_sched(&asoc->stream);
1665bbbbe32SMarcelo Ricardo Leitner
1675bbbbe32SMarcelo Ricardo Leitner asoc->outqueue.sched = n;
1685bbbbe32SMarcelo Ricardo Leitner n->init(&asoc->stream);
16905364ca0SKonstantin Khorenko for (i = 0; i < asoc->stream.outcnt; i++) {
1705bbbbe32SMarcelo Ricardo Leitner if (!SCTP_SO(&asoc->stream, i)->ext)
1715bbbbe32SMarcelo Ricardo Leitner continue;
172b89fc26fSDuoming Zhou
1735bbbbe32SMarcelo Ricardo Leitner ret = n->init_sid(&asoc->stream, i, GFP_ATOMIC);
1745bbbbe32SMarcelo Ricardo Leitner if (ret)
1755bbbbe32SMarcelo Ricardo Leitner goto err;
1765bbbbe32SMarcelo Ricardo Leitner }
1775bbbbe32SMarcelo Ricardo Leitner
1785bbbbe32SMarcelo Ricardo Leitner /* We have to requeue all chunks already queued. */
1795bbbbe32SMarcelo Ricardo Leitner list_for_each_entry(ch, &asoc->outqueue.out_chunk_list, list) {
1805bbbbe32SMarcelo Ricardo Leitner if (ch->msg == msg)
1815bbbbe32SMarcelo Ricardo Leitner continue;
1825bbbbe32SMarcelo Ricardo Leitner msg = ch->msg;
1835bbbbe32SMarcelo Ricardo Leitner n->enqueue(&asoc->outqueue, msg);
1845bbbbe32SMarcelo Ricardo Leitner }
1855bbbbe32SMarcelo Ricardo Leitner
1865bbbbe32SMarcelo Ricardo Leitner return ret;
1875bbbbe32SMarcelo Ricardo Leitner
1887d802c80SXin Long err:
1895bbbbe32SMarcelo Ricardo Leitner sctp_sched_free_sched(&asoc->stream);
1905bbbbe32SMarcelo Ricardo Leitner asoc->outqueue.sched = &sctp_sched_fcfs; /* Always safe */
1915bbbbe32SMarcelo Ricardo Leitner
1925bbbbe32SMarcelo Ricardo Leitner return ret;
1935bbbbe32SMarcelo Ricardo Leitner }
1945bbbbe32SMarcelo Ricardo Leitner
sctp_sched_get_sched(struct sctp_association * asoc)1955bbbbe32SMarcelo Ricardo Leitner int sctp_sched_get_sched(struct sctp_association *asoc)
1965bbbbe32SMarcelo Ricardo Leitner {
1975bbbbe32SMarcelo Ricardo Leitner int i;
1985bbbbe32SMarcelo Ricardo Leitner
1995bbbbe32SMarcelo Ricardo Leitner for (i = 0; i <= SCTP_SS_MAX; i++)
2005bbbbe32SMarcelo Ricardo Leitner if (asoc->outqueue.sched == sctp_sched_ops[i])
2015bbbbe32SMarcelo Ricardo Leitner return i;
2025bbbbe32SMarcelo Ricardo Leitner
2035bbbbe32SMarcelo Ricardo Leitner return 0;
2045bbbbe32SMarcelo Ricardo Leitner }
2055bbbbe32SMarcelo Ricardo Leitner
sctp_sched_set_value(struct sctp_association * asoc,__u16 sid,__u16 value,gfp_t gfp)2065bbbbe32SMarcelo Ricardo Leitner int sctp_sched_set_value(struct sctp_association *asoc, __u16 sid,
2075bbbbe32SMarcelo Ricardo Leitner __u16 value, gfp_t gfp)
2085bbbbe32SMarcelo Ricardo Leitner {
2095bbbbe32SMarcelo Ricardo Leitner if (sid >= asoc->stream.outcnt)
2105bbbbe32SMarcelo Ricardo Leitner return -EINVAL;
21105364ca0SKonstantin Khorenko
2125bbbbe32SMarcelo Ricardo Leitner if (!SCTP_SO(&asoc->stream, sid)->ext) {
2135bbbbe32SMarcelo Ricardo Leitner int ret;
2145bbbbe32SMarcelo Ricardo Leitner
2155bbbbe32SMarcelo Ricardo Leitner ret = sctp_stream_init_ext(&asoc->stream, sid);
2165bbbbe32SMarcelo Ricardo Leitner if (ret)
2175bbbbe32SMarcelo Ricardo Leitner return ret;
2185bbbbe32SMarcelo Ricardo Leitner }
2195bbbbe32SMarcelo Ricardo Leitner
2205bbbbe32SMarcelo Ricardo Leitner return asoc->outqueue.sched->set(&asoc->stream, sid, value, gfp);
2215bbbbe32SMarcelo Ricardo Leitner }
2225bbbbe32SMarcelo Ricardo Leitner
sctp_sched_get_value(struct sctp_association * asoc,__u16 sid,__u16 * value)2235bbbbe32SMarcelo Ricardo Leitner int sctp_sched_get_value(struct sctp_association *asoc, __u16 sid,
2245bbbbe32SMarcelo Ricardo Leitner __u16 *value)
2255bbbbe32SMarcelo Ricardo Leitner {
2265bbbbe32SMarcelo Ricardo Leitner if (sid >= asoc->stream.outcnt)
2275bbbbe32SMarcelo Ricardo Leitner return -EINVAL;
22805364ca0SKonstantin Khorenko
2295bbbbe32SMarcelo Ricardo Leitner if (!SCTP_SO(&asoc->stream, sid)->ext)
2305bbbbe32SMarcelo Ricardo Leitner return 0;
2315bbbbe32SMarcelo Ricardo Leitner
2325bbbbe32SMarcelo Ricardo Leitner return asoc->outqueue.sched->get(&asoc->stream, sid, value);
2335bbbbe32SMarcelo Ricardo Leitner }
2345bbbbe32SMarcelo Ricardo Leitner
sctp_sched_dequeue_done(struct sctp_outq * q,struct sctp_chunk * ch)2355bbbbe32SMarcelo Ricardo Leitner void sctp_sched_dequeue_done(struct sctp_outq *q, struct sctp_chunk *ch)
236ef4775e3SXin Long {
237da1f6d4dSXin Long if (!list_is_last(&ch->frag_list, &ch->msg->chunks) &&
2385bbbbe32SMarcelo Ricardo Leitner !q->asoc->peer.intl_capable) {
2395bbbbe32SMarcelo Ricardo Leitner struct sctp_stream_out *sout;
2405bbbbe32SMarcelo Ricardo Leitner __u16 sid;
2415bbbbe32SMarcelo Ricardo Leitner
2425bbbbe32SMarcelo Ricardo Leitner /* datamsg is not finish, so save it as current one,
2435bbbbe32SMarcelo Ricardo Leitner * in case application switch scheduler or a higher
2445bbbbe32SMarcelo Ricardo Leitner * priority stream comes in.
2455bbbbe32SMarcelo Ricardo Leitner */
24605364ca0SKonstantin Khorenko sid = sctp_chunk_stream_no(ch);
2475bbbbe32SMarcelo Ricardo Leitner sout = SCTP_SO(&q->asoc->stream, sid);
2485bbbbe32SMarcelo Ricardo Leitner q->asoc->stream.out_curr = sout;
2495bbbbe32SMarcelo Ricardo Leitner return;
2505bbbbe32SMarcelo Ricardo Leitner }
2515bbbbe32SMarcelo Ricardo Leitner
2525bbbbe32SMarcelo Ricardo Leitner q->asoc->stream.out_curr = NULL;
2535bbbbe32SMarcelo Ricardo Leitner q->sched->dequeue_done(q, ch);
2545bbbbe32SMarcelo Ricardo Leitner }
2555bbbbe32SMarcelo Ricardo Leitner
2565bbbbe32SMarcelo Ricardo Leitner /* Auxiliary functions for the schedulers */
sctp_sched_dequeue_common(struct sctp_outq * q,struct sctp_chunk * ch)2575bbbbe32SMarcelo Ricardo Leitner void sctp_sched_dequeue_common(struct sctp_outq *q, struct sctp_chunk *ch)
2585bbbbe32SMarcelo Ricardo Leitner {
2595bbbbe32SMarcelo Ricardo Leitner list_del_init(&ch->list);
2605bbbbe32SMarcelo Ricardo Leitner list_del_init(&ch->stream_list);
2615bbbbe32SMarcelo Ricardo Leitner q->out_qlen -= ch->skb->len;
2625bbbbe32SMarcelo Ricardo Leitner }
2635bbbbe32SMarcelo Ricardo Leitner
sctp_sched_init_sid(struct sctp_stream * stream,__u16 sid,gfp_t gfp)2645bbbbe32SMarcelo Ricardo Leitner int sctp_sched_init_sid(struct sctp_stream *stream, __u16 sid, gfp_t gfp)
2655bbbbe32SMarcelo Ricardo Leitner {
26605364ca0SKonstantin Khorenko struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream);
2675bbbbe32SMarcelo Ricardo Leitner struct sctp_stream_out_ext *ext = SCTP_SO(stream, sid)->ext;
26805364ca0SKonstantin Khorenko
2695bbbbe32SMarcelo Ricardo Leitner INIT_LIST_HEAD(&ext->outq);
2705bbbbe32SMarcelo Ricardo Leitner return sched->init_sid(stream, sid, gfp);
2715bbbbe32SMarcelo Ricardo Leitner }
2725bbbbe32SMarcelo Ricardo Leitner
sctp_sched_ops_from_stream(struct sctp_stream * stream)2735bbbbe32SMarcelo Ricardo Leitner struct sctp_sched_ops *sctp_sched_ops_from_stream(struct sctp_stream *stream)
2745bbbbe32SMarcelo Ricardo Leitner {
2755bbbbe32SMarcelo Ricardo Leitner struct sctp_association *asoc;
2765bbbbe32SMarcelo Ricardo Leitner
2775bbbbe32SMarcelo Ricardo Leitner asoc = container_of(stream, struct sctp_association, stream);
2785bbbbe32SMarcelo Ricardo Leitner
2795bbbbe32SMarcelo Ricardo Leitner return asoc->outqueue.sched;
280 }
281