Linux Audio

Check our new training course

Open-source upstreaming

Need help get the support for your hardware in upstream Linux?
Loading...
Note: File does not exist in v5.9.
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/* SCTP kernel implementation
  3 * (C) Copyright Red Hat Inc. 2022
  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 *    Xin Long <lucien.xin@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/* Fair Capacity and Weighted Fair Queueing handling
 23 * RFC 8260 section 3.5 and 3.6
 24 */
 25static void sctp_sched_fc_unsched_all(struct sctp_stream *stream);
 26
 27static int sctp_sched_wfq_set(struct sctp_stream *stream, __u16 sid,
 28			      __u16 weight, gfp_t gfp)
 29{
 30	struct sctp_stream_out_ext *soute = SCTP_SO(stream, sid)->ext;
 31
 32	if (!weight)
 33		return -EINVAL;
 34
 35	soute->fc_weight = weight;
 36	return 0;
 37}
 38
 39static int sctp_sched_wfq_get(struct sctp_stream *stream, __u16 sid,
 40			      __u16 *value)
 41{
 42	struct sctp_stream_out_ext *soute = SCTP_SO(stream, sid)->ext;
 43
 44	*value = soute->fc_weight;
 45	return 0;
 46}
 47
 48static int sctp_sched_fc_set(struct sctp_stream *stream, __u16 sid,
 49			     __u16 weight, gfp_t gfp)
 50{
 51	return 0;
 52}
 53
 54static int sctp_sched_fc_get(struct sctp_stream *stream, __u16 sid,
 55			     __u16 *value)
 56{
 57	return 0;
 58}
 59
 60static int sctp_sched_fc_init(struct sctp_stream *stream)
 61{
 62	INIT_LIST_HEAD(&stream->fc_list);
 63
 64	return 0;
 65}
 66
 67static int sctp_sched_fc_init_sid(struct sctp_stream *stream, __u16 sid,
 68				  gfp_t gfp)
 69{
 70	struct sctp_stream_out_ext *soute = SCTP_SO(stream, sid)->ext;
 71
 72	INIT_LIST_HEAD(&soute->fc_list);
 73	soute->fc_length = 0;
 74	soute->fc_weight = 1;
 75
 76	return 0;
 77}
 78
 79static void sctp_sched_fc_free_sid(struct sctp_stream *stream, __u16 sid)
 80{
 81}
 82
 83static void sctp_sched_fc_sched(struct sctp_stream *stream,
 84				struct sctp_stream_out_ext *soute)
 85{
 86	struct sctp_stream_out_ext *pos;
 87
 88	if (!list_empty(&soute->fc_list))
 89		return;
 90
 91	list_for_each_entry(pos, &stream->fc_list, fc_list)
 92		if ((__u64)pos->fc_length * soute->fc_weight >=
 93		    (__u64)soute->fc_length * pos->fc_weight)
 94			break;
 95	list_add_tail(&soute->fc_list, &pos->fc_list);
 96}
 97
 98static void sctp_sched_fc_enqueue(struct sctp_outq *q,
 99				  struct sctp_datamsg *msg)
