18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* SCTP kernel implementation 38c2ecf20Sopenharmony_ci * (C) Copyright Red Hat Inc. 2017 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This file is part of the SCTP kernel implementation 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * These functions manipulate sctp stream queue/scheduling. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Please send any bug reports or fixes you make to the 108c2ecf20Sopenharmony_ci * email addresched(es): 118c2ecf20Sopenharmony_ci * lksctp developers <linux-sctp@vger.kernel.org> 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Written or modified by: 148c2ecf20Sopenharmony_ci * Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/list.h> 188c2ecf20Sopenharmony_ci#include <net/sctp/sctp.h> 198c2ecf20Sopenharmony_ci#include <net/sctp/sm.h> 208c2ecf20Sopenharmony_ci#include <net/sctp/stream_sched.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* First Come First Serve (a.k.a. FIFO) 238c2ecf20Sopenharmony_ci * RFC DRAFT ndata Section 3.1 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_cistatic int sctp_sched_fcfs_set(struct sctp_stream *stream, __u16 sid, 268c2ecf20Sopenharmony_ci __u16 value, gfp_t gfp) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci return 0; 298c2ecf20Sopenharmony_ci} 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic int sctp_sched_fcfs_get(struct sctp_stream *stream, __u16 sid, 328c2ecf20Sopenharmony_ci __u16 *value) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci *value = 0; 358c2ecf20Sopenharmony_ci return 0; 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic int sctp_sched_fcfs_init(struct sctp_stream *stream) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci return 0; 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic int sctp_sched_fcfs_init_sid(struct sctp_stream *stream, __u16 sid, 448c2ecf20Sopenharmony_ci gfp_t gfp) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci return 0; 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic void sctp_sched_fcfs_free_sid(struct sctp_stream *stream, __u16 sid) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic void sctp_sched_fcfs_free(struct sctp_stream *stream) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic void sctp_sched_fcfs_enqueue(struct sctp_outq *q, 588c2ecf20Sopenharmony_ci struct sctp_datamsg *msg) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic struct sctp_chunk *sctp_sched_fcfs_dequeue(struct sctp_outq *q) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct sctp_stream *stream = &q->asoc->stream; 658c2ecf20Sopenharmony_ci struct sctp_chunk *ch = NULL; 668c2ecf20Sopenharmony_ci struct list_head *entry; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (list_empty(&q->out_chunk_list)) 698c2ecf20Sopenharmony_ci goto out; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (stream->out_curr) { 728c2ecf20Sopenharmony_ci ch = list_entry(stream->out_curr->ext->outq.next, 738c2ecf20Sopenharmony_ci struct sctp_chunk, stream_list); 748c2ecf20Sopenharmony_ci } else { 758c2ecf20Sopenharmony_ci entry = q->out_chunk_list.next; 768c2ecf20Sopenharmony_ci ch = list_entry(entry, struct sctp_chunk, list); 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci sctp_sched_dequeue_common(q, ch); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ciout: 828c2ecf20Sopenharmony_ci return ch; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic void sctp_sched_fcfs_dequeue_done(struct sctp_outq *q, 868c2ecf20Sopenharmony_ci struct sctp_chunk *chunk) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic void sctp_sched_fcfs_sched_all(struct sctp_stream *stream) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic void sctp_sched_fcfs_unsched_all(struct sctp_stream *stream) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic struct sctp_sched_ops sctp_sched_fcfs = { 998c2ecf20Sopenharmony_ci .set = sctp_sched_fcfs_set, 1008c2ecf20Sopenharmony_ci .get = sctp_sched_fcfs_get, 1018c2ecf20Sopenharmony_ci .init = sctp_sched_fcfs_init, 1028c2ecf20Sopenharmony_ci .init_sid = sctp_sched_fcfs_init_sid, 1038c2ecf20Sopenharmony_ci .free_sid = sctp_sched_fcfs_free_sid, 1048c2ecf20Sopenharmony_ci .free = sctp_sched_fcfs_free, 1058c2ecf20Sopenharmony_ci .enqueue = sctp_sched_fcfs_enqueue, 1068c2ecf20Sopenharmony_ci .dequeue = sctp_sched_fcfs_dequeue, 1078c2ecf20Sopenharmony_ci .dequeue_done = sctp_sched_fcfs_dequeue_done, 1088c2ecf20Sopenharmony_ci .sched_all = sctp_sched_fcfs_sched_all, 1098c2ecf20Sopenharmony_ci .unsched_all = sctp_sched_fcfs_unsched_all, 1108c2ecf20Sopenharmony_ci}; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic void sctp_sched_ops_fcfs_init(void) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci sctp_sched_ops_register(SCTP_SS_FCFS, &sctp_sched_fcfs); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci/* API to other parts of the stack */ 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic struct sctp_sched_ops *sctp_sched_ops[SCTP_SS_MAX + 1]; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_civoid sctp_sched_ops_register(enum sctp_sched_type sched, 1228c2ecf20Sopenharmony_ci struct sctp_sched_ops *sched_ops) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci sctp_sched_ops[sched] = sched_ops; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_civoid sctp_sched_ops_init(void) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci sctp_sched_ops_fcfs_init(); 1308c2ecf20Sopenharmony_ci sctp_sched_ops_prio_init(); 1318c2ecf20Sopenharmony_ci sctp_sched_ops_rr_init(); 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ciint sctp_sched_set_sched(struct sctp_association *asoc, 1358c2ecf20Sopenharmony_ci enum sctp_sched_type sched) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci struct sctp_sched_ops *n = sctp_sched_ops[sched]; 1388c2ecf20Sopenharmony_ci struct sctp_sched_ops *old = asoc->outqueue.sched; 1398c2ecf20Sopenharmony_ci struct sctp_datamsg *msg = NULL; 1408c2ecf20Sopenharmony_ci struct sctp_chunk *ch; 1418c2ecf20Sopenharmony_ci int i, ret = 0; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (old == n) 1448c2ecf20Sopenharmony_ci return ret; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (sched > SCTP_SS_MAX) 1478c2ecf20Sopenharmony_ci return -EINVAL; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (old) { 1508c2ecf20Sopenharmony_ci old->free(&asoc->stream); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* Give the next scheduler a clean slate. */ 1538c2ecf20Sopenharmony_ci for (i = 0; i < asoc->stream.outcnt; i++) { 1548c2ecf20Sopenharmony_ci void *p = SCTP_SO(&asoc->stream, i)->ext; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (!p) 1578c2ecf20Sopenharmony_ci continue; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci p += offsetofend(struct sctp_stream_out_ext, outq); 1608c2ecf20Sopenharmony_ci memset(p, 0, sizeof(struct sctp_stream_out_ext) - 1618c2ecf20Sopenharmony_ci offsetofend(struct sctp_stream_out_ext, outq)); 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci asoc->outqueue.sched = n; 1668c2ecf20Sopenharmony_ci n->init(&asoc->stream); 1678c2ecf20Sopenharmony_ci for (i = 0; i < asoc->stream.outcnt; i++) { 1688c2ecf20Sopenharmony_ci if (!SCTP_SO(&asoc->stream, i)->ext) 1698c2ecf20Sopenharmony_ci continue; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci ret = n->init_sid(&asoc->stream, i, GFP_ATOMIC); 1728c2ecf20Sopenharmony_ci if (ret) 1738c2ecf20Sopenharmony_ci goto err; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* We have to requeue all chunks already queued. */ 1778c2ecf20Sopenharmony_ci list_for_each_entry(ch, &asoc->outqueue.out_chunk_list, list) { 1788c2ecf20Sopenharmony_ci if (ch->msg == msg) 1798c2ecf20Sopenharmony_ci continue; 1808c2ecf20Sopenharmony_ci msg = ch->msg; 1818c2ecf20Sopenharmony_ci n->enqueue(&asoc->outqueue, msg); 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci return ret; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cierr: 1878c2ecf20Sopenharmony_ci n->free(&asoc->stream); 1888c2ecf20Sopenharmony_ci asoc->outqueue.sched = &sctp_sched_fcfs; /* Always safe */ 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci return ret; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ciint sctp_sched_get_sched(struct sctp_association *asoc) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci int i; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci for (i = 0; i <= SCTP_SS_MAX; i++) 1988c2ecf20Sopenharmony_ci if (asoc->outqueue.sched == sctp_sched_ops[i]) 1998c2ecf20Sopenharmony_ci return i; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci return 0; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ciint sctp_sched_set_value(struct sctp_association *asoc, __u16 sid, 2058c2ecf20Sopenharmony_ci __u16 value, gfp_t gfp) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci if (sid >= asoc->stream.outcnt) 2088c2ecf20Sopenharmony_ci return -EINVAL; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (!SCTP_SO(&asoc->stream, sid)->ext) { 2118c2ecf20Sopenharmony_ci int ret; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci ret = sctp_stream_init_ext(&asoc->stream, sid); 2148c2ecf20Sopenharmony_ci if (ret) 2158c2ecf20Sopenharmony_ci return ret; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci return asoc->outqueue.sched->set(&asoc->stream, sid, value, gfp); 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ciint sctp_sched_get_value(struct sctp_association *asoc, __u16 sid, 2228c2ecf20Sopenharmony_ci __u16 *value) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci if (sid >= asoc->stream.outcnt) 2258c2ecf20Sopenharmony_ci return -EINVAL; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (!SCTP_SO(&asoc->stream, sid)->ext) 2288c2ecf20Sopenharmony_ci return 0; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci return asoc->outqueue.sched->get(&asoc->stream, sid, value); 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_civoid sctp_sched_dequeue_done(struct sctp_outq *q, struct sctp_chunk *ch) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci if (!list_is_last(&ch->frag_list, &ch->msg->chunks) && 2368c2ecf20Sopenharmony_ci !q->asoc->peer.intl_capable) { 2378c2ecf20Sopenharmony_ci struct sctp_stream_out *sout; 2388c2ecf20Sopenharmony_ci __u16 sid; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* datamsg is not finish, so save it as current one, 2418c2ecf20Sopenharmony_ci * in case application switch scheduler or a higher 2428c2ecf20Sopenharmony_ci * priority stream comes in. 2438c2ecf20Sopenharmony_ci */ 2448c2ecf20Sopenharmony_ci sid = sctp_chunk_stream_no(ch); 2458c2ecf20Sopenharmony_ci sout = SCTP_SO(&q->asoc->stream, sid); 2468c2ecf20Sopenharmony_ci q->asoc->stream.out_curr = sout; 2478c2ecf20Sopenharmony_ci return; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci q->asoc->stream.out_curr = NULL; 2518c2ecf20Sopenharmony_ci q->sched->dequeue_done(q, ch); 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci/* Auxiliary functions for the schedulers */ 2558c2ecf20Sopenharmony_civoid sctp_sched_dequeue_common(struct sctp_outq *q, struct sctp_chunk *ch) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci list_del_init(&ch->list); 2588c2ecf20Sopenharmony_ci list_del_init(&ch->stream_list); 2598c2ecf20Sopenharmony_ci q->out_qlen -= ch->skb->len; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ciint sctp_sched_init_sid(struct sctp_stream *stream, __u16 sid, gfp_t gfp) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream); 2658c2ecf20Sopenharmony_ci struct sctp_stream_out_ext *ext = SCTP_SO(stream, sid)->ext; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ext->outq); 2688c2ecf20Sopenharmony_ci return sched->init_sid(stream, sid, gfp); 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistruct sctp_sched_ops *sctp_sched_ops_from_stream(struct sctp_stream *stream) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct sctp_association *asoc; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci asoc = container_of(stream, struct sctp_association, stream); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci return asoc->outqueue.sched; 2788c2ecf20Sopenharmony_ci} 279