1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* SCTP kernel implementation
3  * (C) Copyright Red Hat Inc. 2022
4  *
5  * This file is part of the SCTP kernel implementation
6  *
7  * These functions manipulate sctp stream queue/scheduling.
8  *
9  * Please send any bug reports or fixes you make to the
10  * email addresched(es):
11  *    lksctp developers <linux-sctp@vger.kernel.org>
12  *
13  * Written or modified by:
14  *    Xin Long <lucien.xin@gmail.com>
15  */
16 
17 #include <linux/list.h>
18 #include <net/sctp/sctp.h>
19 #include <net/sctp/sm.h>
20 #include <net/sctp/stream_sched.h>
21 
22 /* Fair Capacity and Weighted Fair Queueing handling
23  * RFC 8260 section 3.5 and 3.6
24  */
25 static void sctp_sched_fc_unsched_all(struct sctp_stream *stream);
26 
sctp_sched_wfq_set(struct sctp_stream * stream,__u16 sid,__u16 weight,gfp_t gfp)27 static int sctp_sched_wfq_set(struct sctp_stream *stream, __u16 sid,
28 			      __u16 weight, gfp_t gfp)
29 {
30 	struct sctp_stream_out_ext *soute = SCTP_SO(stream, sid)->ext;
31 
32 	if (!weight)
33 		return -EINVAL;
34 
35 	soute->fc_weight = weight;
36 	return 0;
37 }
38 
sctp_sched_wfq_get(struct sctp_stream * stream,__u16 sid,__u16 * value)39 static int sctp_sched_wfq_get(struct sctp_stream *stream, __u16 sid,
40 			      __u16 *value)
41 {
42 	struct sctp_stream_out_ext *soute = SCTP_SO(stream, sid)->ext;
43 
44 	*value = soute->fc_weight;
45 	return 0;
46 }
47 
sctp_sched_fc_set(struct sctp_stream * stream,__u16 sid,__u16 weight,gfp_t gfp)48 static int sctp_sched_fc_set(struct sctp_stream *stream, __u16 sid,
49 			     __u16 weight, gfp_t gfp)
50 {
51 	return 0;
52 }
53 
sctp_sched_fc_get(struct sctp_stream * stream,__u16 sid,__u16 * value)54 static int sctp_sched_fc_get(struct sctp_stream *stream, __u16 sid,
55 			     __u16 *value)
56 {
57 	return 0;
58 }
59 
sctp_sched_fc_init(struct sctp_stream * stream)60 static int sctp_sched_fc_init(struct sctp_stream *stream)
61 {
62 	INIT_LIST_HEAD(&stream->fc_list);
63 
64 	return 0;
65 }
66 
sctp_sched_fc_init_sid(struct sctp_stream * stream,__u16 sid,gfp_t gfp)67 static int sctp_sched_fc_init_sid(struct sctp_stream *stream, __u16 sid,
68 				  gfp_t gfp)
69 {
70 	struct sctp_stream_out_ext *soute = SCTP_SO(stream, sid)->ext;
71 
72 	INIT_LIST_HEAD(&soute->fc_list);
73 	soute->fc_length = 0;
74 	soute->fc_weight = 1;
75 
76 	return 0;
77 }
78 
sctp_sched_fc_free_sid(struct sctp_stream * stream,__u16 sid)79 static void sctp_sched_fc_free_sid(struct sctp_stream *stream, __u16 sid)
80 {
81 }
82 
sctp_sched_fc_sched(struct sctp_stream * stream,struct sctp_stream_out_ext * soute)83 static void sctp_sched_fc_sched(struct sctp_stream *stream,
84 				struct sctp_stream_out_ext *soute)
85 {
86 	struct sctp_stream_out_ext *pos;
87 
88 	if (!list_empty(&soute->fc_list))
89 		return;
90 
91 	list_for_each_entry(pos, &stream->fc_list, fc_list)
92 		if ((__u64)pos->fc_length * soute->fc_weight >=
93 		    (__u64)soute->fc_length * pos->fc_weight)
94 			break;
95 	list_add_tail(&soute->fc_list, &pos->fc_list);
96 }
97 
sctp_sched_fc_enqueue(struct sctp_outq * q,struct sctp_datamsg * msg)98 static void sctp_sched_fc_enqueue(struct sctp_outq *q,
99 				  struct sctp_datamsg *msg)
100 {
101 	struct sctp_stream *stream;
102 	struct sctp_chunk *ch;
103 	__u16 sid;
104 
105 	ch = list_first_entry(&msg->chunks, struct sctp_chunk, frag_list);
106 	sid = sctp_chunk_stream_no(ch);
107 	stream = &q->asoc->stream;
108 	sctp_sched_fc_sched(stream, SCTP_SO(stream, sid)->ext);
109 }
110 
sctp_sched_fc_dequeue(struct sctp_outq * q)111 static struct sctp_chunk *sctp_sched_fc_dequeue(struct sctp_outq *q)
112 {
113 	struct sctp_stream *stream = &q->asoc->stream;
114 	struct sctp_stream_out_ext *soute;
115 	struct sctp_chunk *ch;
116 
117 	/* Bail out quickly if queue is empty */
118 	if (list_empty(&q->out_chunk_list))
119 		return NULL;
120 
121 	/* Find which chunk is next */
122 	if (stream->out_curr)
123 		soute = stream->out_curr->ext;
124 	else
125 		soute = list_entry(stream->fc_list.next, struct sctp_stream_out_ext, fc_list);
126 	ch = list_entry(soute->outq.next, struct sctp_chunk, stream_list);
127 
128 	sctp_sched_dequeue_common(q, ch);
129 	return ch;
130 }
131 
sctp_sched_fc_dequeue_done(struct sctp_outq * q,struct sctp_chunk * ch)132 static void sctp_sched_fc_dequeue_done(struct sctp_outq *q,
133 				       struct sctp_chunk *ch)
134 {
135 	struct sctp_stream *stream = &q->asoc->stream;
136 	struct sctp_stream_out_ext *soute, *pos;
137 	__u16 sid, i;
138 
139 	sid = sctp_chunk_stream_no(ch);
140 	soute = SCTP_SO(stream, sid)->ext;
141 	/* reduce all fc_lengths by U32_MAX / 4 if the current fc_length overflows. */
142 	if (soute->fc_length > U32_MAX - ch->skb->len) {
143 		for (i = 0; i < stream->outcnt; i++) {
144 			pos = SCTP_SO(stream, i)->ext;
145 			if (!pos)
146 				continue;
147 			if (pos->fc_length <= (U32_MAX >> 2)) {
148 				pos->fc_length = 0;
149 				continue;
150 			}
151 			pos->fc_length -= (U32_MAX >> 2);
152 		}
153 	}
154 	soute->fc_length += ch->skb->len;
155 
156 	if (list_empty(&soute->outq)) {
157 		list_del_init(&soute->fc_list);
158 		return;
159 	}
160 
161 	pos = soute;
162 	list_for_each_entry_continue(pos, &stream->fc_list, fc_list)
163 		if ((__u64)pos->fc_length * soute->fc_weight >=
164 		    (__u64)soute->fc_length * pos->fc_weight)
165 			break;
166 	list_move_tail(&soute->fc_list, &pos->fc_list);
167 }
168 
sctp_sched_fc_sched_all(struct sctp_stream * stream)169 static void sctp_sched_fc_sched_all(struct sctp_stream *stream)
170 {
171 	struct sctp_association *asoc;
172 	struct sctp_chunk *ch;
173 
174 	asoc = container_of(stream, struct sctp_association, stream);
175 	list_for_each_entry(ch, &asoc->outqueue.out_chunk_list, list) {
176 		__u16 sid = sctp_chunk_stream_no(ch);
177 
178 		if (SCTP_SO(stream, sid)->ext)
179 			sctp_sched_fc_sched(stream, SCTP_SO(stream, sid)->ext);
180 	}
181 }
182 
sctp_sched_fc_unsched_all(struct sctp_stream * stream)183 static void sctp_sched_fc_unsched_all(struct sctp_stream *stream)
184 {
185 	struct sctp_stream_out_ext *soute, *tmp;
186 
187 	list_for_each_entry_safe(soute, tmp, &stream->fc_list, fc_list)
188 		list_del_init(&soute->fc_list);
189 }
190 
191 static struct sctp_sched_ops sctp_sched_fc = {
192 	.set = sctp_sched_fc_set,
193 	.get = sctp_sched_fc_get,
194 	.init = sctp_sched_fc_init,
195 	.init_sid = sctp_sched_fc_init_sid,
196 	.free_sid = sctp_sched_fc_free_sid,
197 	.enqueue = sctp_sched_fc_enqueue,
198 	.dequeue = sctp_sched_fc_dequeue,
199 	.dequeue_done = sctp_sched_fc_dequeue_done,
200 	.sched_all = sctp_sched_fc_sched_all,
201 	.unsched_all = sctp_sched_fc_unsched_all,
202 };
203 
sctp_sched_ops_fc_init(void)204 void sctp_sched_ops_fc_init(void)
205 {
206 	sctp_sched_ops_register(SCTP_SS_FC, &sctp_sched_fc);
207 }
208 
209 static struct sctp_sched_ops sctp_sched_wfq = {
210 	.set = sctp_sched_wfq_set,
211 	.get = sctp_sched_wfq_get,
212 	.init = sctp_sched_fc_init,
213 	.init_sid = sctp_sched_fc_init_sid,
214 	.free_sid = sctp_sched_fc_free_sid,
215 	.enqueue = sctp_sched_fc_enqueue,
216 	.dequeue = sctp_sched_fc_dequeue,
217 	.dequeue_done = sctp_sched_fc_dequeue_done,
218 	.sched_all = sctp_sched_fc_sched_all,
219 	.unsched_all = sctp_sched_fc_unsched_all,
220 };
221 
sctp_sched_ops_wfq_init(void)222 void sctp_sched_ops_wfq_init(void)
223 {
224 	sctp_sched_ops_register(SCTP_SS_WFQ, &sctp_sched_wfq);
225 }
226