1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* SCTP kernel implementation |
3 | * (C) Copyright Red Hat Inc. 2017 |
4 | * |
5 | * This file is part of the SCTP kernel implementation |
6 | * |
7 | * These functions manipulate sctp stream queue/scheduling. |
8 | * |
9 | * Please send any bug reports or fixes you make to the |
10 | * email addresched(es): |
11 | * lksctp developers <linux-sctp@vger.kernel.org> |
12 | * |
13 | * Written or modified by: |
14 | * Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> |
15 | */ |
16 | |
17 | #include <linux/list.h> |
18 | #include <net/sctp/sctp.h> |
19 | #include <net/sctp/sm.h> |
20 | #include <net/sctp/stream_sched.h> |
21 | |
22 | /* Priority handling |
23 | * RFC DRAFT ndata section 3.2 |
24 | */ |
25 | static void sctp_sched_rr_unsched_all(struct sctp_stream *stream); |
26 | |
27 | static void sctp_sched_rr_next_stream(struct sctp_stream *stream) |
28 | { |
29 | struct list_head *pos; |
30 | |
31 | pos = stream->rr_next->rr_list.next; |
32 | if (pos == &stream->rr_list) |
33 | pos = pos->next; |
34 | stream->rr_next = list_entry(pos, struct sctp_stream_out_ext, rr_list); |
35 | } |
36 | |
37 | static void sctp_sched_rr_unsched(struct sctp_stream *stream, |
38 | struct sctp_stream_out_ext *soute) |
39 | { |
40 | if (stream->rr_next == soute) |
41 | /* Try to move to the next stream */ |
42 | sctp_sched_rr_next_stream(stream); |
43 | |
44 | list_del_init(entry: &soute->rr_list); |
45 | |
46 | /* If we have no other stream queued, clear next */ |
47 | if (list_empty(head: &stream->rr_list)) |
48 | stream->rr_next = NULL; |
49 | } |
50 | |
51 | static void sctp_sched_rr_sched(struct sctp_stream *stream, |
52 | struct sctp_stream_out_ext *soute) |
53 | { |
54 | if (!list_empty(head: &soute->rr_list)) |
55 | /* Already scheduled. */ |
56 | return; |
57 | |
58 | /* Schedule the stream */ |
59 | list_add_tail(new: &soute->rr_list, head: &stream->rr_list); |
60 | |
61 | if (!stream->rr_next) |
62 | stream->rr_next = soute; |
63 | } |
64 | |
65 | static int sctp_sched_rr_set(struct sctp_stream *stream, __u16 sid, |
66 | __u16 prio, gfp_t gfp) |
67 | { |
68 | return 0; |
69 | } |
70 | |
71 | static int sctp_sched_rr_get(struct sctp_stream *stream, __u16 sid, |
72 | __u16 *value) |
73 | { |
74 | return 0; |
75 | } |
76 | |
77 | static int sctp_sched_rr_init(struct sctp_stream *stream) |
78 | { |
79 | INIT_LIST_HEAD(list: &stream->rr_list); |
80 | stream->rr_next = NULL; |
81 | |
82 | return 0; |
83 | } |
84 | |
85 | static int sctp_sched_rr_init_sid(struct sctp_stream *stream, __u16 sid, |
86 | gfp_t gfp) |
87 | { |
88 | INIT_LIST_HEAD(list: &SCTP_SO(stream, sid)->ext->rr_list); |
89 | |
90 | return 0; |
91 | } |
92 | |
93 | static void sctp_sched_rr_free_sid(struct sctp_stream *stream, __u16 sid) |
94 | { |
95 | } |
96 | |
97 | static void sctp_sched_rr_enqueue(struct sctp_outq *q, |
98 | struct sctp_datamsg *msg) |
99 | { |
100 | struct sctp_stream *stream; |
101 | struct sctp_chunk *ch; |
102 | __u16 sid; |
103 | |
104 | ch = list_first_entry(&msg->chunks, struct sctp_chunk, frag_list); |
105 | sid = sctp_chunk_stream_no(ch); |
106 | stream = &q->asoc->stream; |
107 | sctp_sched_rr_sched(stream, SCTP_SO(stream, sid)->ext); |
108 | } |
109 | |
110 | static struct sctp_chunk *sctp_sched_rr_dequeue(struct sctp_outq *q) |
111 | { |
112 | struct sctp_stream *stream = &q->asoc->stream; |
113 | struct sctp_stream_out_ext *soute; |
114 | struct sctp_chunk *ch = NULL; |
115 | |
116 | /* Bail out quickly if queue is empty */ |
117 | if (list_empty(head: &q->out_chunk_list)) |
118 | goto out; |
119 | |
120 | /* Find which chunk is next */ |
121 | if (stream->out_curr) |
122 | soute = stream->out_curr->ext; |
123 | else |
124 | soute = stream->rr_next; |
125 | ch = list_entry(soute->outq.next, struct sctp_chunk, stream_list); |
126 | |
127 | sctp_sched_dequeue_common(q, ch); |
128 | |
129 | out: |
130 | return ch; |
131 | } |
132 | |
133 | static void sctp_sched_rr_dequeue_done(struct sctp_outq *q, |
134 | struct sctp_chunk *ch) |
135 | { |
136 | struct sctp_stream_out_ext *soute; |
137 | __u16 sid; |
138 | |
139 | /* Last chunk on that msg, move to the next stream */ |
140 | sid = sctp_chunk_stream_no(ch); |
141 | soute = SCTP_SO(&q->asoc->stream, sid)->ext; |
142 | |
143 | sctp_sched_rr_next_stream(stream: &q->asoc->stream); |
144 | |
145 | if (list_empty(head: &soute->outq)) |
146 | sctp_sched_rr_unsched(stream: &q->asoc->stream, soute); |
147 | } |
148 | |
149 | static void sctp_sched_rr_sched_all(struct sctp_stream *stream) |
150 | { |
151 | struct sctp_association *asoc; |
152 | struct sctp_stream_out_ext *soute; |
153 | struct sctp_chunk *ch; |
154 | |
155 | asoc = container_of(stream, struct sctp_association, stream); |
156 | list_for_each_entry(ch, &asoc->outqueue.out_chunk_list, list) { |
157 | __u16 sid; |
158 | |
159 | sid = sctp_chunk_stream_no(ch); |
160 | soute = SCTP_SO(stream, sid)->ext; |
161 | if (soute) |
162 | sctp_sched_rr_sched(stream, soute); |
163 | } |
164 | } |
165 | |
166 | static void sctp_sched_rr_unsched_all(struct sctp_stream *stream) |
167 | { |
168 | struct sctp_stream_out_ext *soute, *tmp; |
169 | |
170 | list_for_each_entry_safe(soute, tmp, &stream->rr_list, rr_list) |
171 | sctp_sched_rr_unsched(stream, soute); |
172 | } |
173 | |
174 | static struct sctp_sched_ops sctp_sched_rr = { |
175 | .set = sctp_sched_rr_set, |
176 | .get = sctp_sched_rr_get, |
177 | .init = sctp_sched_rr_init, |
178 | .init_sid = sctp_sched_rr_init_sid, |
179 | .free_sid = sctp_sched_rr_free_sid, |
180 | .enqueue = sctp_sched_rr_enqueue, |
181 | .dequeue = sctp_sched_rr_dequeue, |
182 | .dequeue_done = sctp_sched_rr_dequeue_done, |
183 | .sched_all = sctp_sched_rr_sched_all, |
184 | .unsched_all = sctp_sched_rr_unsched_all, |
185 | }; |
186 | |
187 | void sctp_sched_ops_rr_init(void) |
188 | { |
189 | sctp_sched_ops_register(sched: SCTP_SS_RR, sched_ops: &sctp_sched_rr); |
190 | } |
191 | |