xref: /openbmc/linux/net/sctp/stream_sched_fc.c (revision 42d452e7709fdb4d42376d2a97369e22cc80a5d2)
14821a076SXin Long // SPDX-License-Identifier: GPL-2.0-or-later
24821a076SXin Long /* SCTP kernel implementation
34821a076SXin Long  * (C) Copyright Red Hat Inc. 2022
44821a076SXin Long  *
54821a076SXin Long  * This file is part of the SCTP kernel implementation
64821a076SXin Long  *
74821a076SXin Long  * These functions manipulate sctp stream queue/scheduling.
84821a076SXin Long  *
94821a076SXin Long  * Please send any bug reports or fixes you make to the
104821a076SXin Long  * email addresched(es):
114821a076SXin Long  *    lksctp developers <linux-sctp@vger.kernel.org>
124821a076SXin Long  *
134821a076SXin Long  * Written or modified by:
144821a076SXin Long  *    Xin Long <lucien.xin@gmail.com>
154821a076SXin Long  */
164821a076SXin Long 
174821a076SXin Long #include <linux/list.h>
184821a076SXin Long #include <net/sctp/sctp.h>
194821a076SXin Long #include <net/sctp/sm.h>
204821a076SXin Long #include <net/sctp/stream_sched.h>
214821a076SXin Long 
22*42d452e7SXin Long /* Fair Capacity and Weighted Fair Queueing handling
23*42d452e7SXin Long  * RFC 8260 section 3.5 and 3.6
244821a076SXin Long  */
254821a076SXin Long static void sctp_sched_fc_unsched_all(struct sctp_stream *stream);
264821a076SXin Long 
sctp_sched_wfq_set(struct sctp_stream * stream,__u16 sid,__u16 weight,gfp_t gfp)27*42d452e7SXin Long static int sctp_sched_wfq_set(struct sctp_stream *stream, __u16 sid,
28*42d452e7SXin Long 			      __u16 weight, gfp_t gfp)
29*42d452e7SXin Long {
30*42d452e7SXin Long 	struct sctp_stream_out_ext *soute = SCTP_SO(stream, sid)->ext;
31*42d452e7SXin Long 
32*42d452e7SXin Long 	if (!weight)
33*42d452e7SXin Long 		return -EINVAL;
34*42d452e7SXin Long 
35*42d452e7SXin Long 	soute->fc_weight = weight;
36*42d452e7SXin Long 	return 0;
37*42d452e7SXin Long }
38*42d452e7SXin Long 
sctp_sched_wfq_get(struct sctp_stream * stream,__u16 sid,__u16 * value)39*42d452e7SXin Long static int sctp_sched_wfq_get(struct sctp_stream *stream, __u16 sid,
40*42d452e7SXin Long 			      __u16 *value)
41*42d452e7SXin Long {
42*42d452e7SXin Long 	struct sctp_stream_out_ext *soute = SCTP_SO(stream, sid)->ext;
43*42d452e7SXin Long 
44*42d452e7SXin Long 	*value = soute->fc_weight;
45*42d452e7SXin Long 	return 0;
46*42d452e7SXin Long }
47*42d452e7SXin Long 
sctp_sched_fc_set(struct sctp_stream * stream,__u16 sid,__u16 weight,gfp_t gfp)484821a076SXin Long static int sctp_sched_fc_set(struct sctp_stream *stream, __u16 sid,
494821a076SXin Long 			     __u16 weight, gfp_t gfp)
504821a076SXin Long {
514821a076SXin Long 	return 0;
524821a076SXin Long }
534821a076SXin Long 
sctp_sched_fc_get(struct sctp_stream * stream,__u16 sid,__u16 * value)544821a076SXin Long static int sctp_sched_fc_get(struct sctp_stream *stream, __u16 sid,
554821a076SXin Long 			     __u16 *value)
564821a076SXin Long {
574821a076SXin Long 	return 0;
584821a076SXin Long }
594821a076SXin Long 
sctp_sched_fc_init(struct sctp_stream * stream)604821a076SXin Long static int sctp_sched_fc_init(struct sctp_stream *stream)
614821a076SXin Long {
624821a076SXin Long 	INIT_LIST_HEAD(&stream->fc_list);
634821a076SXin Long 
644821a076SXin Long 	return 0;
654821a076SXin Long }
664821a076SXin Long 
sctp_sched_fc_init_sid(struct sctp_stream * stream,__u16 sid,gfp_t gfp)674821a076SXin Long static int sctp_sched_fc_init_sid(struct sctp_stream *stream, __u16 sid,
684821a076SXin Long 				  gfp_t gfp)
694821a076SXin Long {
704821a076SXin Long 	struct sctp_stream_out_ext *soute = SCTP_SO(stream, sid)->ext;
714821a076SXin Long 
724821a076SXin Long 	INIT_LIST_HEAD(&soute->fc_list);
734821a076SXin Long 	soute->fc_length = 0;
74*42d452e7SXin Long 	soute->fc_weight = 1;
754821a076SXin Long 
764821a076SXin Long 	return 0;
774821a076SXin Long }
784821a076SXin Long 
sctp_sched_fc_free_sid(struct sctp_stream * stream,__u16 sid)794821a076SXin Long static void sctp_sched_fc_free_sid(struct sctp_stream *stream, __u16 sid)
804821a076SXin Long {
814821a076SXin Long }
824821a076SXin Long 
sctp_sched_fc_sched(struct sctp_stream * stream,struct sctp_stream_out_ext * soute)834821a076SXin Long static void sctp_sched_fc_sched(struct sctp_stream *stream,
844821a076SXin Long 				struct sctp_stream_out_ext *soute)
854821a076SXin Long {
864821a076SXin Long 	struct sctp_stream_out_ext *pos;
874821a076SXin Long 
884821a076SXin Long 	if (!list_empty(&soute->fc_list))
894821a076SXin Long 		return;
904821a076SXin Long 
914821a076SXin Long 	list_for_each_entry(pos, &stream->fc_list, fc_list)
92*42d452e7SXin Long 		if ((__u64)pos->fc_length * soute->fc_weight >=
93*42d452e7SXin Long 		    (__u64)soute->fc_length * pos->fc_weight)
944821a076SXin Long 			break;
954821a076SXin Long 	list_add_tail(&soute->fc_list, &pos->fc_list);
964821a076SXin Long }
974821a076SXin Long 
sctp_sched_fc_enqueue(struct sctp_outq * q,struct sctp_datamsg * msg)984821a076SXin Long static void sctp_sched_fc_enqueue(struct sctp_outq *q,
994821a076SXin Long 				  struct sctp_datamsg *msg)
1004821a076SXin Long {
1014821a076SXin Long 	struct sctp_stream *stream;
1024821a076SXin Long 	struct sctp_chunk *ch;
1034821a076SXin Long 	__u16 sid;
1044821a076SXin Long 
1054821a076SXin Long 	ch = list_first_entry(&msg->chunks, struct sctp_chunk, frag_list);
1064821a076SXin Long 	sid = sctp_chunk_stream_no(ch);
1074821a076SXin Long 	stream = &q->asoc->stream;
1084821a076SXin Long 	sctp_sched_fc_sched(stream, SCTP_SO(stream, sid)->ext);
1094821a076SXin Long }
1104821a076SXin Long 
sctp_sched_fc_dequeue(struct sctp_outq * q)1114821a076SXin Long static struct sctp_chunk *sctp_sched_fc_dequeue(struct sctp_outq *q)
1124821a076SXin Long {
1134821a076SXin Long 	struct sctp_stream *stream = &q->asoc->stream;
1144821a076SXin Long 	struct sctp_stream_out_ext *soute;
1154821a076SXin Long 	struct sctp_chunk *ch;
1164821a076SXin Long 
1174821a076SXin Long 	/* Bail out quickly if queue is empty */
1184821a076SXin Long 	if (list_empty(&q->out_chunk_list))
1194821a076SXin Long 		return NULL;
1204821a076SXin Long 
1214821a076SXin Long 	/* Find which chunk is next */
1224821a076SXin Long 	if (stream->out_curr)
1234821a076SXin Long 		soute = stream->out_curr->ext;
1244821a076SXin Long 	else
1254821a076SXin Long 		soute = list_entry(stream->fc_list.next, struct sctp_stream_out_ext, fc_list);
1264821a076SXin Long 	ch = list_entry(soute->outq.next, struct sctp_chunk, stream_list);
1274821a076SXin Long 
1284821a076SXin Long 	sctp_sched_dequeue_common(q, ch);
1294821a076SXin Long 	return ch;
1304821a076SXin Long }
1314821a076SXin Long 
sctp_sched_fc_dequeue_done(struct sctp_outq * q,struct sctp_chunk * ch)1324821a076SXin Long static void sctp_sched_fc_dequeue_done(struct sctp_outq *q,
1334821a076SXin Long 				       struct sctp_chunk *ch)
1344821a076SXin Long {
1354821a076SXin Long 	struct sctp_stream *stream = &q->asoc->stream;
1364821a076SXin Long 	struct sctp_stream_out_ext *soute, *pos;
1374821a076SXin Long 	__u16 sid, i;
1384821a076SXin Long 
1394821a076SXin Long 	sid = sctp_chunk_stream_no(ch);
1404821a076SXin Long 	soute = SCTP_SO(stream, sid)->ext;
1414821a076SXin Long 	/* reduce all fc_lengths by U32_MAX / 4 if the current fc_length overflows. */
1424821a076SXin Long 	if (soute->fc_length > U32_MAX - ch->skb->len) {
1434821a076SXin Long 		for (i = 0; i < stream->outcnt; i++) {
1444821a076SXin Long 			pos = SCTP_SO(stream, i)->ext;
1454821a076SXin Long 			if (!pos)
1464821a076SXin Long 				continue;
1474821a076SXin Long 			if (pos->fc_length <= (U32_MAX >> 2)) {
1484821a076SXin Long 				pos->fc_length = 0;
1494821a076SXin Long 				continue;
1504821a076SXin Long 			}
1514821a076SXin Long 			pos->fc_length -= (U32_MAX >> 2);
1524821a076SXin Long 		}
1534821a076SXin Long 	}
1544821a076SXin Long 	soute->fc_length += ch->skb->len;
1554821a076SXin Long 
1564821a076SXin Long 	if (list_empty(&soute->outq)) {
1574821a076SXin Long 		list_del_init(&soute->fc_list);
1584821a076SXin Long 		return;
1594821a076SXin Long 	}
1604821a076SXin Long 
1614821a076SXin Long 	pos = soute;
1624821a076SXin Long 	list_for_each_entry_continue(pos, &stream->fc_list, fc_list)
163*42d452e7SXin Long 		if ((__u64)pos->fc_length * soute->fc_weight >=
164*42d452e7SXin Long 		    (__u64)soute->fc_length * pos->fc_weight)
1654821a076SXin Long 			break;
1664821a076SXin Long 	list_move_tail(&soute->fc_list, &pos->fc_list);
1674821a076SXin Long }
1684821a076SXin Long 
sctp_sched_fc_sched_all(struct sctp_stream * stream)1694821a076SXin Long static void sctp_sched_fc_sched_all(struct sctp_stream *stream)
1704821a076SXin Long {
1714821a076SXin Long 	struct sctp_association *asoc;
1724821a076SXin Long 	struct sctp_chunk *ch;
1734821a076SXin Long 
1744821a076SXin Long 	asoc = container_of(stream, struct sctp_association, stream);
1754821a076SXin Long 	list_for_each_entry(ch, &asoc->outqueue.out_chunk_list, list) {
1764821a076SXin Long 		__u16 sid = sctp_chunk_stream_no(ch);
1774821a076SXin Long 
1784821a076SXin Long 		if (SCTP_SO(stream, sid)->ext)
1794821a076SXin Long 			sctp_sched_fc_sched(stream, SCTP_SO(stream, sid)->ext);
1804821a076SXin Long 	}
1814821a076SXin Long }
1824821a076SXin Long 
sctp_sched_fc_unsched_all(struct sctp_stream * stream)1834821a076SXin Long static void sctp_sched_fc_unsched_all(struct sctp_stream *stream)
1844821a076SXin Long {
1854821a076SXin Long 	struct sctp_stream_out_ext *soute, *tmp;
1864821a076SXin Long 
1874821a076SXin Long 	list_for_each_entry_safe(soute, tmp, &stream->fc_list, fc_list)
1884821a076SXin Long 		list_del_init(&soute->fc_list);
1894821a076SXin Long }
1904821a076SXin Long 
1914821a076SXin Long static struct sctp_sched_ops sctp_sched_fc = {
1924821a076SXin Long 	.set = sctp_sched_fc_set,
1934821a076SXin Long 	.get = sctp_sched_fc_get,
1944821a076SXin Long 	.init = sctp_sched_fc_init,
1954821a076SXin Long 	.init_sid = sctp_sched_fc_init_sid,
1964821a076SXin Long 	.free_sid = sctp_sched_fc_free_sid,
1974821a076SXin Long 	.enqueue = sctp_sched_fc_enqueue,
1984821a076SXin Long 	.dequeue = sctp_sched_fc_dequeue,
1994821a076SXin Long 	.dequeue_done = sctp_sched_fc_dequeue_done,
2004821a076SXin Long 	.sched_all = sctp_sched_fc_sched_all,
2014821a076SXin Long 	.unsched_all = sctp_sched_fc_unsched_all,
2024821a076SXin Long };
2034821a076SXin Long 
sctp_sched_ops_fc_init(void)2044821a076SXin Long void sctp_sched_ops_fc_init(void)
2054821a076SXin Long {
2064821a076SXin Long 	sctp_sched_ops_register(SCTP_SS_FC, &sctp_sched_fc);
2074821a076SXin Long }
208*42d452e7SXin Long 
209*42d452e7SXin Long static struct sctp_sched_ops sctp_sched_wfq = {
210*42d452e7SXin Long 	.set = sctp_sched_wfq_set,
211*42d452e7SXin Long 	.get = sctp_sched_wfq_get,
212*42d452e7SXin Long 	.init = sctp_sched_fc_init,
213*42d452e7SXin Long 	.init_sid = sctp_sched_fc_init_sid,
214*42d452e7SXin Long 	.free_sid = sctp_sched_fc_free_sid,
215*42d452e7SXin Long 	.enqueue = sctp_sched_fc_enqueue,
216*42d452e7SXin Long 	.dequeue = sctp_sched_fc_dequeue,
217*42d452e7SXin Long 	.dequeue_done = sctp_sched_fc_dequeue_done,
218*42d452e7SXin Long 	.sched_all = sctp_sched_fc_sched_all,
219*42d452e7SXin Long 	.unsched_all = sctp_sched_fc_unsched_all,
220*42d452e7SXin Long };
221*42d452e7SXin Long 
sctp_sched_ops_wfq_init(void)222*42d452e7SXin Long void sctp_sched_ops_wfq_init(void)
223*42d452e7SXin Long {
224*42d452e7SXin Long 	sctp_sched_ops_register(SCTP_SS_WFQ, &sctp_sched_wfq);
225*42d452e7SXin Long }
226