1585d763aSVinicius Costa Gomes /* 2585d763aSVinicius Costa Gomes * net/sched/sch_cbs.c Credit Based Shaper 3585d763aSVinicius Costa Gomes * 4585d763aSVinicius Costa Gomes * This program is free software; you can redistribute it and/or 5585d763aSVinicius Costa Gomes * modify it under the terms of the GNU General Public License 6585d763aSVinicius Costa Gomes * as published by the Free Software Foundation; either version 7585d763aSVinicius Costa Gomes * 2 of the License, or (at your option) any later version. 8585d763aSVinicius Costa Gomes * 9585d763aSVinicius Costa Gomes * Authors: Vinicius Costa Gomes <vinicius.gomes@intel.com> 10585d763aSVinicius Costa Gomes * 11585d763aSVinicius Costa Gomes */ 12585d763aSVinicius Costa Gomes 13585d763aSVinicius Costa Gomes /* Credit Based Shaper (CBS) 14585d763aSVinicius Costa Gomes * ========================= 15585d763aSVinicius Costa Gomes * 16585d763aSVinicius Costa Gomes * This is a simple rate-limiting shaper aimed at TSN applications on 17585d763aSVinicius Costa Gomes * systems with known traffic workloads. 18585d763aSVinicius Costa Gomes * 19585d763aSVinicius Costa Gomes * Its algorithm is defined by the IEEE 802.1Q-2014 Specification, 20585d763aSVinicius Costa Gomes * Section 8.6.8.2, and explained in more detail in the Annex L of the 21585d763aSVinicius Costa Gomes * same specification. 22585d763aSVinicius Costa Gomes * 23585d763aSVinicius Costa Gomes * There are four tunables to be considered: 24585d763aSVinicius Costa Gomes * 25585d763aSVinicius Costa Gomes * 'idleslope': Idleslope is the rate of credits that is 26585d763aSVinicius Costa Gomes * accumulated (in kilobits per second) when there is at least 27585d763aSVinicius Costa Gomes * one packet waiting for transmission. Packets are transmitted 28585d763aSVinicius Costa Gomes * when the current value of credits is equal or greater than 29585d763aSVinicius Costa Gomes * zero. When there is no packet to be transmitted the amount of 30585d763aSVinicius Costa Gomes * credits is set to zero. This is the main tunable of the CBS 31585d763aSVinicius Costa Gomes * algorithm. 32585d763aSVinicius Costa Gomes * 33585d763aSVinicius Costa Gomes * 'sendslope': 34585d763aSVinicius Costa Gomes * Sendslope is the rate of credits that is depleted (it should be a 35585d763aSVinicius Costa Gomes * negative number of kilobits per second) when a transmission is 36585d763aSVinicius Costa Gomes * ocurring. It can be calculated as follows, (IEEE 802.1Q-2014 Section 37585d763aSVinicius Costa Gomes * 8.6.8.2 item g): 38585d763aSVinicius Costa Gomes * 39585d763aSVinicius Costa Gomes * sendslope = idleslope - port_transmit_rate 40585d763aSVinicius Costa Gomes * 41585d763aSVinicius Costa Gomes * 'hicredit': Hicredit defines the maximum amount of credits (in 42585d763aSVinicius Costa Gomes * bytes) that can be accumulated. Hicredit depends on the 43585d763aSVinicius Costa Gomes * characteristics of interfering traffic, 44585d763aSVinicius Costa Gomes * 'max_interference_size' is the maximum size of any burst of 45585d763aSVinicius Costa Gomes * traffic that can delay the transmission of a frame that is 46585d763aSVinicius Costa Gomes * available for transmission for this traffic class, (IEEE 47585d763aSVinicius Costa Gomes * 802.1Q-2014 Annex L, Equation L-3): 48585d763aSVinicius Costa Gomes * 49585d763aSVinicius Costa Gomes * hicredit = max_interference_size * (idleslope / port_transmit_rate) 50585d763aSVinicius Costa Gomes * 51585d763aSVinicius Costa Gomes * 'locredit': Locredit is the minimum amount of credits that can 52585d763aSVinicius Costa Gomes * be reached. It is a function of the traffic flowing through 53585d763aSVinicius Costa Gomes * this qdisc (IEEE 802.1Q-2014 Annex L, Equation L-2): 54585d763aSVinicius Costa Gomes * 55585d763aSVinicius Costa Gomes * locredit = max_frame_size * (sendslope / port_transmit_rate) 56585d763aSVinicius Costa Gomes */ 57585d763aSVinicius Costa Gomes 58585d763aSVinicius Costa Gomes #include <linux/module.h> 59585d763aSVinicius Costa Gomes #include <linux/types.h> 60585d763aSVinicius Costa Gomes #include <linux/kernel.h> 61585d763aSVinicius Costa Gomes #include <linux/string.h> 62585d763aSVinicius Costa Gomes #include <linux/errno.h> 63585d763aSVinicius Costa Gomes #include <linux/skbuff.h> 64585d763aSVinicius Costa Gomes #include <net/netlink.h> 65585d763aSVinicius Costa Gomes #include <net/sch_generic.h> 66585d763aSVinicius Costa Gomes #include <net/pkt_sched.h> 67585d763aSVinicius Costa Gomes 68585d763aSVinicius Costa Gomes #define BYTES_PER_KBIT (1000LL / 8) 69585d763aSVinicius Costa Gomes 70585d763aSVinicius Costa Gomes struct cbs_sched_data { 713d0bd028SVinicius Costa Gomes bool offload; 723d0bd028SVinicius Costa Gomes int queue; 73585d763aSVinicius Costa Gomes s64 port_rate; /* in bytes/s */ 74585d763aSVinicius Costa Gomes s64 last; /* timestamp in ns */ 75585d763aSVinicius Costa Gomes s64 credits; /* in bytes */ 76585d763aSVinicius Costa Gomes s32 locredit; /* in bytes */ 77585d763aSVinicius Costa Gomes s32 hicredit; /* in bytes */ 78585d763aSVinicius Costa Gomes s64 sendslope; /* in bytes/s */ 79585d763aSVinicius Costa Gomes s64 idleslope; /* in bytes/s */ 80585d763aSVinicius Costa Gomes struct qdisc_watchdog watchdog; 81585d763aSVinicius Costa Gomes int (*enqueue)(struct sk_buff *skb, struct Qdisc *sch); 82585d763aSVinicius Costa Gomes struct sk_buff *(*dequeue)(struct Qdisc *sch); 83585d763aSVinicius Costa Gomes }; 84585d763aSVinicius Costa Gomes 853d0bd028SVinicius Costa Gomes static int cbs_enqueue_offload(struct sk_buff *skb, struct Qdisc *sch) 863d0bd028SVinicius Costa Gomes { 873d0bd028SVinicius Costa Gomes return qdisc_enqueue_tail(skb, sch); 883d0bd028SVinicius Costa Gomes } 893d0bd028SVinicius Costa Gomes 90585d763aSVinicius Costa Gomes static int cbs_enqueue_soft(struct sk_buff *skb, struct Qdisc *sch) 91585d763aSVinicius Costa Gomes { 92585d763aSVinicius Costa Gomes struct cbs_sched_data *q = qdisc_priv(sch); 93585d763aSVinicius Costa Gomes 94585d763aSVinicius Costa Gomes if (sch->q.qlen == 0 && q->credits > 0) { 95585d763aSVinicius Costa Gomes /* We need to stop accumulating credits when there's 96585d763aSVinicius Costa Gomes * no enqueued packets and q->credits is positive. 97585d763aSVinicius Costa Gomes */ 98585d763aSVinicius Costa Gomes q->credits = 0; 99585d763aSVinicius Costa Gomes q->last = ktime_get_ns(); 100585d763aSVinicius Costa Gomes } 101585d763aSVinicius Costa Gomes 102585d763aSVinicius Costa Gomes return qdisc_enqueue_tail(skb, sch); 103585d763aSVinicius Costa Gomes } 104585d763aSVinicius Costa Gomes 105585d763aSVinicius Costa Gomes static int cbs_enqueue(struct sk_buff *skb, struct Qdisc *sch, 106585d763aSVinicius Costa Gomes struct sk_buff **to_free) 107585d763aSVinicius Costa Gomes { 108585d763aSVinicius Costa Gomes struct cbs_sched_data *q = qdisc_priv(sch); 109585d763aSVinicius Costa Gomes 110585d763aSVinicius Costa Gomes return q->enqueue(skb, sch); 111585d763aSVinicius Costa Gomes } 112585d763aSVinicius Costa Gomes 113585d763aSVinicius Costa Gomes /* timediff is in ns, slope is in bytes/s */ 114585d763aSVinicius Costa Gomes static s64 timediff_to_credits(s64 timediff, s64 slope) 115585d763aSVinicius Costa Gomes { 116585d763aSVinicius Costa Gomes return div64_s64(timediff * slope, NSEC_PER_SEC); 117585d763aSVinicius Costa Gomes } 118585d763aSVinicius Costa Gomes 119585d763aSVinicius Costa Gomes static s64 delay_from_credits(s64 credits, s64 slope) 120585d763aSVinicius Costa Gomes { 121585d763aSVinicius Costa Gomes if (unlikely(slope == 0)) 122585d763aSVinicius Costa Gomes return S64_MAX; 123585d763aSVinicius Costa Gomes 124585d763aSVinicius Costa Gomes return div64_s64(-credits * NSEC_PER_SEC, slope); 125585d763aSVinicius Costa Gomes } 126585d763aSVinicius Costa Gomes 127585d763aSVinicius Costa Gomes static s64 credits_from_len(unsigned int len, s64 slope, s64 port_rate) 128585d763aSVinicius Costa Gomes { 129585d763aSVinicius Costa Gomes if (unlikely(port_rate == 0)) 130585d763aSVinicius Costa Gomes return S64_MAX; 131585d763aSVinicius Costa Gomes 132585d763aSVinicius Costa Gomes return div64_s64(len * slope, port_rate); 133585d763aSVinicius Costa Gomes } 134585d763aSVinicius Costa Gomes 135585d763aSVinicius Costa Gomes static struct sk_buff *cbs_dequeue_soft(struct Qdisc *sch) 136585d763aSVinicius Costa Gomes { 137585d763aSVinicius Costa Gomes struct cbs_sched_data *q = qdisc_priv(sch); 138585d763aSVinicius Costa Gomes s64 now = ktime_get_ns(); 139585d763aSVinicius Costa Gomes struct sk_buff *skb; 140585d763aSVinicius Costa Gomes s64 credits; 141585d763aSVinicius Costa Gomes int len; 142585d763aSVinicius Costa Gomes 143585d763aSVinicius Costa Gomes if (q->credits < 0) { 144585d763aSVinicius Costa Gomes credits = timediff_to_credits(now - q->last, q->idleslope); 145585d763aSVinicius Costa Gomes 146585d763aSVinicius Costa Gomes credits = q->credits + credits; 147585d763aSVinicius Costa Gomes q->credits = min_t(s64, credits, q->hicredit); 148585d763aSVinicius Costa Gomes 149585d763aSVinicius Costa Gomes if (q->credits < 0) { 150585d763aSVinicius Costa Gomes s64 delay; 151585d763aSVinicius Costa Gomes 152585d763aSVinicius Costa Gomes delay = delay_from_credits(q->credits, q->idleslope); 153585d763aSVinicius Costa Gomes qdisc_watchdog_schedule_ns(&q->watchdog, now + delay); 154585d763aSVinicius Costa Gomes 155585d763aSVinicius Costa Gomes q->last = now; 156585d763aSVinicius Costa Gomes 157585d763aSVinicius Costa Gomes return NULL; 158585d763aSVinicius Costa Gomes } 159585d763aSVinicius Costa Gomes } 160585d763aSVinicius Costa Gomes 161585d763aSVinicius Costa Gomes skb = qdisc_dequeue_head(sch); 162585d763aSVinicius Costa Gomes if (!skb) 163585d763aSVinicius Costa Gomes return NULL; 164585d763aSVinicius Costa Gomes 165585d763aSVinicius Costa Gomes len = qdisc_pkt_len(skb); 166585d763aSVinicius Costa Gomes 167585d763aSVinicius Costa Gomes /* As sendslope is a negative number, this will decrease the 168585d763aSVinicius Costa Gomes * amount of q->credits. 169585d763aSVinicius Costa Gomes */ 170585d763aSVinicius Costa Gomes credits = credits_from_len(len, q->sendslope, q->port_rate); 171585d763aSVinicius Costa Gomes credits += q->credits; 172585d763aSVinicius Costa Gomes 173585d763aSVinicius Costa Gomes q->credits = max_t(s64, credits, q->locredit); 174585d763aSVinicius Costa Gomes q->last = now; 175585d763aSVinicius Costa Gomes 176585d763aSVinicius Costa Gomes return skb; 177585d763aSVinicius Costa Gomes } 178585d763aSVinicius Costa Gomes 1793d0bd028SVinicius Costa Gomes static struct sk_buff *cbs_dequeue_offload(struct Qdisc *sch) 1803d0bd028SVinicius Costa Gomes { 1813d0bd028SVinicius Costa Gomes return qdisc_dequeue_head(sch); 1823d0bd028SVinicius Costa Gomes } 1833d0bd028SVinicius Costa Gomes 184585d763aSVinicius Costa Gomes static struct sk_buff *cbs_dequeue(struct Qdisc *sch) 185585d763aSVinicius Costa Gomes { 186585d763aSVinicius Costa Gomes struct cbs_sched_data *q = qdisc_priv(sch); 187585d763aSVinicius Costa Gomes 188585d763aSVinicius Costa Gomes return q->dequeue(sch); 189585d763aSVinicius Costa Gomes } 190585d763aSVinicius Costa Gomes 191585d763aSVinicius Costa Gomes static const struct nla_policy cbs_policy[TCA_CBS_MAX + 1] = { 192585d763aSVinicius Costa Gomes [TCA_CBS_PARMS] = { .len = sizeof(struct tc_cbs_qopt) }, 193585d763aSVinicius Costa Gomes }; 194585d763aSVinicius Costa Gomes 1953d0bd028SVinicius Costa Gomes static void cbs_disable_offload(struct net_device *dev, 1963d0bd028SVinicius Costa Gomes struct cbs_sched_data *q) 1973d0bd028SVinicius Costa Gomes { 1983d0bd028SVinicius Costa Gomes struct tc_cbs_qopt_offload cbs = { }; 1993d0bd028SVinicius Costa Gomes const struct net_device_ops *ops; 2003d0bd028SVinicius Costa Gomes int err; 2013d0bd028SVinicius Costa Gomes 2023d0bd028SVinicius Costa Gomes if (!q->offload) 2033d0bd028SVinicius Costa Gomes return; 2043d0bd028SVinicius Costa Gomes 2053d0bd028SVinicius Costa Gomes q->enqueue = cbs_enqueue_soft; 2063d0bd028SVinicius Costa Gomes q->dequeue = cbs_dequeue_soft; 2073d0bd028SVinicius Costa Gomes 2083d0bd028SVinicius Costa Gomes ops = dev->netdev_ops; 2093d0bd028SVinicius Costa Gomes if (!ops->ndo_setup_tc) 2103d0bd028SVinicius Costa Gomes return; 2113d0bd028SVinicius Costa Gomes 2123d0bd028SVinicius Costa Gomes cbs.queue = q->queue; 2133d0bd028SVinicius Costa Gomes cbs.enable = 0; 2143d0bd028SVinicius Costa Gomes 2158521db4cSNogah Frankel err = ops->ndo_setup_tc(dev, TC_SETUP_QDISC_CBS, &cbs); 2163d0bd028SVinicius Costa Gomes if (err < 0) 2173d0bd028SVinicius Costa Gomes pr_warn("Couldn't disable CBS offload for queue %d\n", 2183d0bd028SVinicius Costa Gomes cbs.queue); 2193d0bd028SVinicius Costa Gomes } 2203d0bd028SVinicius Costa Gomes 2213d0bd028SVinicius Costa Gomes static int cbs_enable_offload(struct net_device *dev, struct cbs_sched_data *q, 2223d0bd028SVinicius Costa Gomes const struct tc_cbs_qopt *opt) 2233d0bd028SVinicius Costa Gomes { 2243d0bd028SVinicius Costa Gomes const struct net_device_ops *ops = dev->netdev_ops; 2253d0bd028SVinicius Costa Gomes struct tc_cbs_qopt_offload cbs = { }; 2263d0bd028SVinicius Costa Gomes int err; 2273d0bd028SVinicius Costa Gomes 2283d0bd028SVinicius Costa Gomes if (!ops->ndo_setup_tc) 2293d0bd028SVinicius Costa Gomes return -EOPNOTSUPP; 2303d0bd028SVinicius Costa Gomes 2313d0bd028SVinicius Costa Gomes cbs.queue = q->queue; 2323d0bd028SVinicius Costa Gomes 2333d0bd028SVinicius Costa Gomes cbs.enable = 1; 2343d0bd028SVinicius Costa Gomes cbs.hicredit = opt->hicredit; 2353d0bd028SVinicius Costa Gomes cbs.locredit = opt->locredit; 2363d0bd028SVinicius Costa Gomes cbs.idleslope = opt->idleslope; 2373d0bd028SVinicius Costa Gomes cbs.sendslope = opt->sendslope; 2383d0bd028SVinicius Costa Gomes 2398521db4cSNogah Frankel err = ops->ndo_setup_tc(dev, TC_SETUP_QDISC_CBS, &cbs); 2403d0bd028SVinicius Costa Gomes if (err < 0) 2413d0bd028SVinicius Costa Gomes return err; 2423d0bd028SVinicius Costa Gomes 2433d0bd028SVinicius Costa Gomes q->enqueue = cbs_enqueue_offload; 2443d0bd028SVinicius Costa Gomes q->dequeue = cbs_dequeue_offload; 2453d0bd028SVinicius Costa Gomes 2463d0bd028SVinicius Costa Gomes return 0; 2473d0bd028SVinicius Costa Gomes } 2483d0bd028SVinicius Costa Gomes 249585d763aSVinicius Costa Gomes static int cbs_change(struct Qdisc *sch, struct nlattr *opt) 250585d763aSVinicius Costa Gomes { 251585d763aSVinicius Costa Gomes struct cbs_sched_data *q = qdisc_priv(sch); 252585d763aSVinicius Costa Gomes struct net_device *dev = qdisc_dev(sch); 253585d763aSVinicius Costa Gomes struct nlattr *tb[TCA_CBS_MAX + 1]; 254585d763aSVinicius Costa Gomes struct tc_cbs_qopt *qopt; 255585d763aSVinicius Costa Gomes int err; 256585d763aSVinicius Costa Gomes 257585d763aSVinicius Costa Gomes err = nla_parse_nested(tb, TCA_CBS_MAX, opt, cbs_policy, NULL); 258585d763aSVinicius Costa Gomes if (err < 0) 259585d763aSVinicius Costa Gomes return err; 260585d763aSVinicius Costa Gomes 261585d763aSVinicius Costa Gomes if (!tb[TCA_CBS_PARMS]) 262585d763aSVinicius Costa Gomes return -EINVAL; 263585d763aSVinicius Costa Gomes 264585d763aSVinicius Costa Gomes qopt = nla_data(tb[TCA_CBS_PARMS]); 265585d763aSVinicius Costa Gomes 2663d0bd028SVinicius Costa Gomes if (!qopt->offload) { 2673d0bd028SVinicius Costa Gomes struct ethtool_link_ksettings ecmd; 2683d0bd028SVinicius Costa Gomes s64 link_speed; 269585d763aSVinicius Costa Gomes 270585d763aSVinicius Costa Gomes if (!__ethtool_get_link_ksettings(dev, &ecmd)) 271585d763aSVinicius Costa Gomes link_speed = ecmd.base.speed; 272585d763aSVinicius Costa Gomes else 273585d763aSVinicius Costa Gomes link_speed = SPEED_1000; 274585d763aSVinicius Costa Gomes 275585d763aSVinicius Costa Gomes q->port_rate = link_speed * 1000 * BYTES_PER_KBIT; 276585d763aSVinicius Costa Gomes 2773d0bd028SVinicius Costa Gomes cbs_disable_offload(dev, q); 2783d0bd028SVinicius Costa Gomes } else { 2793d0bd028SVinicius Costa Gomes err = cbs_enable_offload(dev, q, qopt); 2803d0bd028SVinicius Costa Gomes if (err < 0) 2813d0bd028SVinicius Costa Gomes return err; 2823d0bd028SVinicius Costa Gomes } 283585d763aSVinicius Costa Gomes 2843d0bd028SVinicius Costa Gomes /* Everything went OK, save the parameters used. */ 285585d763aSVinicius Costa Gomes q->hicredit = qopt->hicredit; 286585d763aSVinicius Costa Gomes q->locredit = qopt->locredit; 287585d763aSVinicius Costa Gomes q->idleslope = qopt->idleslope * BYTES_PER_KBIT; 288585d763aSVinicius Costa Gomes q->sendslope = qopt->sendslope * BYTES_PER_KBIT; 2893d0bd028SVinicius Costa Gomes q->offload = qopt->offload; 290585d763aSVinicius Costa Gomes 291585d763aSVinicius Costa Gomes return 0; 292585d763aSVinicius Costa Gomes } 293585d763aSVinicius Costa Gomes 294*e63d7dfdSAlexander Aring static int cbs_init(struct Qdisc *sch, struct nlattr *opt, 295*e63d7dfdSAlexander Aring struct netlink_ext_ack *extack) 296585d763aSVinicius Costa Gomes { 297585d763aSVinicius Costa Gomes struct cbs_sched_data *q = qdisc_priv(sch); 2983d0bd028SVinicius Costa Gomes struct net_device *dev = qdisc_dev(sch); 299585d763aSVinicius Costa Gomes 300585d763aSVinicius Costa Gomes if (!opt) 301585d763aSVinicius Costa Gomes return -EINVAL; 302585d763aSVinicius Costa Gomes 3033d0bd028SVinicius Costa Gomes q->queue = sch->dev_queue - netdev_get_tx_queue(dev, 0); 3043d0bd028SVinicius Costa Gomes 3053d0bd028SVinicius Costa Gomes q->enqueue = cbs_enqueue_soft; 3063d0bd028SVinicius Costa Gomes q->dequeue = cbs_dequeue_soft; 3073d0bd028SVinicius Costa Gomes 308585d763aSVinicius Costa Gomes qdisc_watchdog_init(&q->watchdog, sch); 309585d763aSVinicius Costa Gomes 310585d763aSVinicius Costa Gomes return cbs_change(sch, opt); 311585d763aSVinicius Costa Gomes } 312585d763aSVinicius Costa Gomes 313585d763aSVinicius Costa Gomes static void cbs_destroy(struct Qdisc *sch) 314585d763aSVinicius Costa Gomes { 315585d763aSVinicius Costa Gomes struct cbs_sched_data *q = qdisc_priv(sch); 3163d0bd028SVinicius Costa Gomes struct net_device *dev = qdisc_dev(sch); 317585d763aSVinicius Costa Gomes 318585d763aSVinicius Costa Gomes qdisc_watchdog_cancel(&q->watchdog); 3193d0bd028SVinicius Costa Gomes 3203d0bd028SVinicius Costa Gomes cbs_disable_offload(dev, q); 321585d763aSVinicius Costa Gomes } 322585d763aSVinicius Costa Gomes 323585d763aSVinicius Costa Gomes static int cbs_dump(struct Qdisc *sch, struct sk_buff *skb) 324585d763aSVinicius Costa Gomes { 325585d763aSVinicius Costa Gomes struct cbs_sched_data *q = qdisc_priv(sch); 326585d763aSVinicius Costa Gomes struct tc_cbs_qopt opt = { }; 327585d763aSVinicius Costa Gomes struct nlattr *nest; 328585d763aSVinicius Costa Gomes 329585d763aSVinicius Costa Gomes nest = nla_nest_start(skb, TCA_OPTIONS); 330585d763aSVinicius Costa Gomes if (!nest) 331585d763aSVinicius Costa Gomes goto nla_put_failure; 332585d763aSVinicius Costa Gomes 333585d763aSVinicius Costa Gomes opt.hicredit = q->hicredit; 334585d763aSVinicius Costa Gomes opt.locredit = q->locredit; 335585d763aSVinicius Costa Gomes opt.sendslope = div64_s64(q->sendslope, BYTES_PER_KBIT); 336585d763aSVinicius Costa Gomes opt.idleslope = div64_s64(q->idleslope, BYTES_PER_KBIT); 3373d0bd028SVinicius Costa Gomes opt.offload = q->offload; 338585d763aSVinicius Costa Gomes 339585d763aSVinicius Costa Gomes if (nla_put(skb, TCA_CBS_PARMS, sizeof(opt), &opt)) 340585d763aSVinicius Costa Gomes goto nla_put_failure; 341585d763aSVinicius Costa Gomes 342585d763aSVinicius Costa Gomes return nla_nest_end(skb, nest); 343585d763aSVinicius Costa Gomes 344585d763aSVinicius Costa Gomes nla_put_failure: 345585d763aSVinicius Costa Gomes nla_nest_cancel(skb, nest); 346585d763aSVinicius Costa Gomes return -1; 347585d763aSVinicius Costa Gomes } 348585d763aSVinicius Costa Gomes 349585d763aSVinicius Costa Gomes static struct Qdisc_ops cbs_qdisc_ops __read_mostly = { 350585d763aSVinicius Costa Gomes .id = "cbs", 351585d763aSVinicius Costa Gomes .priv_size = sizeof(struct cbs_sched_data), 352585d763aSVinicius Costa Gomes .enqueue = cbs_enqueue, 353585d763aSVinicius Costa Gomes .dequeue = cbs_dequeue, 354585d763aSVinicius Costa Gomes .peek = qdisc_peek_dequeued, 355585d763aSVinicius Costa Gomes .init = cbs_init, 356585d763aSVinicius Costa Gomes .reset = qdisc_reset_queue, 357585d763aSVinicius Costa Gomes .destroy = cbs_destroy, 358585d763aSVinicius Costa Gomes .change = cbs_change, 359585d763aSVinicius Costa Gomes .dump = cbs_dump, 360585d763aSVinicius Costa Gomes .owner = THIS_MODULE, 361585d763aSVinicius Costa Gomes }; 362585d763aSVinicius Costa Gomes 363585d763aSVinicius Costa Gomes static int __init cbs_module_init(void) 364585d763aSVinicius Costa Gomes { 365585d763aSVinicius Costa Gomes return register_qdisc(&cbs_qdisc_ops); 366585d763aSVinicius Costa Gomes } 367585d763aSVinicius Costa Gomes 368585d763aSVinicius Costa Gomes static void __exit cbs_module_exit(void) 369585d763aSVinicius Costa Gomes { 370585d763aSVinicius Costa Gomes unregister_qdisc(&cbs_qdisc_ops); 371585d763aSVinicius Costa Gomes } 372585d763aSVinicius Costa Gomes module_init(cbs_module_init) 373585d763aSVinicius Costa Gomes module_exit(cbs_module_exit) 374585d763aSVinicius Costa Gomes MODULE_LICENSE("GPL"); 375