18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * net/sched/sch_fifo.c The simplest FIFO queue. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci#include <linux/types.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/errno.h> 138c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 148c2ecf20Sopenharmony_ci#include <net/pkt_sched.h> 158c2ecf20Sopenharmony_ci#include <net/pkt_cls.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* 1 band FIFO pseudo-"scheduler" */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic int bfifo_enqueue(struct sk_buff *skb, struct Qdisc *sch, 208c2ecf20Sopenharmony_ci struct sk_buff **to_free) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci if (likely(sch->qstats.backlog + qdisc_pkt_len(skb) <= sch->limit)) 238c2ecf20Sopenharmony_ci return qdisc_enqueue_tail(skb, sch); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci return qdisc_drop(skb, sch, to_free); 268c2ecf20Sopenharmony_ci} 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic int pfifo_enqueue(struct sk_buff *skb, struct Qdisc *sch, 298c2ecf20Sopenharmony_ci struct sk_buff **to_free) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci if (likely(sch->q.qlen < sch->limit)) 328c2ecf20Sopenharmony_ci return qdisc_enqueue_tail(skb, sch); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci return qdisc_drop(skb, sch, to_free); 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic int pfifo_tail_enqueue(struct sk_buff *skb, struct Qdisc *sch, 388c2ecf20Sopenharmony_ci struct sk_buff **to_free) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci unsigned int prev_backlog; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci if (likely(sch->q.qlen < sch->limit)) 438c2ecf20Sopenharmony_ci return qdisc_enqueue_tail(skb, sch); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci prev_backlog = sch->qstats.backlog; 468c2ecf20Sopenharmony_ci /* queue full, remove one skb to fulfill the limit */ 478c2ecf20Sopenharmony_ci __qdisc_queue_drop_head(sch, &sch->q, to_free); 488c2ecf20Sopenharmony_ci qdisc_qstats_drop(sch); 498c2ecf20Sopenharmony_ci qdisc_enqueue_tail(skb, sch); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci qdisc_tree_reduce_backlog(sch, 0, prev_backlog - sch->qstats.backlog); 528c2ecf20Sopenharmony_ci return NET_XMIT_CN; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic void fifo_offload_init(struct Qdisc *sch) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci struct net_device *dev = qdisc_dev(sch); 588c2ecf20Sopenharmony_ci struct tc_fifo_qopt_offload qopt; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc) 618c2ecf20Sopenharmony_ci return; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci qopt.command = TC_FIFO_REPLACE; 648c2ecf20Sopenharmony_ci qopt.handle = sch->handle; 658c2ecf20Sopenharmony_ci qopt.parent = sch->parent; 668c2ecf20Sopenharmony_ci dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_FIFO, &qopt); 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic void fifo_offload_destroy(struct Qdisc *sch) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct net_device *dev = qdisc_dev(sch); 728c2ecf20Sopenharmony_ci struct tc_fifo_qopt_offload qopt; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc) 758c2ecf20Sopenharmony_ci return; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci qopt.command = TC_FIFO_DESTROY; 788c2ecf20Sopenharmony_ci qopt.handle = sch->handle; 798c2ecf20Sopenharmony_ci qopt.parent = sch->parent; 808c2ecf20Sopenharmony_ci dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_FIFO, &qopt); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic int fifo_offload_dump(struct Qdisc *sch) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct tc_fifo_qopt_offload qopt; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci qopt.command = TC_FIFO_STATS; 888c2ecf20Sopenharmony_ci qopt.handle = sch->handle; 898c2ecf20Sopenharmony_ci qopt.parent = sch->parent; 908c2ecf20Sopenharmony_ci qopt.stats.bstats = &sch->bstats; 918c2ecf20Sopenharmony_ci qopt.stats.qstats = &sch->qstats; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci return qdisc_offload_dump_helper(sch, TC_SETUP_QDISC_FIFO, &qopt); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic int __fifo_init(struct Qdisc *sch, struct nlattr *opt, 978c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci bool bypass; 1008c2ecf20Sopenharmony_ci bool is_bfifo = sch->ops == &bfifo_qdisc_ops; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (opt == NULL) { 1038c2ecf20Sopenharmony_ci u32 limit = qdisc_dev(sch)->tx_queue_len; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (is_bfifo) 1068c2ecf20Sopenharmony_ci limit *= psched_mtu(qdisc_dev(sch)); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci sch->limit = limit; 1098c2ecf20Sopenharmony_ci } else { 1108c2ecf20Sopenharmony_ci struct tc_fifo_qopt *ctl = nla_data(opt); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (nla_len(opt) < sizeof(*ctl)) 1138c2ecf20Sopenharmony_ci return -EINVAL; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci sch->limit = ctl->limit; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (is_bfifo) 1198c2ecf20Sopenharmony_ci bypass = sch->limit >= psched_mtu(qdisc_dev(sch)); 1208c2ecf20Sopenharmony_ci else 1218c2ecf20Sopenharmony_ci bypass = sch->limit >= 1; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (bypass) 1248c2ecf20Sopenharmony_ci sch->flags |= TCQ_F_CAN_BYPASS; 1258c2ecf20Sopenharmony_ci else 1268c2ecf20Sopenharmony_ci sch->flags &= ~TCQ_F_CAN_BYPASS; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return 0; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic int fifo_init(struct Qdisc *sch, struct nlattr *opt, 1328c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci int err; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci err = __fifo_init(sch, opt, extack); 1378c2ecf20Sopenharmony_ci if (err) 1388c2ecf20Sopenharmony_ci return err; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci fifo_offload_init(sch); 1418c2ecf20Sopenharmony_ci return 0; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic int fifo_hd_init(struct Qdisc *sch, struct nlattr *opt, 1458c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci return __fifo_init(sch, opt, extack); 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic void fifo_destroy(struct Qdisc *sch) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci fifo_offload_destroy(sch); 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int __fifo_dump(struct Qdisc *sch, struct sk_buff *skb) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct tc_fifo_qopt opt = { .limit = sch->limit }; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (nla_put(skb, TCA_OPTIONS, sizeof(opt), &opt)) 1608c2ecf20Sopenharmony_ci goto nla_put_failure; 1618c2ecf20Sopenharmony_ci return skb->len; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cinla_put_failure: 1648c2ecf20Sopenharmony_ci return -1; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic int fifo_dump(struct Qdisc *sch, struct sk_buff *skb) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci int err; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci err = fifo_offload_dump(sch); 1728c2ecf20Sopenharmony_ci if (err) 1738c2ecf20Sopenharmony_ci return err; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci return __fifo_dump(sch, skb); 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic int fifo_hd_dump(struct Qdisc *sch, struct sk_buff *skb) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci return __fifo_dump(sch, skb); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistruct Qdisc_ops pfifo_qdisc_ops __read_mostly = { 1848c2ecf20Sopenharmony_ci .id = "pfifo", 1858c2ecf20Sopenharmony_ci .priv_size = 0, 1868c2ecf20Sopenharmony_ci .enqueue = pfifo_enqueue, 1878c2ecf20Sopenharmony_ci .dequeue = qdisc_dequeue_head, 1888c2ecf20Sopenharmony_ci .peek = qdisc_peek_head, 1898c2ecf20Sopenharmony_ci .init = fifo_init, 1908c2ecf20Sopenharmony_ci .destroy = fifo_destroy, 1918c2ecf20Sopenharmony_ci .reset = qdisc_reset_queue, 1928c2ecf20Sopenharmony_ci .change = fifo_init, 1938c2ecf20Sopenharmony_ci .dump = fifo_dump, 1948c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1958c2ecf20Sopenharmony_ci}; 1968c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pfifo_qdisc_ops); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistruct Qdisc_ops bfifo_qdisc_ops __read_mostly = { 1998c2ecf20Sopenharmony_ci .id = "bfifo", 2008c2ecf20Sopenharmony_ci .priv_size = 0, 2018c2ecf20Sopenharmony_ci .enqueue = bfifo_enqueue, 2028c2ecf20Sopenharmony_ci .dequeue = qdisc_dequeue_head, 2038c2ecf20Sopenharmony_ci .peek = qdisc_peek_head, 2048c2ecf20Sopenharmony_ci .init = fifo_init, 2058c2ecf20Sopenharmony_ci .destroy = fifo_destroy, 2068c2ecf20Sopenharmony_ci .reset = qdisc_reset_queue, 2078c2ecf20Sopenharmony_ci .change = fifo_init, 2088c2ecf20Sopenharmony_ci .dump = fifo_dump, 2098c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2108c2ecf20Sopenharmony_ci}; 2118c2ecf20Sopenharmony_ciEXPORT_SYMBOL(bfifo_qdisc_ops); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistruct Qdisc_ops pfifo_head_drop_qdisc_ops __read_mostly = { 2148c2ecf20Sopenharmony_ci .id = "pfifo_head_drop", 2158c2ecf20Sopenharmony_ci .priv_size = 0, 2168c2ecf20Sopenharmony_ci .enqueue = pfifo_tail_enqueue, 2178c2ecf20Sopenharmony_ci .dequeue = qdisc_dequeue_head, 2188c2ecf20Sopenharmony_ci .peek = qdisc_peek_head, 2198c2ecf20Sopenharmony_ci .init = fifo_hd_init, 2208c2ecf20Sopenharmony_ci .reset = qdisc_reset_queue, 2218c2ecf20Sopenharmony_ci .change = fifo_hd_init, 2228c2ecf20Sopenharmony_ci .dump = fifo_hd_dump, 2238c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2248c2ecf20Sopenharmony_ci}; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci/* Pass size change message down to embedded FIFO */ 2278c2ecf20Sopenharmony_ciint fifo_set_limit(struct Qdisc *q, unsigned int limit) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci struct nlattr *nla; 2308c2ecf20Sopenharmony_ci int ret = -ENOMEM; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* Hack to avoid sending change message to non-FIFO */ 2338c2ecf20Sopenharmony_ci if (strncmp(q->ops->id + 1, "fifo", 4) != 0) 2348c2ecf20Sopenharmony_ci return 0; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (!q->ops->change) 2378c2ecf20Sopenharmony_ci return 0; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci nla = kmalloc(nla_attr_size(sizeof(struct tc_fifo_qopt)), GFP_KERNEL); 2408c2ecf20Sopenharmony_ci if (nla) { 2418c2ecf20Sopenharmony_ci nla->nla_type = RTM_NEWQDISC; 2428c2ecf20Sopenharmony_ci nla->nla_len = nla_attr_size(sizeof(struct tc_fifo_qopt)); 2438c2ecf20Sopenharmony_ci ((struct tc_fifo_qopt *)nla_data(nla))->limit = limit; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci ret = q->ops->change(q, nla, NULL); 2468c2ecf20Sopenharmony_ci kfree(nla); 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci return ret; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fifo_set_limit); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistruct Qdisc *fifo_create_dflt(struct Qdisc *sch, struct Qdisc_ops *ops, 2538c2ecf20Sopenharmony_ci unsigned int limit, 2548c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci struct Qdisc *q; 2578c2ecf20Sopenharmony_ci int err = -ENOMEM; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci q = qdisc_create_dflt(sch->dev_queue, ops, TC_H_MAKE(sch->handle, 1), 2608c2ecf20Sopenharmony_ci extack); 2618c2ecf20Sopenharmony_ci if (q) { 2628c2ecf20Sopenharmony_ci err = fifo_set_limit(q, limit); 2638c2ecf20Sopenharmony_ci if (err < 0) { 2648c2ecf20Sopenharmony_ci qdisc_put(q); 2658c2ecf20Sopenharmony_ci q = NULL; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci return q ? : ERR_PTR(err); 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fifo_create_dflt); 272