aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--include/net/sctp/stream_sched.h72
-rw-r--r--include/net/sctp/structs.h15
-rw-r--r--include/uapi/linux/sctp.h6
-rw-r--r--net/sctp/Makefile2
-rw-r--r--net/sctp/outqueue.c59
-rw-r--r--net/sctp/sm_sideeffect.c3
-rw-r--r--net/sctp/stream.c88
-rw-r--r--net/sctp/stream_sched.c270
8 files changed, 477 insertions, 38 deletions
diff --git a/include/net/sctp/stream_sched.h b/include/net/sctp/stream_sched.h
new file mode 100644
index 000000000000..c676550a4c7d
--- /dev/null
+++ b/include/net/sctp/stream_sched.h
@@ -0,0 +1,72 @@
1/* SCTP kernel implementation
2 * (C) Copyright Red Hat Inc. 2017
3 *
4 * These are definitions used by the stream schedulers, defined in RFC
5 * draft ndata (https://tools.ietf.org/html/draft-ietf-tsvwg-sctp-ndata-11)
6 *
7 * This SCTP implementation is free software;
8 * you can redistribute it and/or modify it under the terms of
9 * the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
11 * any later version.
12 *
13 * This SCTP implementation is distributed in the hope that it
14 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
15 * ************************
16 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 * See the GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with GNU CC; see the file COPYING. If not, see
21 * <http://www.gnu.org/licenses/>.
22 *
23 * Please send any bug reports or fixes you make to the
24 * email addresses:
25 * lksctp developers <linux-sctp@vger.kernel.org>
26 *
27 * Written or modified by:
28 * Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
29 */
30
31#ifndef __sctp_stream_sched_h__
32#define __sctp_stream_sched_h__
33
34struct sctp_sched_ops {
35 /* Property handling for a given stream */
36 int (*set)(struct sctp_stream *stream, __u16 sid, __u16 value,
37 gfp_t gfp);
38 int (*get)(struct sctp_stream *stream, __u16 sid, __u16 *value);
39
40 /* Init the specific scheduler */
41 int (*init)(struct sctp_stream *stream);
42 /* Init a stream */
43 int (*init_sid)(struct sctp_stream *stream, __u16 sid, gfp_t gfp);
44 /* Frees the entire thing */
45 void (*free)(struct sctp_stream *stream);
46
47 /* Enqueue a chunk */
48 void (*enqueue)(struct sctp_outq *q, struct sctp_datamsg *msg);
49 /* Dequeue a chunk */
50 struct sctp_chunk *(*dequeue)(struct sctp_outq *q);
51 /* Called only if the chunk fit the packet */
52 void (*dequeue_done)(struct sctp_outq *q, struct sctp_chunk *chunk);
53 /* Sched all chunks already enqueued */
54 void (*sched_all)(struct sctp_stream *steam);
55 /* Unched all chunks already enqueued */
56 void (*unsched_all)(struct sctp_stream *steam);
57};
58
59int sctp_sched_set_sched(struct sctp_association *asoc,
60 enum sctp_sched_type sched);
61int sctp_sched_get_sched(struct sctp_association *asoc);
62int sctp_sched_set_value(struct sctp_association *asoc, __u16 sid,
63 __u16 value, gfp_t gfp);
64int sctp_sched_get_value(struct sctp_association *asoc, __u16 sid,
65 __u16 *value);
66void sctp_sched_dequeue_done(struct sctp_outq *q, struct sctp_chunk *ch);
67
68void sctp_sched_dequeue_common(struct sctp_outq *q, struct sctp_chunk *ch);
69int sctp_sched_init_sid(struct sctp_stream *stream, __u16 sid, gfp_t gfp);
70struct sctp_sched_ops *sctp_sched_ops_from_stream(struct sctp_stream *stream);
71
72#endif /* __sctp_stream_sched_h__ */
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index c48f7999fe9b..3c22a30fd71b 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -84,7 +84,6 @@ struct sctp_ulpq;
84struct sctp_ep_common; 84struct sctp_ep_common;
85struct crypto_shash; 85struct crypto_shash;
86struct sctp_stream; 86struct sctp_stream;
87struct sctp_stream_out;
88 87
89 88
90#include <net/sctp/tsnmap.h> 89#include <net/sctp/tsnmap.h>
@@ -531,8 +530,12 @@ struct sctp_chunk {
531 /* How many times this chunk have been sent, for prsctp RTX policy */ 530 /* How many times this chunk have been sent, for prsctp RTX policy */
532 int sent_count; 531 int sent_count;
533 532
534 /* This is our link to the per-transport transmitted list. */ 533 union {
535 struct list_head transmitted_list; 534 /* This is our link to the per-transport transmitted list. */
535 struct list_head transmitted_list;
536 /* List in specific stream outq */
537 struct list_head stream_list;
538 };
536 539
537 /* This field is used by chunks that hold fragmented data. 540 /* This field is used by chunks that hold fragmented data.
538 * For the first fragment this is the list that holds the rest of 541 * For the first fragment this is the list that holds the rest of
@@ -1019,6 +1022,9 @@ struct sctp_outq {
1019 /* Data pending that has never been transmitted. */ 1022 /* Data pending that has never been transmitted. */
1020 struct list_head out_chunk_list; 1023 struct list_head out_chunk_list;
1021 1024
1025 /* Stream scheduler being used */
1026 struct sctp_sched_ops *sched;
1027
1022 unsigned int out_qlen; /* Total length of queued data chunks. */ 1028 unsigned int out_qlen; /* Total length of queued data chunks. */
1023 1029
1024 /* Error of send failed, may used in SCTP_SEND_FAILED event. */ 1030 /* Error of send failed, may used in SCTP_SEND_FAILED event. */
@@ -1325,6 +1331,7 @@ struct sctp_inithdr_host {
1325struct sctp_stream_out_ext { 1331struct sctp_stream_out_ext {
1326 __u64 abandoned_unsent[SCTP_PR_INDEX(MAX) + 1]; 1332 __u64 abandoned_unsent[SCTP_PR_INDEX(MAX) + 1];
1327 __u64 abandoned_sent[SCTP_PR_INDEX(MAX) + 1]; 1333 __u64 abandoned_sent[SCTP_PR_INDEX(MAX) + 1];
1334 struct list_head outq; /* chunks enqueued by this stream */
1328}; 1335};
1329 1336
1330struct sctp_stream_out { 1337struct sctp_stream_out {
@@ -1342,6 +1349,8 @@ struct sctp_stream {
1342 struct sctp_stream_in *in; 1349 struct sctp_stream_in *in;
1343 __u16 outcnt; 1350 __u16 outcnt;
1344 __u16 incnt; 1351 __u16 incnt;
1352 /* Current stream being sent, if any */
1353 struct sctp_stream_out *out_curr;
1345}; 1354};
1346 1355
1347#define SCTP_STREAM_CLOSED 0x00 1356#define SCTP_STREAM_CLOSED 0x00
diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h
index 6217ff8500a1..4487e7625ddb 100644
--- a/include/uapi/linux/sctp.h
+++ b/include/uapi/linux/sctp.h
@@ -1088,4 +1088,10 @@ struct sctp_add_streams {
1088 uint16_t sas_outstrms; 1088 uint16_t sas_outstrms;
1089}; 1089};
1090 1090
1091/* SCTP Stream schedulers */
1092enum sctp_sched_type {
1093 SCTP_SS_FCFS,
1094 SCTP_SS_MAX = SCTP_SS_FCFS
1095};
1096
1091#endif /* _UAPI_SCTP_H */ 1097#endif /* _UAPI_SCTP_H */
diff --git a/net/sctp/Makefile b/net/sctp/Makefile
index 70f1b570bab9..0f6e6d1d69fd 100644
--- a/net/sctp/Makefile
+++ b/net/sctp/Makefile
@@ -12,7 +12,7 @@ sctp-y := sm_statetable.o sm_statefuns.o sm_sideeffect.o \
12 inqueue.o outqueue.o ulpqueue.o \ 12 inqueue.o outqueue.o ulpqueue.o \
13 tsnmap.o bind_addr.o socket.o primitive.o \ 13 tsnmap.o bind_addr.o socket.o primitive.o \
14 output.o input.o debug.o stream.o auth.o \ 14 output.o input.o debug.o stream.o auth.o \
15 offload.o 15 offload.o stream_sched.o
16 16
17sctp_probe-y := probe.o 17sctp_probe-y := probe.o
18 18
diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c
index 746b07b7937d..4db012aa25f7 100644
--- a/net/sctp/outqueue.c
+++ b/net/sctp/outqueue.c
@@ -50,6 +50,7 @@
50 50
51#include <net/sctp/sctp.h> 51#include <net/sctp/sctp.h>
52#include <net/sctp/sm.h> 52#include <net/sctp/sm.h>
53#include <net/sctp/stream_sched.h>
53 54
54/* Declare internal functions here. */ 55/* Declare internal functions here. */
55static int sctp_acked(struct sctp_sackhdr *sack, __u32 tsn); 56static int sctp_acked(struct sctp_sackhdr *sack, __u32 tsn);
@@ -72,32 +73,38 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp);
72 73
73/* Add data to the front of the queue. */ 74/* Add data to the front of the queue. */
74static inline void sctp_outq_head_data(struct sctp_outq *q, 75static inline void sctp_outq_head_data(struct sctp_outq *q,
75 struct sctp_chunk *ch) 76 struct sctp_chunk *ch)
76{ 77{
78 struct sctp_stream_out_ext *oute;
79 __u16 stream;
80
77 list_add(&ch->list, &q->out_chunk_list); 81 list_add(&ch->list, &q->out_chunk_list);
78 q->out_qlen += ch->skb->len; 82 q->out_qlen += ch->skb->len;
83
84 stream = sctp_chunk_stream_no(ch);
85 oute = q->asoc->stream.out[stream].ext;
86 list_add(&ch->stream_list, &oute->outq);
79} 87}
80 88
81/* Take data from the front of the queue. */ 89/* Take data from the front of the queue. */
82static inline struct sctp_chunk *sctp_outq_dequeue_data(struct sctp_outq *q) 90static inline struct sctp_chunk *sctp_outq_dequeue_data(struct sctp_outq *q)
83{ 91{
84 struct sctp_chunk *ch = NULL; 92 return q->sched->dequeue(q);
85
86 if (!list_empty(&q->out_chunk_list)) {
87 struct list_head *entry = q->out_chunk_list.next;
88
89 ch = list_entry(entry, struct sctp_chunk, list);
90 list_del_init(entry);
91 q->out_qlen -= ch->skb->len;
92 }
93 return ch;
94} 93}
94
95/* Add data chunk to the end of the queue. */ 95/* Add data chunk to the end of the queue. */
96static inline void sctp_outq_tail_data(struct sctp_outq *q, 96static inline void sctp_outq_tail_data(struct sctp_outq *q,
97 struct sctp_chunk *ch) 97 struct sctp_chunk *ch)
98{ 98{
99 struct sctp_stream_out_ext *oute;
100 __u16 stream;
101
99 list_add_tail(&ch->list, &q->out_chunk_list); 102 list_add_tail(&ch->list, &q->out_chunk_list);
100 q->out_qlen += ch->skb->len; 103 q->out_qlen += ch->skb->len;
104
105 stream = sctp_chunk_stream_no(ch);
106 oute = q->asoc->stream.out[stream].ext;
107 list_add_tail(&ch->stream_list, &oute->outq);
101} 108}
102 109
103/* 110/*
@@ -207,6 +214,7 @@ void sctp_outq_init(struct sctp_association *asoc, struct sctp_outq *q)
207 INIT_LIST_HEAD(&q->retransmit); 214 INIT_LIST_HEAD(&q->retransmit);
208 INIT_LIST_HEAD(&q->sacked); 215 INIT_LIST_HEAD(&q->sacked);
209 INIT_LIST_HEAD(&q->abandoned); 216 INIT_LIST_HEAD(&q->abandoned);
217 sctp_sched_set_sched(asoc, SCTP_SS_FCFS);
210} 218}
211 219
212/* Free the outqueue structure and any related pending chunks. 220/* Free the outqueue structure and any related pending chunks.
@@ -258,6 +266,7 @@ static void __sctp_outq_teardown(struct sctp_outq *q)
258 266
259 /* Throw away any leftover data chunks. */ 267 /* Throw away any leftover data chunks. */
260 while ((chunk = sctp_outq_dequeue_data(q)) != NULL) { 268 while ((chunk = sctp_outq_dequeue_data(q)) != NULL) {
269 sctp_sched_dequeue_done(q, chunk);
261 270
262 /* Mark as send failure. */ 271 /* Mark as send failure. */
263 sctp_chunk_fail(chunk, q->error); 272 sctp_chunk_fail(chunk, q->error);
@@ -391,13 +400,14 @@ static int sctp_prsctp_prune_unsent(struct sctp_association *asoc,
391 struct sctp_outq *q = &asoc->outqueue; 400 struct sctp_outq *q = &asoc->outqueue;
392 struct sctp_chunk *chk, *temp; 401 struct sctp_chunk *chk, *temp;
393 402
403 q->sched->unsched_all(&asoc->stream);
404
394 list_for_each_entry_safe(chk, temp, &q->out_chunk_list, list) { 405 list_for_each_entry_safe(chk, temp, &q->out_chunk_list, list) {
395 if (!SCTP_PR_PRIO_ENABLED(chk->sinfo.sinfo_flags) || 406 if (!SCTP_PR_PRIO_ENABLED(chk->sinfo.sinfo_flags) ||
396 chk->sinfo.sinfo_timetolive <= sinfo->sinfo_timetolive) 407 chk->sinfo.sinfo_timetolive <= sinfo->sinfo_timetolive)
397 continue; 408 continue;
398 409
399 list_del_init(&chk->list); 410 sctp_sched_dequeue_common(q, chk);
400 q->out_qlen -= chk->skb->len;
401 asoc->sent_cnt_removable--; 411 asoc->sent_cnt_removable--;
402 asoc->abandoned_unsent[SCTP_PR_INDEX(PRIO)]++; 412 asoc->abandoned_unsent[SCTP_PR_INDEX(PRIO)]++;
403 if (chk->sinfo.sinfo_stream < asoc->stream.outcnt) { 413 if (chk->sinfo.sinfo_stream < asoc->stream.outcnt) {
@@ -415,6 +425,8 @@ static int sctp_prsctp_prune_unsent(struct sctp_association *asoc,
415 break; 425 break;
416 } 426 }
417 427
428 q->sched->sched_all(&asoc->stream);
429
418 return msg_len; 430 return msg_len;
419} 431}
420 432
@@ -1033,22 +1045,9 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
1033 while ((chunk = sctp_outq_dequeue_data(q)) != NULL) { 1045 while ((chunk = sctp_outq_dequeue_data(q)) != NULL) {
1034 __u32 sid = ntohs(chunk->subh.data_hdr->stream); 1046 __u32 sid = ntohs(chunk->subh.data_hdr->stream);
1035 1047
1036 /* RFC 2960 6.5 Every DATA chunk MUST carry a valid
1037 * stream identifier.
1038 */
1039 if (chunk->sinfo.sinfo_stream >= asoc->stream.outcnt) {
1040
1041 /* Mark as failed send. */
1042 sctp_chunk_fail(chunk, SCTP_ERROR_INV_STRM);
1043 if (asoc->peer.prsctp_capable &&
1044 SCTP_PR_PRIO_ENABLED(chunk->sinfo.sinfo_flags))
1045 asoc->sent_cnt_removable--;
1046 sctp_chunk_free(chunk);
1047 continue;
1048 }
1049
1050 /* Has this chunk expired? */ 1048 /* Has this chunk expired? */
1051 if (sctp_chunk_abandoned(chunk)) { 1049 if (sctp_chunk_abandoned(chunk)) {
1050 sctp_sched_dequeue_done(q, chunk);
1052 sctp_chunk_fail(chunk, 0); 1051 sctp_chunk_fail(chunk, 0);
1053 sctp_chunk_free(chunk); 1052 sctp_chunk_free(chunk);
1054 continue; 1053 continue;
@@ -1070,6 +1069,7 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
1070 new_transport = asoc->peer.active_path; 1069 new_transport = asoc->peer.active_path;
1071 if (new_transport->state == SCTP_UNCONFIRMED) { 1070 if (new_transport->state == SCTP_UNCONFIRMED) {
1072 WARN_ONCE(1, "Attempt to send packet on unconfirmed path."); 1071 WARN_ONCE(1, "Attempt to send packet on unconfirmed path.");
1072 sctp_sched_dequeue_done(q, chunk);
1073 sctp_chunk_fail(chunk, 0); 1073 sctp_chunk_fail(chunk, 0);
1074 sctp_chunk_free(chunk); 1074 sctp_chunk_free(chunk);
1075 continue; 1075 continue;
@@ -1133,6 +1133,11 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
1133 else 1133 else
1134 asoc->stats.oodchunks++; 1134 asoc->stats.oodchunks++;
1135 1135
1136 /* Only now it's safe to consider this
1137 * chunk as sent, sched-wise.
1138 */
1139 sctp_sched_dequeue_done(q, chunk);
1140
1136 break; 1141 break;
1137 1142
1138 default: 1143 default:
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index e6a2974e020e..402bfbb888cd 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -50,6 +50,7 @@
50#include <net/sock.h> 50#include <net/sock.h>
51#include <net/sctp/sctp.h> 51#include <net/sctp/sctp.h>
52#include <net/sctp/sm.h> 52#include <net/sctp/sm.h>
53#include <net/sctp/stream_sched.h>
53 54
54static int sctp_cmd_interpreter(enum sctp_event event_type, 55static int sctp_cmd_interpreter(enum sctp_event event_type,
55 union sctp_subtype subtype, 56 union sctp_subtype subtype,
@@ -1089,6 +1090,8 @@ static void sctp_cmd_send_msg(struct sctp_association *asoc,
1089 1090
1090 list_for_each_entry(chunk, &msg->chunks, frag_list) 1091 list_for_each_entry(chunk, &msg->chunks, frag_list)
1091 sctp_outq_tail(&asoc->outqueue, chunk, gfp); 1092 sctp_outq_tail(&asoc->outqueue, chunk, gfp);
1093
1094 asoc->outqueue.sched->enqueue(&asoc->outqueue, msg);
1092} 1095}
1093 1096
1094 1097
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}
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}