aboutsummaryrefslogtreecommitdiffstats
path: root/net/sctp/stream.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.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.c')
-rw-r--r--net/sctp/stream.c88
1 files changed, 81 insertions, 7 deletions
diff --git a/net/sctp/stream.c b/net/sctp/stream.c
index 055ca25bbc91..5ea33a2c453b 100644
--- a/net/sctp/stream.c
+++ b/net/sctp/stream.c
@@ -32,8 +32,61 @@
32 * Xin Long <lucien.xin@gmail.com> 32 * Xin Long <lucien.xin@gmail.com>
33 */ 33 */
34 34
35#include <linux/list.h>
35#include <net/sctp/sctp.h> 36#include <net/sctp/sctp.h>
36#include <net/sctp/sm.h> 37#include <net/sctp/sm.h>
38#include <net/sctp/stream_sched.h>
39
40/* Migrates chunks from stream queues to new stream queues if needed,
41 * but not across associations. Also, removes those chunks to streams
42 * higher than the new max.
43 */
44static void sctp_stream_outq_migrate(struct sctp_stream *stream,
45 struct sctp_stream *new, __u16 outcnt)
46{
47 struct sctp_association *asoc;
48 struct sctp_chunk *ch, *temp;
49 struct sctp_outq *outq;
50 int i;
51
52 asoc = container_of(stream, struct sctp_association, stream);
53 outq = &asoc->outqueue;
54
55 list_for_each_entry_safe(ch, temp, &outq->out_chunk_list, list) {
56 __u16 sid = sctp_chunk_stream_no(ch);
57
58 if (sid < outcnt)
59 continue;
60
61 sctp_sched_dequeue_common(outq, ch);
62 /* No need to call dequeue_done here because
63 * the chunks are not scheduled by now.
64 */
65
66 /* Mark as failed send. */
67 sctp_chunk_fail(ch, SCTP_ERROR_INV_STRM);
68 if (asoc->peer.prsctp_capable &&
69 SCTP_PR_PRIO_ENABLED(ch->sinfo.sinfo_flags))
70 asoc->sent_cnt_removable--;
71
72 sctp_chunk_free(ch);
73 }
74
75 if (new) {
76 /* Here we actually move the old ext stuff into the new
77 * buffer, because we want to keep it. Then
78 * sctp_stream_update will swap ->out pointers.
79 */
80 for (i = 0; i < outcnt; i++) {
81 kfree(new->out[i].ext);
82 new->out[i].ext = stream->out[i].ext;
83 stream->out[i].ext = NULL;
84 }
85 }
86
87 for (i = outcnt; i < stream->outcnt; i++)
88 kfree(stream->out[i].ext);
89}
37 90
38static int sctp_stream_alloc_out(struct sctp_stream *stream, __u16 outcnt, 91static int sctp_stream_alloc_out(struct sctp_stream *stream, __u16 outcnt,
39 gfp_t gfp) 92 gfp_t gfp)
@@ -87,7 +140,8 @@ static int sctp_stream_alloc_in(struct sctp_stream *stream, __u16 incnt,
87int sctp_stream_init(struct sctp_stream *stream, __u16 outcnt, __u16 incnt, 140int sctp_stream_init(struct sctp_stream *stream, __u16 outcnt, __u16 incnt,
88 gfp_t gfp) 141 gfp_t gfp)
89{ 142{
90 int i; 143 struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream);
144 int i, ret = 0;
91 145
92 gfp |= __GFP_NOWARN; 146 gfp |= __GFP_NOWARN;
93 147
@@ -97,6 +151,11 @@ int sctp_stream_init(struct sctp_stream *stream, __u16 outcnt, __u16 incnt,
97 if (outcnt == stream->outcnt) 151 if (outcnt == stream->outcnt)
98 goto in; 152 goto in;
99 153
154 /* Filter out chunks queued on streams that won't exist anymore */
155 sched->unsched_all(stream);
156 sctp_stream_outq_migrate(stream, NULL, outcnt);
157 sched->sched_all(stream);
158
100 i = sctp_stream_alloc_out(stream, outcnt, gfp); 159 i = sctp_stream_alloc_out(stream, outcnt, gfp);
101 if (i) 160 if (i)
102 return i; 161 return i;
@@ -105,20 +164,27 @@ int sctp_stream_init(struct sctp_stream *stream, __u16 outcnt, __u16 incnt,
105 for (i = 0; i < stream->outcnt; i++) 164 for (i = 0; i < stream->outcnt; i++)
106 stream->out[i].state = SCTP_STREAM_OPEN; 165 stream->out[i].state = SCTP_STREAM_OPEN;
107 166
167 sched->init(stream);
168
108in: 169in:
109 if (!incnt) 170 if (!incnt)
110 return 0; 171 goto out;
111 172
112 i = sctp_stream_alloc_in(stream, incnt, gfp); 173 i = sctp_stream_alloc_in(stream, incnt, gfp);
113 if (i) { 174 if (i) {
114 kfree(stream->out); 175 ret = -ENOMEM;
115 stream->out = NULL; 176 goto free;
116 return -ENOMEM;
117 } 177 }
118 178
119 stream->incnt = incnt; 179 stream->incnt = incnt;
180 goto out;
120 181
121 return 0; 182free:
183 sched->free(stream);
184 kfree(stream->out);
185 stream->out = NULL;
186out:
187 return ret;
122} 188}
123 189
124int sctp_stream_init_ext(struct sctp_stream *stream, __u16 sid) 190int sctp_stream_init_ext(struct sctp_stream *stream, __u16 sid)
@@ -130,13 +196,15 @@ int sctp_stream_init_ext(struct sctp_stream *stream, __u16 sid)
130 return -ENOMEM; 196 return -ENOMEM;
131 stream->out[sid].ext = soute; 197 stream->out[sid].ext = soute;
132 198
133 return 0; 199 return sctp_sched_init_sid(stream, sid, GFP_KERNEL);
134} 200}
135 201
136void sctp_stream_free(struct sctp_stream *stream) 202void sctp_stream_free(struct sctp_stream *stream)
137{ 203{
204 struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream);
138 int i; 205 int i;
139 206
207 sched->free(stream);
140 for (i = 0; i < stream->outcnt; i++) 208 for (i = 0; i < stream->outcnt; i++)
141 kfree(stream->out[i].ext); 209 kfree(stream->out[i].ext);
142 kfree(stream->out); 210 kfree(stream->out);
@@ -156,6 +224,10 @@ void sctp_stream_clear(struct sctp_stream *stream)
156 224
157void sctp_stream_update(struct sctp_stream *stream, struct sctp_stream *new) 225void sctp_stream_update(struct sctp_stream *stream, struct sctp_stream *new)
158{ 226{
227 struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream);
228
229 sched->unsched_all(stream);
230 sctp_stream_outq_migrate(stream, new, new->outcnt);
159 sctp_stream_free(stream); 231 sctp_stream_free(stream);
160 232
161 stream->out = new->out; 233 stream->out = new->out;
@@ -163,6 +235,8 @@ void sctp_stream_update(struct sctp_stream *stream, struct sctp_stream *new)
163 stream->outcnt = new->outcnt; 235 stream->outcnt = new->outcnt;
164 stream->incnt = new->incnt; 236 stream->incnt = new->incnt;
165 237
238 sched->sched_all(stream);
239
166 new->out = NULL; 240 new->out = NULL;
167 new->in = NULL; 241 new->in = NULL;
168} 242}