Skip to content

Commit 5bbbbe3

Browse files
marceloleitnerdavem330
authored andcommitted
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 <[email protected]> Signed-off-by: Marcelo Ricardo Leitner <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 2fc019f commit 5bbbbe3

File tree

8 files changed

+477
-38
lines changed

8 files changed

+477
-38
lines changed

include/net/sctp/stream_sched.h

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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 <[email protected]>
26+
*
27+
* Written or modified by:
28+
* Marcelo Ricardo Leitner <[email protected]>
29+
*/
30+
31+
#ifndef __sctp_stream_sched_h__
32+
#define __sctp_stream_sched_h__
33+
34+
struct 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+
59+
int sctp_sched_set_sched(struct sctp_association *asoc,
60+
enum sctp_sched_type sched);
61+
int sctp_sched_get_sched(struct sctp_association *asoc);
62+
int sctp_sched_set_value(struct sctp_association *asoc, __u16 sid,
63+
__u16 value, gfp_t gfp);
64+
int sctp_sched_get_value(struct sctp_association *asoc, __u16 sid,
65+
__u16 *value);
66+
void sctp_sched_dequeue_done(struct sctp_outq *q, struct sctp_chunk *ch);
67+
68+
void sctp_sched_dequeue_common(struct sctp_outq *q, struct sctp_chunk *ch);
69+
int sctp_sched_init_sid(struct sctp_stream *stream, __u16 sid, gfp_t gfp);
70+
struct sctp_sched_ops *sctp_sched_ops_from_stream(struct sctp_stream *stream);
71+
72+
#endif /* __sctp_stream_sched_h__ */

include/net/sctp/structs.h

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ struct sctp_ulpq;
8484
struct sctp_ep_common;
8585
struct crypto_shash;
8686
struct sctp_stream;
87-
struct sctp_stream_out;
8887

8988

9089
#include <net/sctp/tsnmap.h>
@@ -531,8 +530,12 @@ struct sctp_chunk {
531530
/* How many times this chunk have been sent, for prsctp RTX policy */
532531
int sent_count;
533532

534-
/* This is our link to the per-transport transmitted list. */
535-
struct list_head transmitted_list;
533+
union {
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+
};
536539

537540
/* This field is used by chunks that hold fragmented data.
538541
* For the first fragment this is the list that holds the rest of
@@ -1019,6 +1022,9 @@ struct sctp_outq {
10191022
/* Data pending that has never been transmitted. */
10201023
struct list_head out_chunk_list;
10211024

1025+
/* Stream scheduler being used */
1026+
struct sctp_sched_ops *sched;
1027+
10221028
unsigned int out_qlen; /* Total length of queued data chunks. */
10231029

10241030
/* Error of send failed, may used in SCTP_SEND_FAILED event. */
@@ -1325,6 +1331,7 @@ struct sctp_inithdr_host {
13251331
struct sctp_stream_out_ext {
13261332
__u64 abandoned_unsent[SCTP_PR_INDEX(MAX) + 1];
13271333
__u64 abandoned_sent[SCTP_PR_INDEX(MAX) + 1];
1334+
struct list_head outq; /* chunks enqueued by this stream */
13281335
};
13291336

13301337
struct sctp_stream_out {
@@ -1342,6 +1349,8 @@ struct sctp_stream {
13421349
struct sctp_stream_in *in;
13431350
__u16 outcnt;
13441351
__u16 incnt;
1352+
/* Current stream being sent, if any */
1353+
struct sctp_stream_out *out_curr;
13451354
};
13461355

13471356
#define SCTP_STREAM_CLOSED 0x00

include/uapi/linux/sctp.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,4 +1088,10 @@ struct sctp_add_streams {
10881088
uint16_t sas_outstrms;
10891089
};
10901090

1091+
/* SCTP Stream schedulers */
1092+
enum sctp_sched_type {
1093+
SCTP_SS_FCFS,
1094+
SCTP_SS_MAX = SCTP_SS_FCFS
1095+
};
1096+
10911097
#endif /* _UAPI_SCTP_H */

net/sctp/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ sctp-y := sm_statetable.o sm_statefuns.o sm_sideeffect.o \
1212
inqueue.o outqueue.o ulpqueue.o \
1313
tsnmap.o bind_addr.o socket.o primitive.o \
1414
output.o input.o debug.o stream.o auth.o \
15-
offload.o
15+
offload.o stream_sched.o
1616

1717
sctp_probe-y := probe.o
1818

net/sctp/outqueue.c

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050

5151
#include <net/sctp/sctp.h>
5252
#include <net/sctp/sm.h>
53+
#include <net/sctp/stream_sched.h>
5354

5455
/* Declare internal functions here. */
5556
static 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);
7273

7374
/* Add data to the front of the queue. */
7475
static inline void sctp_outq_head_data(struct sctp_outq *q,
75-
struct sctp_chunk *ch)
76+
struct sctp_chunk *ch)
7677
{
78+
struct sctp_stream_out_ext *oute;
79+
__u16 stream;
80+
7781
list_add(&ch->list, &q->out_chunk_list);
7882
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);
7987
}
8088

