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