sctp: introduce round robin stream scheduler
authorMarcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Tue, 3 Oct 2017 22:20:17 +0000 (19:20 -0300)
committerDavid S. Miller <davem@davemloft.net>
Tue, 3 Oct 2017 23:27:29 +0000 (16:27 -0700)
This patch introduces RFC Draft ndata section 3.2 Priority Based
Scheduler (SCTP_SS_RR).

Works by maintaining a list of enqueued streams and tracking the last
one used to send data. When the datamsg is done, it switches to the next
stream.

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>
include/net/sctp/structs.h
include/uapi/linux/sctp.h
net/sctp/Makefile
net/sctp/stream_sched.c
net/sctp/stream_sched_rr.c [new file with mode: 0644]

index 40eb8d66a37c3ecee39141dc111663f7aac7326a..16f949eef52fdfd7c90fa15b44093334d1355aaf 100644 (file)
@@ -1348,6 +1348,10 @@ struct sctp_stream_out_ext {
                        struct list_head prio_list;
                        struct sctp_stream_priorities *prio_head;
                };
+               /* Fields used by RR scheduler */
+               struct {
+                       struct list_head rr_list;
+               };
        };
 };
 
@@ -1374,6 +1378,13 @@ struct sctp_stream {
                        /* List of priorities scheduled */
                        struct list_head prio_list;
                };
+               /* Fields used by RR scheduler */
+               struct {
+                       /* List of streams scheduled */
+                       struct list_head rr_list;
+                       /* The next stream stream in line */
+                       struct sctp_stream_out_ext *rr_next;
+               };
        };
 };
 
