18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* SCTP kernel implementation
38c2ecf20Sopenharmony_ci * (C) Copyright Red Hat Inc. 2017
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This file is part of the SCTP kernel implementation
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * These functions manipulate sctp stream queue/scheduling.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Please send any bug reports or fixes you make to the
108c2ecf20Sopenharmony_ci * email addresched(es):
118c2ecf20Sopenharmony_ci *    lksctp developers <linux-sctp@vger.kernel.org>
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * Written or modified by:
148c2ecf20Sopenharmony_ci *    Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/list.h>
188c2ecf20Sopenharmony_ci#include <net/sctp/sctp.h>
198c2ecf20Sopenharmony_ci#include <net/sctp/sm.h>
208c2ecf20Sopenharmony_ci#include <net/sctp/stream_sched.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/* First Come First Serve (a.k.a. FIFO)
238c2ecf20Sopenharmony_ci * RFC DRAFT ndata Section 3.1
248c2ecf20Sopenharmony_ci */
258c2ecf20Sopenharmony_cistatic int sctp_sched_fcfs_set(struct sctp_stream *stream, __u16 sid,
268c2ecf20Sopenharmony_ci			       __u16 value, gfp_t gfp)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	return 0;
298c2ecf20Sopenharmony_ci}
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic int sctp_sched_fcfs_get(struct sctp_stream *stream, __u16 sid,
328c2ecf20Sopenharmony_ci			       __u16 *value)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	*value = 0;
358c2ecf20Sopenharmony_ci	return 0;
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic int sctp_sched_fcfs_init(struct sctp_stream *stream)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	return 0;
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic int sctp_sched_fcfs_init_sid(struct sctp_stream *stream, __u16 sid,
448c2ecf20Sopenharmony_ci				    gfp_t gfp)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	return 0;
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic void sctp_sched_fcfs_free_sid(struct sctp_stream *stream, __u16 sid)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic void sctp_sched_fcfs_free(struct sctp_stream *stream)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic void sctp_sched_fcfs_enqueue(struct sctp_outq *q,
588c2ecf20Sopenharmony_ci				    struct sctp_datamsg *msg)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic struct sctp_chunk *sctp_sched_fcfs_dequeue(struct sctp_outq *q)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	struct sctp_stream *stream = &q->asoc->stream;
658c2ecf20Sopenharmony_ci	struct sctp_chunk *ch = NULL;
668c2ecf20Sopenharmony_ci	struct list_head *entry;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	if (list_empty(&q->out_chunk_list))
698c2ecf20Sopenharmony_ci		goto out;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	if (stream->out_curr) {
728c2ecf20Sopenharmony_ci		ch = list_entry(stream->out_curr->ext->outq.next,
738c2ecf20Sopenharmony_ci				struct sctp_chunk, stream_list);
748c2ecf20Sopenharmony_ci	} else {
758c2ecf20Sopenharmony_ci		entry = q->out_chunk_list.next;
768c2ecf20Sopenharmony_ci		ch = list_entry(entry, struct sctp_chunk, list);
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	sctp_sched_dequeue_common(q, ch);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ciout:
828c2ecf20Sopenharmony_ci	return ch;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic void sctp_sched_fcfs_dequeue_done(struct sctp_outq *q,
868c2ecf20Sopenharmony_ci					 struct sctp_chunk *chunk)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic void sctp_sched_fcfs_sched_all(struct sctp_stream *stream)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic void sctp_sched_fcfs_unsched_all(struct sctp_stream *stream)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic struct sctp_sched_ops sctp_sched_fcfs = {
998c2ecf20Sopenharmony_ci	.set = sctp_sched_fcfs_set,
1008c2ecf20Sopenharmony_ci	.get = sctp_sched_fcfs_get,
1018c2ecf20Sopenharmony_ci	.init = sctp_sched_fcfs_init,
1028c2ecf20Sopenharmony_ci	.init_sid = sctp_sched_fcfs_init_sid,
1038c2ecf20Sopenharmony_ci	.free_sid = sctp_sched_fcfs_free_sid,
1048c2ecf20Sopenharmony_ci	.free = sctp_sched_fcfs_free,
1058c2ecf20Sopenharmony_ci	.enqueue = sctp_sched_fcfs_enqueue,
1068c2ecf20Sopenharmony_ci	.dequeue = sctp_sched_fcfs_dequeue,
1078c2ecf20Sopenharmony_ci	.dequeue_done = sctp_sched_fcfs_dequeue_done,
1088c2ecf20Sopenharmony_ci	.sched_all = sctp_sched_fcfs_sched_all,
1098c2ecf20Sopenharmony_ci	.unsched_all = sctp_sched_fcfs_unsched_all,
1108c2ecf20Sopenharmony_ci};
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic void sctp_sched_ops_fcfs_init(void)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	sctp_sched_ops_register(SCTP_SS_FCFS, &sctp_sched_fcfs);
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci/* API to other parts of the stack */
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic struct sctp_sched_ops *sctp_sched_ops[SCTP_SS_MAX + 1];
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_civoid sctp_sched_ops_register(enum sctp_sched_type sched,
1228c2ecf20Sopenharmony_ci			     struct sctp_sched_ops *sched_ops)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	sctp_sched_ops[sched] = sched_ops;
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_civoid sctp_sched_ops_init(void)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	sctp_sched_ops_fcfs_init();
1308c2ecf20Sopenharmony_ci	sctp_sched_ops_prio_init();
1318c2ecf20Sopenharmony_ci	sctp_sched_ops_rr_init();
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ciint sctp_sched_set_sched(struct sctp_association *asoc,
1358c2ecf20Sopenharmony_ci			 enum sctp_sched_type sched)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	struct sctp_sched_ops *n = sctp_sched_ops[sched];
1388c2ecf20Sopenharmony_ci	struct sctp_sched_ops *old = asoc->outqueue.sched;
1398c2ecf20Sopenharmony_ci	struct sctp_datamsg *msg = NULL;
1408c2ecf20Sopenharmony_ci	struct sctp_chunk *ch;
1418c2ecf20Sopenharmony_ci	int i, ret = 0;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	if (old == n)
1448c2ecf20Sopenharmony_ci		return ret;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	if (sched > SCTP_SS_MAX)
1478c2ecf20Sopenharmony_ci		return -EINVAL;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	if (old) {
1508c2ecf20Sopenharmony_ci		old->free(&asoc->stream);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci		/* Give the next scheduler a clean slate. */
1538c2ecf20Sopenharmony_ci		for (i = 0; i < asoc->stream.outcnt; i++) {
1548c2ecf20Sopenharmony_ci			void *p = SCTP_SO(&asoc->stream, i)->ext;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci			if (!p)
1578c2ecf20Sopenharmony_ci				continue;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci			p += offsetofend(struct sctp_stream_out_ext, outq);
1608c2ecf20Sopenharmony_ci			memset(p, 0, sizeof(struct sctp_stream_out_ext) -
1618c2ecf20Sopenharmony_ci				     offsetofend(struct sctp_stream_out_ext, outq));
1628c2ecf20Sopenharmony_ci		}
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	asoc->outqueue.sched = n;
1668c2ecf20Sopenharmony_ci	n->init(&asoc->stream);
1678c2ecf20Sopenharmony_ci	for (i = 0; i < asoc->stream.outcnt; i++) {
1688c2ecf20Sopenharmony_ci		if (!SCTP_SO(&asoc->stream, i)->ext)
1698c2ecf20Sopenharmony_ci			continue;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci		ret = n->init_sid(&asoc->stream, i, GFP_ATOMIC);
1728c2ecf20Sopenharmony_ci		if (ret)
1738c2ecf20Sopenharmony_ci			goto err;
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	/* We have to requeue all chunks already queued. */
1778c2ecf20Sopenharmony_ci	list_for_each_entry(ch, &asoc->outqueue.out_chunk_list, list) {
1788c2ecf20Sopenharmony_ci		if (ch->msg == msg)
1798c2ecf20Sopenharmony_ci			continue;
1808c2ecf20Sopenharmony_ci		msg = ch->msg;
1818c2ecf20Sopenharmony_ci		n->enqueue(&asoc->outqueue, msg);
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	return ret;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cierr:
1878c2ecf20Sopenharmony_ci	n->free(&asoc->stream);
1888c2ecf20Sopenharmony_ci	asoc->outqueue.sched = &sctp_sched_fcfs; /* Always safe */
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	return ret;
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ciint sctp_sched_get_sched(struct sctp_association *asoc)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	int i;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	for (i = 0; i <= SCTP_SS_MAX; i++)
1988c2ecf20Sopenharmony_ci		if (asoc->outqueue.sched == sctp_sched_ops[i])
1998c2ecf20Sopenharmony_ci			return i;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	return 0;
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ciint sctp_sched_set_value(struct sctp_association *asoc, __u16 sid,
2058c2ecf20Sopenharmony_ci			 __u16 value, gfp_t gfp)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	if (sid >= asoc->stream.outcnt)
2088c2ecf20Sopenharmony_ci		return -EINVAL;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	if (!SCTP_SO(&asoc->stream, sid)->ext) {
2118c2ecf20Sopenharmony_ci		int ret;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci		ret = sctp_stream_init_ext(&asoc->stream, sid);
2148c2ecf20Sopenharmony_ci		if (ret)
2158c2ecf20Sopenharmony_ci			return ret;
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	return asoc->outqueue.sched->set(&asoc->stream, sid, value, gfp);
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ciint sctp_sched_get_value(struct sctp_association *asoc, __u16 sid,
2228c2ecf20Sopenharmony_ci			 __u16 *value)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	if (sid >= asoc->stream.outcnt)
2258c2ecf20Sopenharmony_ci		return -EINVAL;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (!SCTP_SO(&asoc->stream, sid)->ext)
2288c2ecf20Sopenharmony_ci		return 0;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	return asoc->outqueue.sched->get(&asoc->stream, sid, value);
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_civoid sctp_sched_dequeue_done(struct sctp_outq *q, struct sctp_chunk *ch)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	if (!list_is_last(&ch->frag_list, &ch->msg->chunks) &&
2368c2ecf20Sopenharmony_ci	    !q->asoc->peer.intl_capable) {
2378c2ecf20Sopenharmony_ci		struct sctp_stream_out *sout;
2388c2ecf20Sopenharmony_ci		__u16 sid;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci		/* datamsg is not finish, so save it as current one,
2418c2ecf20Sopenharmony_ci		 * in case application switch scheduler or a higher
2428c2ecf20Sopenharmony_ci		 * priority stream comes in.
2438c2ecf20Sopenharmony_ci		 */
2448c2ecf20Sopenharmony_ci		sid = sctp_chunk_stream_no(ch);
2458c2ecf20Sopenharmony_ci		sout = SCTP_SO(&q->asoc->stream, sid);
2468c2ecf20Sopenharmony_ci		q->asoc->stream.out_curr = sout;
2478c2ecf20Sopenharmony_ci		return;
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	q->asoc->stream.out_curr = NULL;
2518c2ecf20Sopenharmony_ci	q->sched->dequeue_done(q, ch);
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci/* Auxiliary functions for the schedulers */
2558c2ecf20Sopenharmony_civoid sctp_sched_dequeue_common(struct sctp_outq *q, struct sctp_chunk *ch)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	list_del_init(&ch->list);
2588c2ecf20Sopenharmony_ci	list_del_init(&ch->stream_list);
2598c2ecf20Sopenharmony_ci	q->out_qlen -= ch->skb->len;
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ciint sctp_sched_init_sid(struct sctp_stream *stream, __u16 sid, gfp_t gfp)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream);
2658c2ecf20Sopenharmony_ci	struct sctp_stream_out_ext *ext = SCTP_SO(stream, sid)->ext;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ext->outq);
2688c2ecf20Sopenharmony_ci	return sched->init_sid(stream, sid, gfp);
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistruct sctp_sched_ops *sctp_sched_ops_from_stream(struct sctp_stream *stream)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	asoc = container_of(stream, struct sctp_association, stream);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	return asoc->outqueue.sched;
2788c2ecf20Sopenharmony_ci}
279