8189
/* Take data from the front of the queue. */
8290
static inline struct sctp_chunk *sctp_outq_dequeue_data(struct sctp_outq *q)
8391
{
84-
struct sctp_chunk *ch = NULL;
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;
92+
return q->sched->dequeue(q);
9493
}
94+
9595
/* Add data chunk to the end of the queue. */
9696
static inline void sctp_outq_tail_data(struct sctp_outq *q,
9797
struct sctp_chunk *ch)
9898
{
99+
struct sctp_stream_out_ext *oute;
100+
__u16 stream;
101+
99102
list_add_tail(&ch->list, &q->out_chunk_list);
100103
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);
101108
}
102109

103110
/*
@@ -207,6 +214,7 @@ void sctp_outq_init(struct sctp_association *asoc, struct sctp_outq *q)
207214
INIT_LIST_HEAD(&q->retransmit);
208215
INIT_LIST_HEAD(&q->sacked);
209216
INIT_LIST_HEAD(&q->abandoned);
217+
sctp_sched_set_sched(asoc, SCTP_SS_FCFS);
210218
}
211219

212220
/* Free the outqueue structure and any related pending chunks.
@@ -258,6 +266,7 @@ static void __sctp_outq_teardown(struct sctp_outq *q)
258266

259267
/* Throw away any leftover data chunks. */
260268
while ((chunk = sctp_outq_dequeue_data(q)) != NULL) {
269+
sctp_sched_dequeue_done(q, chunk);
261270

262271
/* Mark as send failure. */
263272
sctp_chunk_fail(chunk, q->error);
@@ -391,13 +400,14 @@ static int sctp_prsctp_prune_unsent(struct sctp_association *asoc,
391400
struct sctp_outq *q = &asoc->outqueue;
392401
struct sctp_chunk *chk, *temp;
393402

403+
q->sched->unsched_all(&asoc->stream);
404+
394405
list_for_each_entry_safe(chk, temp, &q->out_chunk_list, list) {
395406
if (!SCTP_PR_PRIO_ENABLED(chk->sinfo.sinfo_flags) ||
396407
chk->sinfo.sinfo_timetolive <= sinfo->sinfo_timetolive)
397408
continue;
398409

399-
list_del_init(&chk->list);
400-
q->out_qlen -= chk->skb->len;
410+
sctp_sched_dequeue_common(q, chk);
401411
asoc->sent_cnt_removable--;
402412
asoc->abandoned_unsent[SCTP_PR_INDEX(PRIO)]++;
403413
if (chk->sinfo.sinfo_stream < asoc->stream.outcnt) {
@@ -415,6 +425,8 @@ static int sctp_prsctp_prune_unsent(struct sctp_association *asoc,
415425
break;
416426
}
417427

428+
q->sched->sched_all(&asoc->stream);
429+
418430
return msg_len;
419431
}
420432

@@ -1033,22 +1045,9 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
10331045
while ((chunk = sctp_outq_dequeue_data(q)) != NULL) {
10341046
__u32 sid = ntohs(chunk->subh.data_hdr->stream);
10351047

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-
10501048
/* Has this chunk expired? */
10511049
if (sctp_chunk_abandoned(chunk)) {
1050+
sctp_sched_dequeue_done(q, chunk);
10521051
sctp_chunk_fail(chunk, 0);
10531052
sctp_chunk_free(chunk);
10541053
continue;
@@ -1070,6 +1069,7 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
10701069
new_transport = asoc->peer.active_path;
10711070
if (new_transport->state == SCTP_UNCONFIRMED) {
10721071
WARN_ONCE(1, "Attempt to send packet on unconfirmed path.");
1072+
sctp_sched_dequeue_done(q, chunk);
10731073
sctp_chunk_fail(chunk, 0);
10741074
sctp_chunk_free(chunk);
10751075
continue;
@@ -1133,6 +1133,11 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
11331133
else
11341134
asoc->stats.oodchunks++;
11351135

1136+
/* Only now it's safe to consider this
1137+
* chunk as sent, sched-wise.
1138+
*/
1139+
sctp_sched_dequeue_done(q, chunk);
1140+
11361141
break;
11371142

11381143
default:

net/sctp/sm_sideeffect.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
#include <net/sock.h>
5151
#include <net/sctp/sctp.h>
5252
#include <net/sctp/sm.h>
53+
#include <net/sctp/stream_sched.h>
5354

5455
static int sctp_cmd_interpreter(enum sctp_event event_type,
5556
union sctp_subtype subtype,
@@ -1089,6 +1090,8 @@ static void sctp_cmd_send_msg(struct sctp_association *asoc,
10891090

10901091
list_for_each_entry(chunk, &msg->chunks, frag_list)
10911092
sctp_outq_tail(&asoc->outqueue, chunk, gfp);
1093+
1094+
asoc->outqueue.sched->enqueue(&asoc->outqueue, msg);
10921095
}
10931096

10941097

0 commit comments

Comments
 (0)