18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * net/sched/sch_cbs.c Credit Based Shaper 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: Vinicius Costa Gomes <vinicius.gomes@intel.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci/* Credit Based Shaper (CBS) 98c2ecf20Sopenharmony_ci * ========================= 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * This is a simple rate-limiting shaper aimed at TSN applications on 128c2ecf20Sopenharmony_ci * systems with known traffic workloads. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Its algorithm is defined by the IEEE 802.1Q-2014 Specification, 158c2ecf20Sopenharmony_ci * Section 8.6.8.2, and explained in more detail in the Annex L of the 168c2ecf20Sopenharmony_ci * same specification. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * There are four tunables to be considered: 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * 'idleslope': Idleslope is the rate of credits that is 218c2ecf20Sopenharmony_ci * accumulated (in kilobits per second) when there is at least 228c2ecf20Sopenharmony_ci * one packet waiting for transmission. Packets are transmitted 238c2ecf20Sopenharmony_ci * when the current value of credits is equal or greater than 248c2ecf20Sopenharmony_ci * zero. When there is no packet to be transmitted the amount of 258c2ecf20Sopenharmony_ci * credits is set to zero. This is the main tunable of the CBS 268c2ecf20Sopenharmony_ci * algorithm. 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * 'sendslope': 298c2ecf20Sopenharmony_ci * Sendslope is the rate of credits that is depleted (it should be a 308c2ecf20Sopenharmony_ci * negative number of kilobits per second) when a transmission is 318c2ecf20Sopenharmony_ci * ocurring. It can be calculated as follows, (IEEE 802.1Q-2014 Section 328c2ecf20Sopenharmony_ci * 8.6.8.2 item g): 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci * sendslope = idleslope - port_transmit_rate 358c2ecf20Sopenharmony_ci * 368c2ecf20Sopenharmony_ci * 'hicredit': Hicredit defines the maximum amount of credits (in 378c2ecf20Sopenharmony_ci * bytes) that can be accumulated. Hicredit depends on the 388c2ecf20Sopenharmony_ci * characteristics of interfering traffic, 398c2ecf20Sopenharmony_ci * 'max_interference_size' is the maximum size of any burst of 408c2ecf20Sopenharmony_ci * traffic that can delay the transmission of a frame that is 418c2ecf20Sopenharmony_ci * available for transmission for this traffic class, (IEEE 428c2ecf20Sopenharmony_ci * 802.1Q-2014 Annex L, Equation L-3): 438c2ecf20Sopenharmony_ci * 448c2ecf20Sopenharmony_ci * hicredit = max_interference_size * (idleslope / port_transmit_rate) 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci * 'locredit': Locredit is the minimum amount of credits that can 478c2ecf20Sopenharmony_ci * be reached. It is a function of the traffic flowing through 488c2ecf20Sopenharmony_ci * this qdisc (IEEE 802.1Q-2014 Annex L, Equation L-2): 498c2ecf20Sopenharmony_ci * 508c2ecf20Sopenharmony_ci * locredit = max_frame_size * (sendslope / port_transmit_rate) 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#include <linux/module.h> 548c2ecf20Sopenharmony_ci#include <linux/types.h> 558c2ecf20Sopenharmony_ci#include <linux/kernel.h> 568c2ecf20Sopenharmony_ci#include <linux/string.h> 578c2ecf20Sopenharmony_ci#include <linux/errno.h> 588c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 598c2ecf20Sopenharmony_ci#include <net/netevent.h> 608c2ecf20Sopenharmony_ci#include <net/netlink.h> 618c2ecf20Sopenharmony_ci#include <net/sch_generic.h> 628c2ecf20Sopenharmony_ci#include <net/pkt_sched.h> 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic LIST_HEAD(cbs_list); 658c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(cbs_list_lock); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define BYTES_PER_KBIT (1000LL / 8) 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistruct cbs_sched_data { 708c2ecf20Sopenharmony_ci bool offload; 718c2ecf20Sopenharmony_ci int queue; 728c2ecf20Sopenharmony_ci atomic64_t port_rate; /* in bytes/s */ 738c2ecf20Sopenharmony_ci s64 last; /* timestamp in ns */ 748c2ecf20Sopenharmony_ci s64 credits; /* in bytes */ 758c2ecf20Sopenharmony_ci s32 locredit; /* in bytes */ 768c2ecf20Sopenharmony_ci s32 hicredit; /* in bytes */ 778c2ecf20Sopenharmony_ci s64 sendslope; /* in bytes/s */ 788c2ecf20Sopenharmony_ci s64 idleslope; /* in bytes/s */ 798c2ecf20Sopenharmony_ci struct qdisc_watchdog watchdog; 808c2ecf20Sopenharmony_ci int (*enqueue)(struct sk_buff *skb, struct Qdisc *sch, 818c2ecf20Sopenharmony_ci struct sk_buff **to_free); 828c2ecf20Sopenharmony_ci struct sk_buff *(*dequeue)(struct Qdisc *sch); 838c2ecf20Sopenharmony_ci struct Qdisc *qdisc; 848c2ecf20Sopenharmony_ci struct list_head cbs_list; 858c2ecf20Sopenharmony_ci}; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic int cbs_child_enqueue(struct sk_buff *skb, struct Qdisc *sch, 888c2ecf20Sopenharmony_ci struct Qdisc *child, 898c2ecf20Sopenharmony_ci struct sk_buff **to_free) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci unsigned int len = qdisc_pkt_len(skb); 928c2ecf20Sopenharmony_ci int err; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci err = child->ops->enqueue(skb, child, to_free); 958c2ecf20Sopenharmony_ci if (err != NET_XMIT_SUCCESS) 968c2ecf20Sopenharmony_ci return err; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci sch->qstats.backlog += len; 998c2ecf20Sopenharmony_ci sch->q.qlen++; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci return NET_XMIT_SUCCESS; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic int cbs_enqueue_offload(struct sk_buff *skb, struct Qdisc *sch, 1058c2ecf20Sopenharmony_ci struct sk_buff **to_free) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct cbs_sched_data *q = qdisc_priv(sch); 1088c2ecf20Sopenharmony_ci struct Qdisc *qdisc = q->qdisc; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci return cbs_child_enqueue(skb, sch, qdisc, to_free); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic int cbs_enqueue_soft(struct sk_buff *skb, struct Qdisc *sch, 1148c2ecf20Sopenharmony_ci struct sk_buff **to_free) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct cbs_sched_data *q = qdisc_priv(sch); 1178c2ecf20Sopenharmony_ci struct Qdisc *qdisc = q->qdisc; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (sch->q.qlen == 0 && q->credits > 0) { 1208c2ecf20Sopenharmony_ci /* We need to stop accumulating credits when there's 1218c2ecf20Sopenharmony_ci * no enqueued packets and q->credits is positive. 1228c2ecf20Sopenharmony_ci */ 1238c2ecf20Sopenharmony_ci q->credits = 0; 1248c2ecf20Sopenharmony_ci q->last = ktime_get_ns(); 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return cbs_child_enqueue(skb, sch, qdisc, to_free); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic int cbs_enqueue(struct sk_buff *skb, struct Qdisc *sch, 1318c2ecf20Sopenharmony_ci struct sk_buff **to_free) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct cbs_sched_data *q = qdisc_priv(sch); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci return q->enqueue(skb, sch, to_free); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci/* timediff is in ns, slope is in bytes/s */ 1398c2ecf20Sopenharmony_cistatic s64 timediff_to_credits(s64 timediff, s64 slope) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci return div64_s64(timediff * slope, NSEC_PER_SEC); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic s64 delay_from_credits(s64 credits, s64 slope) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci if (unlikely(slope == 0)) 1478c2ecf20Sopenharmony_ci return S64_MAX; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return div64_s64(-credits * NSEC_PER_SEC, slope); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic s64 credits_from_len(unsigned int len, s64 slope, s64 port_rate) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci if (unlikely(port_rate == 0)) 1558c2ecf20Sopenharmony_ci return S64_MAX; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return div64_s64(len * slope, port_rate); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic struct sk_buff *cbs_child_dequeue(struct Qdisc *sch, struct Qdisc *child) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct sk_buff *skb; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci skb = child->ops->dequeue(child); 1658c2ecf20Sopenharmony_ci if (!skb) 1668c2ecf20Sopenharmony_ci return NULL; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci qdisc_qstats_backlog_dec(sch, skb); 1698c2ecf20Sopenharmony_ci qdisc_bstats_update(sch, skb); 1708c2ecf20Sopenharmony_ci sch->q.qlen--; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci return skb; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic struct sk_buff *cbs_dequeue_soft(struct Qdisc *sch) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci struct cbs_sched_data *q = qdisc_priv(sch); 1788c2ecf20Sopenharmony_ci struct Qdisc *qdisc = q->qdisc; 1798c2ecf20Sopenharmony_ci s64 now = ktime_get_ns(); 1808c2ecf20Sopenharmony_ci struct sk_buff *skb; 1818c2ecf20Sopenharmony_ci s64 credits; 1828c2ecf20Sopenharmony_ci int len; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* The previous packet is still being sent */ 1858c2ecf20Sopenharmony_ci if (now < q->last) { 1868c2ecf20Sopenharmony_ci qdisc_watchdog_schedule_ns(&q->watchdog, q->last); 1878c2ecf20Sopenharmony_ci return NULL; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci if (q->credits < 0) { 1908c2ecf20Sopenharmony_ci credits = timediff_to_credits(now - q->last, q->idleslope); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci credits = q->credits + credits; 1938c2ecf20Sopenharmony_ci q->credits = min_t(s64, credits, q->hicredit); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (q->credits < 0) { 1968c2ecf20Sopenharmony_ci s64 delay; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci delay = delay_from_credits(q->credits, q->idleslope); 1998c2ecf20Sopenharmony_ci qdisc_watchdog_schedule_ns(&q->watchdog, now + delay); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci q->last = now; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci return NULL; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci skb = cbs_child_dequeue(sch, qdisc); 2078c2ecf20Sopenharmony_ci if (!skb) 2088c2ecf20Sopenharmony_ci return NULL; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci len = qdisc_pkt_len(skb); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* As sendslope is a negative number, this will decrease the 2138c2ecf20Sopenharmony_ci * amount of q->credits. 2148c2ecf20Sopenharmony_ci */ 2158c2ecf20Sopenharmony_ci credits = credits_from_len(len, q->sendslope, 2168c2ecf20Sopenharmony_ci atomic64_read(&q->port_rate)); 2178c2ecf20Sopenharmony_ci credits += q->credits; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci q->credits = max_t(s64, credits, q->locredit); 2208c2ecf20Sopenharmony_ci /* Estimate of the transmission of the last byte of the packet in ns */ 2218c2ecf20Sopenharmony_ci if (unlikely(atomic64_read(&q->port_rate) == 0)) 2228c2ecf20Sopenharmony_ci q->last = now; 2238c2ecf20Sopenharmony_ci else 2248c2ecf20Sopenharmony_ci q->last = now + div64_s64(len * NSEC_PER_SEC, 2258c2ecf20Sopenharmony_ci atomic64_read(&q->port_rate)); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci return skb; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic struct sk_buff *cbs_dequeue_offload(struct Qdisc *sch) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci struct cbs_sched_data *q = qdisc_priv(sch); 2338c2ecf20Sopenharmony_ci struct Qdisc *qdisc = q->qdisc; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return cbs_child_dequeue(sch, qdisc); 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic struct sk_buff *cbs_dequeue(struct Qdisc *sch) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct cbs_sched_data *q = qdisc_priv(sch); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci return q->dequeue(sch); 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic const struct nla_policy cbs_policy[TCA_CBS_MAX + 1] = { 2468c2ecf20Sopenharmony_ci [TCA_CBS_PARMS] = { .len = sizeof(struct tc_cbs_qopt) }, 2478c2ecf20Sopenharmony_ci}; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic void cbs_disable_offload(struct net_device *dev, 2508c2ecf20Sopenharmony_ci struct cbs_sched_data *q) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci struct tc_cbs_qopt_offload cbs = { }; 2538c2ecf20Sopenharmony_ci const struct net_device_ops *ops; 2548c2ecf20Sopenharmony_ci int err; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (!q->offload) 2578c2ecf20Sopenharmony_ci return; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci q->enqueue = cbs_enqueue_soft; 2608c2ecf20Sopenharmony_ci q->dequeue = cbs_dequeue_soft; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci ops = dev->netdev_ops; 2638c2ecf20Sopenharmony_ci if (!ops->ndo_setup_tc) 2648c2ecf20Sopenharmony_ci return; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci cbs.queue = q->queue; 2678c2ecf20Sopenharmony_ci cbs.enable = 0; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci err = ops->ndo_setup_tc(dev, TC_SETUP_QDISC_CBS, &cbs); 2708c2ecf20Sopenharmony_ci if (err < 0) 2718c2ecf20Sopenharmony_ci pr_warn("Couldn't disable CBS offload for queue %d\n", 2728c2ecf20Sopenharmony_ci cbs.queue); 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic int cbs_enable_offload(struct net_device *dev, struct cbs_sched_data *q, 2768c2ecf20Sopenharmony_ci const struct tc_cbs_qopt *opt, 2778c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci const struct net_device_ops *ops = dev->netdev_ops; 2808c2ecf20Sopenharmony_ci struct tc_cbs_qopt_offload cbs = { }; 2818c2ecf20Sopenharmony_ci int err; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (!ops->ndo_setup_tc) { 2848c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Specified device does not support cbs offload"); 2858c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci cbs.queue = q->queue; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci cbs.enable = 1; 2918c2ecf20Sopenharmony_ci cbs.hicredit = opt->hicredit; 2928c2ecf20Sopenharmony_ci cbs.locredit = opt->locredit; 2938c2ecf20Sopenharmony_ci cbs.idleslope = opt->idleslope; 2948c2ecf20Sopenharmony_ci cbs.sendslope = opt->sendslope; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci err = ops->ndo_setup_tc(dev, TC_SETUP_QDISC_CBS, &cbs); 2978c2ecf20Sopenharmony_ci if (err < 0) { 2988c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Specified device failed to setup cbs hardware offload"); 2998c2ecf20Sopenharmony_ci return err; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci q->enqueue = cbs_enqueue_offload; 3038c2ecf20Sopenharmony_ci q->dequeue = cbs_dequeue_offload; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci return 0; 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic void cbs_set_port_rate(struct net_device *dev, struct cbs_sched_data *q) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci struct ethtool_link_ksettings ecmd; 3118c2ecf20Sopenharmony_ci int speed = SPEED_10; 3128c2ecf20Sopenharmony_ci int port_rate; 3138c2ecf20Sopenharmony_ci int err; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci err = __ethtool_get_link_ksettings(dev, &ecmd); 3168c2ecf20Sopenharmony_ci if (err < 0) 3178c2ecf20Sopenharmony_ci goto skip; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (ecmd.base.speed && ecmd.base.speed != SPEED_UNKNOWN) 3208c2ecf20Sopenharmony_ci speed = ecmd.base.speed; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ciskip: 3238c2ecf20Sopenharmony_ci port_rate = speed * 1000 * BYTES_PER_KBIT; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci atomic64_set(&q->port_rate, port_rate); 3268c2ecf20Sopenharmony_ci netdev_dbg(dev, "cbs: set %s's port_rate to: %lld, linkspeed: %d\n", 3278c2ecf20Sopenharmony_ci dev->name, (long long)atomic64_read(&q->port_rate), 3288c2ecf20Sopenharmony_ci ecmd.base.speed); 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic int cbs_dev_notifier(struct notifier_block *nb, unsigned long event, 3328c2ecf20Sopenharmony_ci void *ptr) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 3358c2ecf20Sopenharmony_ci struct cbs_sched_data *q; 3368c2ecf20Sopenharmony_ci struct net_device *qdev; 3378c2ecf20Sopenharmony_ci bool found = false; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci ASSERT_RTNL(); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (event != NETDEV_UP && event != NETDEV_CHANGE) 3428c2ecf20Sopenharmony_ci return NOTIFY_DONE; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci spin_lock(&cbs_list_lock); 3458c2ecf20Sopenharmony_ci list_for_each_entry(q, &cbs_list, cbs_list) { 3468c2ecf20Sopenharmony_ci qdev = qdisc_dev(q->qdisc); 3478c2ecf20Sopenharmony_ci if (qdev == dev) { 3488c2ecf20Sopenharmony_ci found = true; 3498c2ecf20Sopenharmony_ci break; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci spin_unlock(&cbs_list_lock); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci if (found) 3558c2ecf20Sopenharmony_ci cbs_set_port_rate(dev, q); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci return NOTIFY_DONE; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic int cbs_change(struct Qdisc *sch, struct nlattr *opt, 3618c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci struct cbs_sched_data *q = qdisc_priv(sch); 3648c2ecf20Sopenharmony_ci struct net_device *dev = qdisc_dev(sch); 3658c2ecf20Sopenharmony_ci struct nlattr *tb[TCA_CBS_MAX + 1]; 3668c2ecf20Sopenharmony_ci struct tc_cbs_qopt *qopt; 3678c2ecf20Sopenharmony_ci int err; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci err = nla_parse_nested_deprecated(tb, TCA_CBS_MAX, opt, cbs_policy, 3708c2ecf20Sopenharmony_ci extack); 3718c2ecf20Sopenharmony_ci if (err < 0) 3728c2ecf20Sopenharmony_ci return err; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (!tb[TCA_CBS_PARMS]) { 3758c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Missing CBS parameter which are mandatory"); 3768c2ecf20Sopenharmony_ci return -EINVAL; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci qopt = nla_data(tb[TCA_CBS_PARMS]); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (!qopt->offload) { 3828c2ecf20Sopenharmony_ci cbs_set_port_rate(dev, q); 3838c2ecf20Sopenharmony_ci cbs_disable_offload(dev, q); 3848c2ecf20Sopenharmony_ci } else { 3858c2ecf20Sopenharmony_ci err = cbs_enable_offload(dev, q, qopt, extack); 3868c2ecf20Sopenharmony_ci if (err < 0) 3878c2ecf20Sopenharmony_ci return err; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci /* Everything went OK, save the parameters used. */ 3918c2ecf20Sopenharmony_ci q->hicredit = qopt->hicredit; 3928c2ecf20Sopenharmony_ci q->locredit = qopt->locredit; 3938c2ecf20Sopenharmony_ci q->idleslope = qopt->idleslope * BYTES_PER_KBIT; 3948c2ecf20Sopenharmony_ci q->sendslope = qopt->sendslope * BYTES_PER_KBIT; 3958c2ecf20Sopenharmony_ci q->offload = qopt->offload; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci return 0; 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic int cbs_init(struct Qdisc *sch, struct nlattr *opt, 4018c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci struct cbs_sched_data *q = qdisc_priv(sch); 4048c2ecf20Sopenharmony_ci struct net_device *dev = qdisc_dev(sch); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (!opt) { 4078c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Missing CBS qdisc options which are mandatory"); 4088c2ecf20Sopenharmony_ci return -EINVAL; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci q->qdisc = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, 4128c2ecf20Sopenharmony_ci sch->handle, extack); 4138c2ecf20Sopenharmony_ci if (!q->qdisc) 4148c2ecf20Sopenharmony_ci return -ENOMEM; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci spin_lock(&cbs_list_lock); 4178c2ecf20Sopenharmony_ci list_add(&q->cbs_list, &cbs_list); 4188c2ecf20Sopenharmony_ci spin_unlock(&cbs_list_lock); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci qdisc_hash_add(q->qdisc, false); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci q->queue = sch->dev_queue - netdev_get_tx_queue(dev, 0); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci q->enqueue = cbs_enqueue_soft; 4258c2ecf20Sopenharmony_ci q->dequeue = cbs_dequeue_soft; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci qdisc_watchdog_init(&q->watchdog, sch); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci return cbs_change(sch, opt, extack); 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic void cbs_destroy(struct Qdisc *sch) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci struct cbs_sched_data *q = qdisc_priv(sch); 4358c2ecf20Sopenharmony_ci struct net_device *dev = qdisc_dev(sch); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci /* Nothing to do if we couldn't create the underlying qdisc */ 4388c2ecf20Sopenharmony_ci if (!q->qdisc) 4398c2ecf20Sopenharmony_ci return; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci qdisc_watchdog_cancel(&q->watchdog); 4428c2ecf20Sopenharmony_ci cbs_disable_offload(dev, q); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci spin_lock(&cbs_list_lock); 4458c2ecf20Sopenharmony_ci list_del(&q->cbs_list); 4468c2ecf20Sopenharmony_ci spin_unlock(&cbs_list_lock); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci qdisc_put(q->qdisc); 4498c2ecf20Sopenharmony_ci} 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_cistatic int cbs_dump(struct Qdisc *sch, struct sk_buff *skb) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci struct cbs_sched_data *q = qdisc_priv(sch); 4548c2ecf20Sopenharmony_ci struct tc_cbs_qopt opt = { }; 4558c2ecf20Sopenharmony_ci struct nlattr *nest; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci nest = nla_nest_start_noflag(skb, TCA_OPTIONS); 4588c2ecf20Sopenharmony_ci if (!nest) 4598c2ecf20Sopenharmony_ci goto nla_put_failure; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci opt.hicredit = q->hicredit; 4628c2ecf20Sopenharmony_ci opt.locredit = q->locredit; 4638c2ecf20Sopenharmony_ci opt.sendslope = div64_s64(q->sendslope, BYTES_PER_KBIT); 4648c2ecf20Sopenharmony_ci opt.idleslope = div64_s64(q->idleslope, BYTES_PER_KBIT); 4658c2ecf20Sopenharmony_ci opt.offload = q->offload; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci if (nla_put(skb, TCA_CBS_PARMS, sizeof(opt), &opt)) 4688c2ecf20Sopenharmony_ci goto nla_put_failure; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci return nla_nest_end(skb, nest); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cinla_put_failure: 4738c2ecf20Sopenharmony_ci nla_nest_cancel(skb, nest); 4748c2ecf20Sopenharmony_ci return -1; 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic int cbs_dump_class(struct Qdisc *sch, unsigned long cl, 4788c2ecf20Sopenharmony_ci struct sk_buff *skb, struct tcmsg *tcm) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci struct cbs_sched_data *q = qdisc_priv(sch); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if (cl != 1 || !q->qdisc) /* only one class */ 4838c2ecf20Sopenharmony_ci return -ENOENT; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci tcm->tcm_handle |= TC_H_MIN(1); 4868c2ecf20Sopenharmony_ci tcm->tcm_info = q->qdisc->handle; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci return 0; 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic int cbs_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, 4928c2ecf20Sopenharmony_ci struct Qdisc **old, struct netlink_ext_ack *extack) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci struct cbs_sched_data *q = qdisc_priv(sch); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci if (!new) { 4978c2ecf20Sopenharmony_ci new = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, 4988c2ecf20Sopenharmony_ci sch->handle, NULL); 4998c2ecf20Sopenharmony_ci if (!new) 5008c2ecf20Sopenharmony_ci new = &noop_qdisc; 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci *old = qdisc_replace(sch, new, &q->qdisc); 5048c2ecf20Sopenharmony_ci return 0; 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic struct Qdisc *cbs_leaf(struct Qdisc *sch, unsigned long arg) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct cbs_sched_data *q = qdisc_priv(sch); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci return q->qdisc; 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cistatic unsigned long cbs_find(struct Qdisc *sch, u32 classid) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci return 1; 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cistatic void cbs_walk(struct Qdisc *sch, struct qdisc_walker *walker) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci if (!walker->stop) { 5228c2ecf20Sopenharmony_ci if (walker->count >= walker->skip) { 5238c2ecf20Sopenharmony_ci if (walker->fn(sch, 1, walker) < 0) { 5248c2ecf20Sopenharmony_ci walker->stop = 1; 5258c2ecf20Sopenharmony_ci return; 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci walker->count++; 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cistatic const struct Qdisc_class_ops cbs_class_ops = { 5338c2ecf20Sopenharmony_ci .graft = cbs_graft, 5348c2ecf20Sopenharmony_ci .leaf = cbs_leaf, 5358c2ecf20Sopenharmony_ci .find = cbs_find, 5368c2ecf20Sopenharmony_ci .walk = cbs_walk, 5378c2ecf20Sopenharmony_ci .dump = cbs_dump_class, 5388c2ecf20Sopenharmony_ci}; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistatic struct Qdisc_ops cbs_qdisc_ops __read_mostly = { 5418c2ecf20Sopenharmony_ci .id = "cbs", 5428c2ecf20Sopenharmony_ci .cl_ops = &cbs_class_ops, 5438c2ecf20Sopenharmony_ci .priv_size = sizeof(struct cbs_sched_data), 5448c2ecf20Sopenharmony_ci .enqueue = cbs_enqueue, 5458c2ecf20Sopenharmony_ci .dequeue = cbs_dequeue, 5468c2ecf20Sopenharmony_ci .peek = qdisc_peek_dequeued, 5478c2ecf20Sopenharmony_ci .init = cbs_init, 5488c2ecf20Sopenharmony_ci .reset = qdisc_reset_queue, 5498c2ecf20Sopenharmony_ci .destroy = cbs_destroy, 5508c2ecf20Sopenharmony_ci .change = cbs_change, 5518c2ecf20Sopenharmony_ci .dump = cbs_dump, 5528c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 5538c2ecf20Sopenharmony_ci}; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistatic struct notifier_block cbs_device_notifier = { 5568c2ecf20Sopenharmony_ci .notifier_call = cbs_dev_notifier, 5578c2ecf20Sopenharmony_ci}; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_cistatic int __init cbs_module_init(void) 5608c2ecf20Sopenharmony_ci{ 5618c2ecf20Sopenharmony_ci int err; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci err = register_netdevice_notifier(&cbs_device_notifier); 5648c2ecf20Sopenharmony_ci if (err) 5658c2ecf20Sopenharmony_ci return err; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci err = register_qdisc(&cbs_qdisc_ops); 5688c2ecf20Sopenharmony_ci if (err) 5698c2ecf20Sopenharmony_ci unregister_netdevice_notifier(&cbs_device_notifier); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci return err; 5728c2ecf20Sopenharmony_ci} 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_cistatic void __exit cbs_module_exit(void) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci unregister_qdisc(&cbs_qdisc_ops); 5778c2ecf20Sopenharmony_ci unregister_netdevice_notifier(&cbs_device_notifier); 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_cimodule_init(cbs_module_init) 5808c2ecf20Sopenharmony_cimodule_exit(cbs_module_exit) 5818c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 582