100{
101	struct sctp_stream *stream;
102	struct sctp_chunk *ch;
103	__u16 sid;
104
105	ch = list_first_entry(&msg->chunks, struct sctp_chunk, frag_list);
106	sid = sctp_chunk_stream_no(ch);
107	stream = &q->asoc->stream;
108	sctp_sched_fc_sched(stream, SCTP_SO(stream, sid)->ext);
109}
110
111static struct sctp_chunk *sctp_sched_fc_dequeue(struct sctp_outq *q)
112{
113	struct sctp_stream *stream = &q->asoc->stream;
114	struct sctp_stream_out_ext *soute;
115	struct sctp_chunk *ch;
116
117	/* Bail out quickly if queue is empty */
118	if (list_empty(&q->out_chunk_list))
119		return NULL;
120
121	/* Find which chunk is next */
122	if (stream->out_curr)
123		soute = stream->out_curr->ext;
124	else
125		soute = list_entry(stream->fc_list.next, struct sctp_stream_out_ext, fc_list);
126	ch = list_entry(soute->outq.next, struct sctp_chunk, stream_list);
127
128	sctp_sched_dequeue_common(q, ch);
129	return ch;
130}
131
132static void sctp_sched_fc_dequeue_done(struct sctp_outq *q,
133				       struct sctp_chunk *ch)
134{
135	struct sctp_stream *stream = &q->asoc->stream;
136	struct sctp_stream_out_ext *soute, *pos;
137	__u16 sid, i;
138
139	sid = sctp_chunk_stream_no(ch);
140	soute = SCTP_SO(stream, sid)->ext;
141	/* reduce all fc_lengths by U32_MAX / 4 if the current fc_length overflows. */
142	if (soute->fc_length > U32_MAX - ch->skb->len) {
143		for (i = 0; i < stream->outcnt; i++) {
144			pos = SCTP_SO(stream, i)->ext;
145			if (!pos)
146				continue;
147			if (pos->fc_length <= (U32_MAX >> 2)) {
148				pos->fc_length = 0;
149				continue;
150			}
151			pos->fc_length -= (U32_MAX >> 2);
152		}
153	}
154	soute->fc_length += ch->skb->len;
155
156	if (list_empty(&soute->outq)) {
157		list_del_init(&soute->fc_list);
158		return;
159	}
160
161	pos = soute;
162	list_for_each_entry_continue(pos, &stream->fc_list, fc_list)
163		if ((__u64)pos->fc_length * soute->fc_weight >=
164		    (__u64)soute->fc_length * pos->fc_weight)
165			break;
166	list_move_tail(&soute->fc_list, &pos->fc_list);
167}
168
169static void sctp_sched_fc_sched_all(struct sctp_stream *stream)
170{
171	struct sctp_association *asoc;
172	struct sctp_chunk *ch;
173
174	asoc = container_of(stream, struct sctp_association, stream);
175	list_for_each_entry(ch, &asoc->outqueue.out_chunk_list, list) {
176		__u16 sid = sctp_chunk_stream_no(ch);
177
178		if (SCTP_SO(stream, sid)->ext)
179			sctp_sched_fc_sched(stream, SCTP_SO(stream, sid)->ext);
180	}
181}
182
183static void sctp_sched_fc_unsched_all(struct sctp_stream *stream)
184{
185	struct sctp_stream_out_ext *soute, *tmp;
186
187	list_for_each_entry_safe(soute, tmp, &stream->fc_list, fc_list)
188		list_del_init(&soute->fc_list);
189}
190
191static struct sctp_sched_ops sctp_sched_fc = {
192	.set = sctp_sched_fc_set,
193	.get = sctp_sched_fc_get,
194	.init = sctp_sched_fc_init,
195	.init_sid = sctp_sched_fc_init_sid,
196	.free_sid = sctp_sched_fc_free_sid,
197	.enqueue = sctp_sched_fc_enqueue,
198	.dequeue = sctp_sched_fc_dequeue,
199	.dequeue_done = sctp_sched_fc_dequeue_done,
200	.sched_all = sctp_sched_fc_sched_all,
201	.unsched_all = sctp_sched_fc_unsched_all,
202};
203
204void sctp_sched_ops_fc_init(void)
205{
206	sctp_sched_ops_register(SCTP_SS_FC, &sctp_sched_fc);
207}
208
209static struct sctp_sched_ops sctp_sched_wfq = {
210	.set = sctp_sched_wfq_set,
211	.get = sctp_sched_wfq_get,
212	.init = sctp_sched_fc_init,
213	.init_sid = sctp_sched_fc_init_sid,
214	.free_sid = sctp_sched_fc_free_sid,
215	.enqueue = sctp_sched_fc_enqueue,
216	.dequeue = sctp_sched_fc_dequeue,
217	.dequeue_done = sctp_sched_fc_dequeue_done,
218	.sched_all = sctp_sched_fc_sched_all,
219	.unsched_all = sctp_sched_fc_unsched_all,
220};
221
222void sctp_sched_ops_wfq_init(void)
223{
224	sctp_sched_ops_register(SCTP_SS_WFQ, &sctp_sched_wfq);
225}