index 850fa8b29d7e8163dc4ee88af192309bb2535ae9..6cd7d416ca406e59d3214976fc425bb805f5c6cc 100644 (file)
@@ -1100,7 +1100,8 @@ struct sctp_add_streams {
 enum sctp_sched_type {
        SCTP_SS_FCFS,
        SCTP_SS_PRIO,
-       SCTP_SS_MAX = SCTP_SS_PRIO
+       SCTP_SS_RR,
+       SCTP_SS_MAX = SCTP_SS_RR
 };
 
 #endif /* _UAPI_SCTP_H */
index 647c9cfd4e95be4429d25792e5832d7be2efc5c8..bf90c53977190ff563c2b43af31afb7c431d4534 100644 (file)
@@ -12,7 +12,8 @@ sctp-y := sm_statetable.o sm_statefuns.o sm_sideeffect.o \
          inqueue.o outqueue.o ulpqueue.o \
          tsnmap.o bind_addr.o socket.o primitive.o \
          output.o input.o debug.o stream.o auth.o \
-         offload.o stream_sched.o stream_sched_prio.o
+         offload.o stream_sched.o stream_sched_prio.o \
+         stream_sched_rr.o
 
 sctp_probe-y := probe.o
 
index 115ddb7651695cca7417cb63004a1a59c93523b8..03513a9fa110b5317af4502f98ab37702c1eddb9 100644 (file)
@@ -122,10 +122,12 @@ static struct sctp_sched_ops sctp_sched_fcfs = {
 /* API to other parts of the stack */
 
 extern struct sctp_sched_ops sctp_sched_prio;
+extern struct sctp_sched_ops sctp_sched_rr;
 
 struct sctp_sched_ops *sctp_sched_ops[] = {
        &sctp_sched_fcfs,
        &sctp_sched_prio,
+       &sctp_sched_rr,
 };
 
 int sctp_sched_set_sched(struct sctp_association *asoc,
diff --git a/net/sctp/stream_sched_rr.c b/net/sctp/stream_sched_rr.c
new file mode 100644 (file)
index 0000000..7612a43
--- /dev/null
@@ -0,0 +1,201 @@
+/* SCTP kernel implementation
+ * (C) Copyright Red Hat Inc. 2017
+ *
+ * This file is part of the SCTP kernel implementation
+ *
+ * These functions manipulate sctp stream queue/scheduling.
+ *
+ * This SCTP implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This SCTP implementation is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *                 ************************
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Please send any bug reports or fixes you make to the
+ * email addresched(es):
+ *    lksctp developers <linux-sctp@vger.kernel.org>
+ *
+ * Written or modified by:
+ *    Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
+ */
+
+#include <linux/list.h>
+#include <net/sctp/sctp.h>
+#include <net/sctp/sm.h>
+#include <net/sctp/stream_sched.h>
+
+/* Priority handling
+ * RFC DRAFT ndata section 3.2
+ */
+static void sctp_sched_rr_unsched_all(struct sctp_stream *stream);
+
+static void sctp_sched_rr_next_stream(struct sctp_stream *stream)
+{
+       struct list_head *pos;
+
+       pos = stream->rr_next->rr_list.next;
+       if (pos == &stream->rr_list)
+               pos = pos->next;
+       stream->rr_next = list_entry(pos, struct sctp_stream_out_ext, rr_list);
+}
+
+static void sctp_sched_rr_unsched(struct sctp_stream *stream,
+                                 struct sctp_stream_out_ext *soute)
+{
+       if (stream->rr_next == soute)
+               /* Try to move to the next stream */
+               sctp_sched_rr_next_stream(stream);
+
+       list_del_init(&soute->rr_list);
+
+       /* If we have no other stream queued, clear next */
+       if (list_empty(&stream->rr_list))
+               stream->rr_next = NULL;
+}
+
+static void sctp_sched_rr_sched(struct sctp_stream *stream,
+                               struct sctp_stream_out_ext *soute)
+{
+       if (!list_empty(&soute->rr_list))
+               /* Already scheduled. */
+               return;
+
+       /* Schedule the stream */
+       list_add_tail(&soute->rr_list, &stream->rr_list);
+
+       if (!stream->rr_next)
+               stream->rr_next = soute;
+}
+
+static int sctp_sched_rr_set(struct sctp_stream *stream, __u16 sid,
+                            __u16 prio, gfp_t gfp)
+{
+       return 0;
+}
+
+static int sctp_sched_rr_get(struct sctp_stream *stream, __u16 sid,
+                            __u16 *value)
+{
+       return 0;
+}
+
+static int sctp_sched_rr_init(struct sctp_stream *stream)
+{
+       INIT_LIST_HEAD(&stream->rr_list);
+       stream->rr_next = NULL;
+
+       return 0;
+}
+
+static int sctp_sched_rr_init_sid(struct sctp_stream *stream, __u16 sid,
+                                 gfp_t gfp)
+{
+       INIT_LIST_HEAD(&stream->out[sid].ext->rr_list);
+
+       return 0;
+}
+
+static void sctp_sched_rr_free(struct sctp_stream *stream)
+{
+       sctp_sched_rr_unsched_all(stream);
+}
+
+static void sctp_sched_rr_enqueue(struct sctp_outq *q,
+                                 struct sctp_datamsg *msg)
+{
+       struct sctp_stream *stream;
+       struct sctp_chunk *ch;
+       __u16 sid;
+
+       ch = list_first_entry(&msg->chunks, struct sctp_chunk, frag_list);
+       sid = sctp_chunk_stream_no(ch);
+       stream = &q->asoc->stream;
+       sctp_sched_rr_sched(stream, stream->out[sid].ext);
+}
+
+static struct sctp_chunk *sctp_sched_rr_dequeue(struct sctp_outq *q)
+{
+       struct sctp_stream *stream = &q->asoc->stream;
+       struct sctp_stream_out_ext *soute;
+       struct sctp_chunk *ch = NULL;
+
+       /* Bail out quickly if queue is empty */
+       if (list_empty(&q->out_chunk_list))
+               goto out;
+
+       /* Find which chunk is next */
+       if (stream->out_curr)
+               soute = stream->out_curr->ext;
+       else
+               soute = stream->rr_next;
+       ch = list_entry(soute->outq.next, struct sctp_chunk, stream_list);
+
+       sctp_sched_dequeue_common(q, ch);
+
+out:
+       return ch;
+}
+
+static void sctp_sched_rr_dequeue_done(struct sctp_outq *q,
+                                      struct sctp_chunk *ch)
+{
+       struct sctp_stream_out_ext *soute;
+       __u16 sid;
+
+       /* Last chunk on that msg, move to the next stream */
+       sid = sctp_chunk_stream_no(ch);
+       soute = q->asoc->stream.out[sid].ext;
+
+       sctp_sched_rr_next_stream(&q->asoc->stream);
+
+       if (list_empty(&soute->outq))
+               sctp_sched_rr_unsched(&q->asoc->stream, soute);
+}
+
+static void sctp_sched_rr_sched_all(struct sctp_stream *stream)
+{
+       struct sctp_association *asoc;
+       struct sctp_stream_out_ext *soute;
+       struct sctp_chunk *ch;
+
+       asoc = container_of(stream, struct sctp_association, stream);
+       list_for_each_entry(ch, &asoc->outqueue.out_chunk_list, list) {
+               __u16 sid;
+
+               sid = sctp_chunk_stream_no(ch);
+               soute = stream->out[sid].ext;
+               if (soute)
+                       sctp_sched_rr_sched(stream, soute);
+       }
+}
+
+static void sctp_sched_rr_unsched_all(struct sctp_stream *stream)
+{
+       struct sctp_stream_out_ext *soute, *tmp;
+
+       list_for_each_entry_safe(soute, tmp, &stream->rr_list, rr_list)
+               sctp_sched_rr_unsched(stream, soute);
+}
+
+struct sctp_sched_ops sctp_sched_rr = {
+       .set = sctp_sched_rr_set,
+       .get = sctp_sched_rr_get,
+       .init = sctp_sched_rr_init,
+       .init_sid = sctp_sched_rr_init_sid,
+       .free = sctp_sched_rr_free,
+       .enqueue = sctp_sched_rr_enqueue,
+       .dequeue = sctp_sched_rr_dequeue,
+       .dequeue_done = sctp_sched_rr_dequeue_done,
+       .sched_all = sctp_sched_rr_sched_all,
+       .unsched_all = sctp_sched_rr_unsched_all,
+};