aboutsummaryrefslogtreecommitdiffstats
path: root/net/sctp/stream_sched.c
diff options
context:
space:
mode:
authorMarcelo Ricardo Leitner <marcelo.leitner@gmail.com>2017-10-03 18:20:13 -0400
committerDavid S. Miller <davem@davemloft.net>2017-10-03 19:27:29 -0400
commit5bbbbe32a43199c2b9ea5ea66fab6241c64beb51 (patch)
tree689b8896bcbcc50cfbc4554d79577cec95bb39f2 /net/sctp/stream_sched.c
parent2fc019f790312e703efa1a44204c586112a430dc (diff)
sctp: introduce stream scheduler foundations
This patch introduces the hooks necessary to do stream scheduling, as per RFC Draft ndata. It also introduces the first scheduler, which is what we do today but now factored out: first come first served (FCFS). With stream scheduling now we have to track which chunk was enqueued on which stream and be able to select another other than the in front of the main outqueue. So we introduce a list on sctp_stream_out_ext structure for this purpose. We reuse sctp_chunk->transmitted_list space for the list above, as the chunk cannot belong to the two lists at the same time. By using the union in there, we can have distinct names for these moments. sctp_sched_ops are the operations expected to be implemented by each scheduler. The dequeueing is a bit particular to this implementation but it is to match how we dequeue packets today. We first dequeue and then check if it fits the packet and if not, we requeue it at head. Thus why we don't have a peek operation but have dequeue_done instead, which is called once the chunk can be safely considered as transmitted. The check removed from sctp_outq_flush is now performed by sctp_stream_outq_migrate, which is only called during assoc setup. (sctp_sendmsg() also checks for it) The only operation that is foreseen but not yet added here is a way to signalize that a new packet is starting or that the packet is done, for round robin scheduler per packet, but is intentionally left to the patch that actually implements it. Support for I-DATA chunks, also described in this RFC, with user message interleaving is straightforward as it just requires the schedulers to probe for the feature and ignore datamsg boundaries when dequeueing. See-also: https://tools.ietf.org/html/draft-ietf-tsvwg-sctp-ndata-13 Tested-by: Xin Long <lucien.xin@gmail.com> Signed-off-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/sctp/stream_sched.c')
-rw-r--r--net/sctp/stream_sched.c270
1 files changed, 270 insertions, 0 deletions
diff --git a/net/sctp/stream_sched.c b/net/sctp/stream_sched.c
new file mode 100644
index 000000000000..40a9a9de2b98
--- /dev/null
+++ b/net/sctp/stream_sched.c
@@ -0,0 +1,270 @@
1/* SCTP kernel implementation
2 * (C) Copyright Red Hat Inc. 2017
3 *
4 * This file is part of the SCTP kernel implementation
5 *
6 * These functions manipulate sctp stream queue/scheduling.
7 *
8 * This SCTP implementation is free software;
9 * you can redistribute it and/or modify it under the terms of
10 * the GNU General Public License as published by
11 * the Free Software Foundation; either version 2, or (at your option)
12 * any later version.
13 *
14 * This SCTP implementation is distributed in the hope that it
15 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
16 * ************************
17 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 * See the GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with GNU CC; see the file COPYING. If not, see
22 * <http://www.gnu.org/licenses/>.
23 *
24 * Please send any bug reports or fixes you make to the
25 * email addresched(es):
26 * lksctp developers <linux-sctp@vger.kernel.org>
27 *
28 * Written or modified by:
29 * Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
30 */
31
32#include <linux/list.h>
33#include <net/sctp/sctp.h>
34#include <net/sctp/sm.h>
35#include <net/sctp/stream_sched.h>
36
37/* First Come First Serve (a.k.a. FIFO)
38 * RFC DRAFT ndata Section 3.1
39 */
40static int sctp_sched_fcfs_set(struct sctp_stream *stream, __u16 sid,
41 __u16 value, gfp_t gfp)
42{
43 return 0;
44}
45
46static int sctp_sched_fcfs_get(struct sctp_stream *stream, __u16 sid,
47 __u16 *value)
48{
49 *value = 0;
50 return 0;
51}
52
53static int sctp_sched_fcfs_init(struct sctp_stream *stream)
54{
55 return 0;
56}
57
58static int sctp_sched_fcfs_init_sid(struct sctp_stream *stream, __u16 sid,
59 gfp_t gfp)
60{
61 return 0;
62}
63
64static void sctp_sched_fcfs_free(struct sctp_stream *stream)
65{
66}
67
68static void sctp_sched_fcfs_enqueue(struct sctp_outq *q,
69 struct sctp_datamsg *msg)
70{
71}
72
73static struct sctp_chunk *sctp_sched_fcfs_dequeue(struct sctp_outq *q)
74{
75 struct sctp_stream *stream = &q->asoc->stream;
76 struct sctp_chunk *ch = NULL;
77 struct list_head *entry;
78
79 if (list_empty(&q->out_chunk_list))
80 goto out;
81
82 if (stream->out_curr) {
83 ch = list_entry(stream->out_curr->ext->outq.next,
84 struct sctp_chunk, stream_list);
85 } else {
86 entry = q->out_chunk_list.next;
87 ch = list_entry(entry, struct sctp_chunk, list);
88 }
89
90 sctp_sched_dequeue_common(q, ch);
91
92out:
93 return ch;
94}
95
96static void sctp_sched_fcfs_dequeue_done(struct sctp_outq *q,
97 struct sctp_chunk *chunk)
98{
99}
100
101static void sctp_sched_fcfs_sched_all(struct sctp_stream *stream)
102{
103}
104
105static void sctp_sched_fcfs_unsched_all(struct sctp_stream *stream)
106{
107}
108
109static struct sctp_sched_ops sctp_sched_fcfs = {
110 .set = sctp_sched_fcfs_set,
111 .get = sctp_sched_fcfs_get,
112 .init = sctp_sched_fcfs_init,
113 .init_sid = sctp_sched_fcfs_init_sid,
114 .free = sctp_sched_fcfs_free,
115 .enqueue = sctp_sched_fcfs_enqueue,
116 .dequeue = sctp_sched_fcfs_dequeue,
117 .dequeue_done = sctp_sched_fcfs_dequeue_done,
118 .sched_all = sctp_sched_fcfs_sched_all,
119 .unsched_all = sctp_sched_fcfs_unsched_all,
120};
121
122/* API to other parts of the stack */
123
124struct sctp_sched_ops *sctp_sched_ops[] = {
125 &sctp_sched_fcfs,
126};
127
128int sctp_sched_set_sched(struct sctp_association *asoc,
129 enum sctp_sched_type sched)
130{
131 struct sctp_sched_ops *n = sctp_sched_ops[sched];
132 struct sctp_sched_ops *old = asoc->outqueue.sched;
133 struct sctp_datamsg *msg = NULL;
134 struct sctp_chunk *ch;
135 int i, ret = 0;
136
137 if (old == n)
138 return ret;
139
140 if (sched > SCTP_SS_MAX)
141 return -EINVAL;
142
143 if (old) {
144 old->free(&asoc->stream);
145
146 /* Give the next scheduler a clean slate. */
147 for (i = 0; i < asoc->stream.outcnt; i++) {
148 void *p = asoc->stream.out[i].ext;
149
150 if (!p)
151 continue;
152
153 p += offsetofend(struct sctp_stream_out_ext, outq);
154 memset(p, 0, sizeof(struct sctp_stream_out_ext) -
155 offsetofend(struct sctp_stream_out_ext, outq));
156 }
157 }
158
159 asoc->outqueue.sched = n;
160 n->init(&asoc->stream);
161 for (i = 0; i < asoc->stream.outcnt; i++) {
162 if (!asoc->stream.out[i].ext)
163 continue;
164
165 ret = n->init_sid(&asoc->stream, i, GFP_KERNEL);
166 if (ret)
167 goto err;
168 }
169
170 /* We have to requeue all chunks already queued. */
171 list_for_each_entry(ch, &asoc->outqueue.out_chunk_list, list) {
172 if (ch->msg == msg)
173 continue;
174 msg = ch->msg;
175 n->enqueue(&asoc->outqueue, msg);
176 }
177
178 return ret;
179
180err:
181 n->free(&asoc->stream);
182 asoc->outqueue.sched = &sctp_sched_fcfs; /* Always safe */
183
184 return ret;
185}
186
187int sctp_sched_get_sched(struct sctp_association *asoc)
188{
189 int i;
190
191 for (i = 0; i <= SCTP_SS_MAX; i++)
192 if (asoc->outqueue.sched == sctp_sched_ops[i])
193 return i;
194
195 return 0;
196}
197
198int sctp_sched_set_value(struct sctp_association *asoc, __u16 sid,
199 __u16 value, gfp_t gfp)
200{
201 if (sid >= asoc->stream.outcnt)
202 return -EINVAL;
203
204 if (!asoc->stream.out[sid].ext) {
205 int ret;
206
207 ret = sctp_stream_init_ext(&asoc->stream, sid);
208 if (ret)
209 return ret;
210 }
211
212 return asoc->outqueue.sched->set(&asoc->stream, sid, value, gfp);
213}
214
215int sctp_sched_get_value(struct sctp_association *asoc, __u16 sid,
216 __u16 *value)
217{
218 if (sid >= asoc->stream.outcnt)
219 return -EINVAL;
220
221 if (!asoc->stream.out[sid].ext)
222 return 0;
223
224 return asoc->outqueue.sched->get(&asoc->stream, sid, value);
225}
226
227void sctp_sched_dequeue_done(struct sctp_outq *q, struct sctp_chunk *ch)
228{
229 if (!list_is_last(&ch->frag_list, &ch->msg->chunks)) {
230 struct sctp_stream_out *sout;
231 __u16 sid;
232
233 /* datamsg is not finish, so save it as current one,
234 * in case application switch scheduler or a higher
235 * priority stream comes in.
236 */
237 sid = sctp_chunk_stream_no(ch);
238 sout = &q->asoc->stream.out[sid];
239 q->asoc->stream.out_curr = sout;
240 return;
241 }
242
243 q->asoc->stream.out_curr = NULL;
244 q->sched->dequeue_done(q, ch);
245}
246
247/* Auxiliary functions for the schedulers */
248void sctp_sched_dequeue_common(struct sctp_outq *q, struct sctp_chunk *ch)
249{
250 list_del_init(&ch->list);
251 list_del_init(&ch->stream_list);
252 q->out_qlen -= ch->skb->len;
253}
254
255int sctp_sched_init_sid(struct sctp_stream *stream, __u16 sid, gfp_t gfp)
256{
257 struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream);
258
259 INIT_LIST_HEAD(&stream->out[sid].ext->outq);
260 return sched->init_sid(stream, sid, gfp);
261}
262
263struct sctp_sched_ops *sctp_sched_ops_from_stream(struct sctp_stream *stream)
264{
265 struct sctp_association *asoc;
266
267 asoc = container_of(stream, struct sctp_association, stream);
268
269 return asoc->outqueue.sched;
270}