125db26a9SVinicius Costa Gomes // SPDX-License-Identifier: GPL-2.0 225db26a9SVinicius Costa Gomes 325db26a9SVinicius Costa Gomes /* net/sched/sch_etf.c Earliest TxTime First queueing discipline. 425db26a9SVinicius Costa Gomes * 525db26a9SVinicius Costa Gomes * Authors: Jesus Sanchez-Palencia <jesus.sanchez-palencia@intel.com> 625db26a9SVinicius Costa Gomes * Vinicius Costa Gomes <vinicius.gomes@intel.com> 725db26a9SVinicius Costa Gomes */ 825db26a9SVinicius Costa Gomes 925db26a9SVinicius Costa Gomes #include <linux/module.h> 1025db26a9SVinicius Costa Gomes #include <linux/types.h> 1125db26a9SVinicius Costa Gomes #include <linux/kernel.h> 1225db26a9SVinicius Costa Gomes #include <linux/string.h> 1325db26a9SVinicius Costa Gomes #include <linux/errno.h> 1425db26a9SVinicius Costa Gomes #include <linux/rbtree.h> 1525db26a9SVinicius Costa Gomes #include <linux/skbuff.h> 1625db26a9SVinicius Costa Gomes #include <linux/posix-timers.h> 1725db26a9SVinicius Costa Gomes #include <net/netlink.h> 1825db26a9SVinicius Costa Gomes #include <net/sch_generic.h> 1925db26a9SVinicius Costa Gomes #include <net/pkt_sched.h> 2025db26a9SVinicius Costa Gomes #include <net/sock.h> 2125db26a9SVinicius Costa Gomes 2225db26a9SVinicius Costa Gomes #define DEADLINE_MODE_IS_ON(x) ((x)->flags & TC_ETF_DEADLINE_MODE_ON) 2388cab771SJesus Sanchez-Palencia #define OFFLOAD_IS_ON(x) ((x)->flags & TC_ETF_OFFLOAD_ON) 2425db26a9SVinicius Costa Gomes 2525db26a9SVinicius Costa Gomes struct etf_sched_data { 2688cab771SJesus Sanchez-Palencia bool offload; 2725db26a9SVinicius Costa Gomes bool deadline_mode; 2825db26a9SVinicius Costa Gomes int clockid; 2925db26a9SVinicius Costa Gomes int queue; 3025db26a9SVinicius Costa Gomes s32 delta; /* in ns */ 3125db26a9SVinicius Costa Gomes ktime_t last; /* The txtime of the last skb sent to the netdevice. */ 3225db26a9SVinicius Costa Gomes struct rb_root head; 3325db26a9SVinicius Costa Gomes struct qdisc_watchdog watchdog; 3425db26a9SVinicius Costa Gomes ktime_t (*get_time)(void); 3525db26a9SVinicius Costa Gomes }; 3625db26a9SVinicius Costa Gomes 3725db26a9SVinicius Costa Gomes static const struct nla_policy etf_policy[TCA_ETF_MAX + 1] = { 3825db26a9SVinicius Costa Gomes [TCA_ETF_PARMS] = { .len = sizeof(struct tc_etf_qopt) }, 3925db26a9SVinicius Costa Gomes }; 4025db26a9SVinicius Costa Gomes 4125db26a9SVinicius Costa Gomes static inline int validate_input_params(struct tc_etf_qopt *qopt, 4225db26a9SVinicius Costa Gomes struct netlink_ext_ack *extack) 4325db26a9SVinicius Costa Gomes { 4425db26a9SVinicius Costa Gomes /* Check if params comply to the following rules: 4525db26a9SVinicius Costa Gomes * * Clockid and delta must be valid. 4625db26a9SVinicius Costa Gomes * 4725db26a9SVinicius Costa Gomes * * Dynamic clockids are not supported. 4825db26a9SVinicius Costa Gomes * 4925db26a9SVinicius Costa Gomes * * Delta must be a positive integer. 5088cab771SJesus Sanchez-Palencia * 5188cab771SJesus Sanchez-Palencia * Also note that for the HW offload case, we must 5288cab771SJesus Sanchez-Palencia * expect that system clocks have been synchronized to PHC. 5325db26a9SVinicius Costa Gomes */ 5425db26a9SVinicius Costa Gomes if (qopt->clockid < 0) { 5525db26a9SVinicius Costa Gomes NL_SET_ERR_MSG(extack, "Dynamic clockids are not supported"); 5625db26a9SVinicius Costa Gomes return -ENOTSUPP; 5725db26a9SVinicius Costa Gomes } 5825db26a9SVinicius Costa Gomes 5925db26a9SVinicius Costa Gomes if (qopt->clockid != CLOCK_TAI) { 6025db26a9SVinicius Costa Gomes NL_SET_ERR_MSG(extack, "Invalid clockid. CLOCK_TAI must be used"); 6125db26a9SVinicius Costa Gomes return -EINVAL; 6225db26a9SVinicius Costa Gomes } 6325db26a9SVinicius Costa Gomes 6425db26a9SVinicius Costa Gomes if (qopt->delta < 0) { 6525db26a9SVinicius Costa Gomes NL_SET_ERR_MSG(extack, "Delta must be positive"); 6625db26a9SVinicius Costa Gomes return -EINVAL; 6725db26a9SVinicius Costa Gomes } 6825db26a9SVinicius Costa Gomes 6925db26a9SVinicius Costa Gomes return 0; 7025db26a9SVinicius Costa Gomes } 7125db26a9SVinicius Costa Gomes 7225db26a9SVinicius Costa Gomes static bool is_packet_valid(struct Qdisc *sch, struct sk_buff *nskb) 7325db26a9SVinicius Costa Gomes { 7425db26a9SVinicius Costa Gomes struct etf_sched_data *q = qdisc_priv(sch); 7525db26a9SVinicius Costa Gomes ktime_t txtime = nskb->tstamp; 7625db26a9SVinicius Costa Gomes struct sock *sk = nskb->sk; 7725db26a9SVinicius Costa Gomes ktime_t now; 7825db26a9SVinicius Costa Gomes 7925db26a9SVinicius Costa Gomes if (!sk) 8025db26a9SVinicius Costa Gomes return false; 8125db26a9SVinicius Costa Gomes 8225db26a9SVinicius Costa Gomes if (!sock_flag(sk, SOCK_TXTIME)) 8325db26a9SVinicius Costa Gomes return false; 8425db26a9SVinicius Costa Gomes 8525db26a9SVinicius Costa Gomes /* We don't perform crosstimestamping. 8625db26a9SVinicius Costa Gomes * Drop if packet's clockid differs from qdisc's. 8725db26a9SVinicius Costa Gomes */ 8825db26a9SVinicius Costa Gomes if (sk->sk_clockid != q->clockid) 8925db26a9SVinicius Costa Gomes return false; 9025db26a9SVinicius Costa Gomes 9125db26a9SVinicius Costa Gomes if (sk->sk_txtime_deadline_mode != q->deadline_mode) 9225db26a9SVinicius Costa Gomes return false; 9325db26a9SVinicius Costa Gomes 9425db26a9SVinicius Costa Gomes now = q->get_time(); 9525db26a9SVinicius Costa Gomes if (ktime_before(txtime, now) || ktime_before(txtime, q->last)) 9625db26a9SVinicius Costa Gomes return false; 9725db26a9SVinicius Costa Gomes 9825db26a9SVinicius Costa Gomes return true; 9925db26a9SVinicius Costa Gomes } 10025db26a9SVinicius Costa Gomes 10125db26a9SVinicius Costa Gomes static struct sk_buff *etf_peek_timesortedlist(struct Qdisc *sch) 10225db26a9SVinicius Costa Gomes { 10325db26a9SVinicius Costa Gomes struct etf_sched_data *q = qdisc_priv(sch); 10425db26a9SVinicius Costa Gomes struct rb_node *p; 10525db26a9SVinicius Costa Gomes 10625db26a9SVinicius Costa Gomes p = rb_first(&q->head); 10725db26a9SVinicius Costa Gomes if (!p) 10825db26a9SVinicius Costa Gomes return NULL; 10925db26a9SVinicius Costa Gomes 11025db26a9SVinicius Costa Gomes return rb_to_skb(p); 11125db26a9SVinicius Costa Gomes } 11225db26a9SVinicius Costa Gomes 11325db26a9SVinicius Costa Gomes static void reset_watchdog(struct Qdisc *sch) 11425db26a9SVinicius Costa Gomes { 11525db26a9SVinicius Costa Gomes struct etf_sched_data *q = qdisc_priv(sch); 11625db26a9SVinicius Costa Gomes struct sk_buff *skb = etf_peek_timesortedlist(sch); 11725db26a9SVinicius Costa Gomes ktime_t next; 11825db26a9SVinicius Costa Gomes 11925db26a9SVinicius Costa Gomes if (!skb) 12025db26a9SVinicius Costa Gomes return; 12125db26a9SVinicius Costa Gomes 12225db26a9SVinicius Costa Gomes next = ktime_sub_ns(skb->tstamp, q->delta); 12325db26a9SVinicius Costa Gomes qdisc_watchdog_schedule_ns(&q->watchdog, ktime_to_ns(next)); 12425db26a9SVinicius Costa Gomes } 12525db26a9SVinicius Costa Gomes 12625db26a9SVinicius Costa Gomes static int etf_enqueue_timesortedlist(struct sk_buff *nskb, struct Qdisc *sch, 12725db26a9SVinicius Costa Gomes struct sk_buff **to_free) 12825db26a9SVinicius Costa Gomes { 12925db26a9SVinicius Costa Gomes struct etf_sched_data *q = qdisc_priv(sch); 13025db26a9SVinicius Costa Gomes struct rb_node **p = &q->head.rb_node, *parent = NULL; 13125db26a9SVinicius Costa Gomes ktime_t txtime = nskb->tstamp; 13225db26a9SVinicius Costa Gomes 13325db26a9SVinicius Costa Gomes if (!is_packet_valid(sch, nskb)) 13425db26a9SVinicius Costa Gomes return qdisc_drop(nskb, sch, to_free); 13525db26a9SVinicius Costa Gomes 13625db26a9SVinicius Costa Gomes while (*p) { 13725db26a9SVinicius Costa Gomes struct sk_buff *skb; 13825db26a9SVinicius Costa Gomes 13925db26a9SVinicius Costa Gomes parent = *p; 14025db26a9SVinicius Costa Gomes skb = rb_to_skb(parent); 14125db26a9SVinicius Costa Gomes if (ktime_after(txtime, skb->tstamp)) 14225db26a9SVinicius Costa Gomes p = &parent->rb_right; 14325db26a9SVinicius Costa Gomes else 14425db26a9SVinicius Costa Gomes p = &parent->rb_left; 14525db26a9SVinicius Costa Gomes } 14625db26a9SVinicius Costa Gomes rb_link_node(&nskb->rbnode, parent, p); 14725db26a9SVinicius Costa Gomes rb_insert_color(&nskb->rbnode, &q->head); 14825db26a9SVinicius Costa Gomes 14925db26a9SVinicius Costa Gomes qdisc_qstats_backlog_inc(sch, nskb); 15025db26a9SVinicius Costa Gomes sch->q.qlen++; 15125db26a9SVinicius Costa Gomes 15225db26a9SVinicius Costa Gomes /* Now we may need to re-arm the qdisc watchdog for the next packet. */ 15325db26a9SVinicius Costa Gomes reset_watchdog(sch); 15425db26a9SVinicius Costa Gomes 15525db26a9SVinicius Costa Gomes return NET_XMIT_SUCCESS; 15625db26a9SVinicius Costa Gomes } 15725db26a9SVinicius Costa Gomes 15825db26a9SVinicius Costa Gomes static void timesortedlist_erase(struct Qdisc *sch, struct sk_buff *skb, 15925db26a9SVinicius Costa Gomes bool drop) 16025db26a9SVinicius Costa Gomes { 16125db26a9SVinicius Costa Gomes struct etf_sched_data *q = qdisc_priv(sch); 16225db26a9SVinicius Costa Gomes 16325db26a9SVinicius Costa Gomes rb_erase(&skb->rbnode, &q->head); 16425db26a9SVinicius Costa Gomes 16525db26a9SVinicius Costa Gomes /* The rbnode field in the skb re-uses these fields, now that 16625db26a9SVinicius Costa Gomes * we are done with the rbnode, reset them. 16725db26a9SVinicius Costa Gomes */ 16825db26a9SVinicius Costa Gomes skb->next = NULL; 16925db26a9SVinicius Costa Gomes skb->prev = NULL; 17025db26a9SVinicius Costa Gomes skb->dev = qdisc_dev(sch); 17125db26a9SVinicius Costa Gomes 17225db26a9SVinicius Costa Gomes qdisc_qstats_backlog_dec(sch, skb); 17325db26a9SVinicius Costa Gomes 17425db26a9SVinicius Costa Gomes if (drop) { 17525db26a9SVinicius Costa Gomes struct sk_buff *to_free = NULL; 17625db26a9SVinicius Costa Gomes 17725db26a9SVinicius Costa Gomes qdisc_drop(skb, sch, &to_free); 17825db26a9SVinicius Costa Gomes kfree_skb_list(to_free); 17925db26a9SVinicius Costa Gomes qdisc_qstats_overlimit(sch); 18025db26a9SVinicius Costa Gomes } else { 18125db26a9SVinicius Costa Gomes qdisc_bstats_update(sch, skb); 18225db26a9SVinicius Costa Gomes 18325db26a9SVinicius Costa Gomes q->last = skb->tstamp; 18425db26a9SVinicius Costa Gomes } 18525db26a9SVinicius Costa Gomes 18625db26a9SVinicius Costa Gomes sch->q.qlen--; 18725db26a9SVinicius Costa Gomes } 18825db26a9SVinicius Costa Gomes 18925db26a9SVinicius Costa Gomes static struct sk_buff *etf_dequeue_timesortedlist(struct Qdisc *sch) 19025db26a9SVinicius Costa Gomes { 19125db26a9SVinicius Costa Gomes struct etf_sched_data *q = qdisc_priv(sch); 19225db26a9SVinicius Costa Gomes struct sk_buff *skb; 19325db26a9SVinicius Costa Gomes ktime_t now, next; 19425db26a9SVinicius Costa Gomes 19525db26a9SVinicius Costa Gomes skb = etf_peek_timesortedlist(sch); 19625db26a9SVinicius Costa Gomes if (!skb) 19725db26a9SVinicius Costa Gomes return NULL; 19825db26a9SVinicius Costa Gomes 19925db26a9SVinicius Costa Gomes now = q->get_time(); 20025db26a9SVinicius Costa Gomes 20125db26a9SVinicius Costa Gomes /* Drop if packet has expired while in queue. */ 20225db26a9SVinicius Costa Gomes /* FIXME: Must return error on the socket's error queue */ 20325db26a9SVinicius Costa Gomes if (ktime_before(skb->tstamp, now)) { 20425db26a9SVinicius Costa Gomes timesortedlist_erase(sch, skb, true); 20525db26a9SVinicius Costa Gomes skb = NULL; 20625db26a9SVinicius Costa Gomes goto out; 20725db26a9SVinicius Costa Gomes } 20825db26a9SVinicius Costa Gomes 20925db26a9SVinicius Costa Gomes /* When in deadline mode, dequeue as soon as possible and change the 21025db26a9SVinicius Costa Gomes * txtime from deadline to (now + delta). 21125db26a9SVinicius Costa Gomes */ 21225db26a9SVinicius Costa Gomes if (q->deadline_mode) { 21325db26a9SVinicius Costa Gomes timesortedlist_erase(sch, skb, false); 21425db26a9SVinicius Costa Gomes skb->tstamp = now; 21525db26a9SVinicius Costa Gomes goto out; 21625db26a9SVinicius Costa Gomes } 21725db26a9SVinicius Costa Gomes 21825db26a9SVinicius Costa Gomes next = ktime_sub_ns(skb->tstamp, q->delta); 21925db26a9SVinicius Costa Gomes 22025db26a9SVinicius Costa Gomes /* Dequeue only if now is within the [txtime - delta, txtime] range. */ 22125db26a9SVinicius Costa Gomes if (ktime_after(now, next)) 22225db26a9SVinicius Costa Gomes timesortedlist_erase(sch, skb, false); 22325db26a9SVinicius Costa Gomes else 22425db26a9SVinicius Costa Gomes skb = NULL; 22525db26a9SVinicius Costa Gomes 22625db26a9SVinicius Costa Gomes out: 22725db26a9SVinicius Costa Gomes /* Now we may need to re-arm the qdisc watchdog for the next packet. */ 22825db26a9SVinicius Costa Gomes reset_watchdog(sch); 22925db26a9SVinicius Costa Gomes 23025db26a9SVinicius Costa Gomes return skb; 23125db26a9SVinicius Costa Gomes } 23225db26a9SVinicius Costa Gomes 23388cab771SJesus Sanchez-Palencia static void etf_disable_offload(struct net_device *dev, 23488cab771SJesus Sanchez-Palencia struct etf_sched_data *q) 23588cab771SJesus Sanchez-Palencia { 23688cab771SJesus Sanchez-Palencia struct tc_etf_qopt_offload etf = { }; 23788cab771SJesus Sanchez-Palencia const struct net_device_ops *ops; 23888cab771SJesus Sanchez-Palencia int err; 23988cab771SJesus Sanchez-Palencia 24088cab771SJesus Sanchez-Palencia if (!q->offload) 24188cab771SJesus Sanchez-Palencia return; 24288cab771SJesus Sanchez-Palencia 24388cab771SJesus Sanchez-Palencia ops = dev->netdev_ops; 24488cab771SJesus Sanchez-Palencia if (!ops->ndo_setup_tc) 24588cab771SJesus Sanchez-Palencia return; 24688cab771SJesus Sanchez-Palencia 24788cab771SJesus Sanchez-Palencia etf.queue = q->queue; 24888cab771SJesus Sanchez-Palencia etf.enable = 0; 24988cab771SJesus Sanchez-Palencia 25088cab771SJesus Sanchez-Palencia err = ops->ndo_setup_tc(dev, TC_SETUP_QDISC_ETF, &etf); 25188cab771SJesus Sanchez-Palencia if (err < 0) 25288cab771SJesus Sanchez-Palencia pr_warn("Couldn't disable ETF offload for queue %d\n", 25388cab771SJesus Sanchez-Palencia etf.queue); 25488cab771SJesus Sanchez-Palencia } 25588cab771SJesus Sanchez-Palencia 25688cab771SJesus Sanchez-Palencia static int etf_enable_offload(struct net_device *dev, struct etf_sched_data *q, 25788cab771SJesus Sanchez-Palencia struct netlink_ext_ack *extack) 25888cab771SJesus Sanchez-Palencia { 25988cab771SJesus Sanchez-Palencia const struct net_device_ops *ops = dev->netdev_ops; 26088cab771SJesus Sanchez-Palencia struct tc_etf_qopt_offload etf = { }; 26188cab771SJesus Sanchez-Palencia int err; 26288cab771SJesus Sanchez-Palencia 26388cab771SJesus Sanchez-Palencia if (q->offload) 26488cab771SJesus Sanchez-Palencia return 0; 26588cab771SJesus Sanchez-Palencia 26688cab771SJesus Sanchez-Palencia if (!ops->ndo_setup_tc) { 26788cab771SJesus Sanchez-Palencia NL_SET_ERR_MSG(extack, "Specified device does not support ETF offload"); 26888cab771SJesus Sanchez-Palencia return -EOPNOTSUPP; 26988cab771SJesus Sanchez-Palencia } 27088cab771SJesus Sanchez-Palencia 27188cab771SJesus Sanchez-Palencia etf.queue = q->queue; 27288cab771SJesus Sanchez-Palencia etf.enable = 1; 27388cab771SJesus Sanchez-Palencia 27488cab771SJesus Sanchez-Palencia err = ops->ndo_setup_tc(dev, TC_SETUP_QDISC_ETF, &etf); 27588cab771SJesus Sanchez-Palencia if (err < 0) { 27688cab771SJesus Sanchez-Palencia NL_SET_ERR_MSG(extack, "Specified device failed to setup ETF hardware offload"); 27788cab771SJesus Sanchez-Palencia return err; 27888cab771SJesus Sanchez-Palencia } 27988cab771SJesus Sanchez-Palencia 28088cab771SJesus Sanchez-Palencia return 0; 28188cab771SJesus Sanchez-Palencia } 28288cab771SJesus Sanchez-Palencia 28325db26a9SVinicius Costa Gomes static int etf_init(struct Qdisc *sch, struct nlattr *opt, 28425db26a9SVinicius Costa Gomes struct netlink_ext_ack *extack) 28525db26a9SVinicius Costa Gomes { 28625db26a9SVinicius Costa Gomes struct etf_sched_data *q = qdisc_priv(sch); 28725db26a9SVinicius Costa Gomes struct net_device *dev = qdisc_dev(sch); 28825db26a9SVinicius Costa Gomes struct nlattr *tb[TCA_ETF_MAX + 1]; 28925db26a9SVinicius Costa Gomes struct tc_etf_qopt *qopt; 29025db26a9SVinicius Costa Gomes int err; 29125db26a9SVinicius Costa Gomes 29225db26a9SVinicius Costa Gomes if (!opt) { 29325db26a9SVinicius Costa Gomes NL_SET_ERR_MSG(extack, 29425db26a9SVinicius Costa Gomes "Missing ETF qdisc options which are mandatory"); 29525db26a9SVinicius Costa Gomes return -EINVAL; 29625db26a9SVinicius Costa Gomes } 29725db26a9SVinicius Costa Gomes 29825db26a9SVinicius Costa Gomes err = nla_parse_nested(tb, TCA_ETF_MAX, opt, etf_policy, extack); 29925db26a9SVinicius Costa Gomes if (err < 0) 30025db26a9SVinicius Costa Gomes return err; 30125db26a9SVinicius Costa Gomes 30225db26a9SVinicius Costa Gomes if (!tb[TCA_ETF_PARMS]) { 30325db26a9SVinicius Costa Gomes NL_SET_ERR_MSG(extack, "Missing mandatory ETF parameters"); 30425db26a9SVinicius Costa Gomes return -EINVAL; 30525db26a9SVinicius Costa Gomes } 30625db26a9SVinicius Costa Gomes 30725db26a9SVinicius Costa Gomes qopt = nla_data(tb[TCA_ETF_PARMS]); 30825db26a9SVinicius Costa Gomes 30988cab771SJesus Sanchez-Palencia pr_debug("delta %d clockid %d offload %s deadline %s\n", 31025db26a9SVinicius Costa Gomes qopt->delta, qopt->clockid, 31188cab771SJesus Sanchez-Palencia OFFLOAD_IS_ON(qopt) ? "on" : "off", 31225db26a9SVinicius Costa Gomes DEADLINE_MODE_IS_ON(qopt) ? "on" : "off"); 31325db26a9SVinicius Costa Gomes 31425db26a9SVinicius Costa Gomes err = validate_input_params(qopt, extack); 31525db26a9SVinicius Costa Gomes if (err < 0) 31625db26a9SVinicius Costa Gomes return err; 31725db26a9SVinicius Costa Gomes 31825db26a9SVinicius Costa Gomes q->queue = sch->dev_queue - netdev_get_tx_queue(dev, 0); 31925db26a9SVinicius Costa Gomes 32088cab771SJesus Sanchez-Palencia if (OFFLOAD_IS_ON(qopt)) { 32188cab771SJesus Sanchez-Palencia err = etf_enable_offload(dev, q, extack); 32288cab771SJesus Sanchez-Palencia if (err < 0) 32388cab771SJesus Sanchez-Palencia return err; 32488cab771SJesus Sanchez-Palencia } 32588cab771SJesus Sanchez-Palencia 32625db26a9SVinicius Costa Gomes /* Everything went OK, save the parameters used. */ 32725db26a9SVinicius Costa Gomes q->delta = qopt->delta; 32825db26a9SVinicius Costa Gomes q->clockid = qopt->clockid; 32988cab771SJesus Sanchez-Palencia q->offload = OFFLOAD_IS_ON(qopt); 33025db26a9SVinicius Costa Gomes q->deadline_mode = DEADLINE_MODE_IS_ON(qopt); 33125db26a9SVinicius Costa Gomes 33225db26a9SVinicius Costa Gomes switch (q->clockid) { 33325db26a9SVinicius Costa Gomes case CLOCK_REALTIME: 33425db26a9SVinicius Costa Gomes q->get_time = ktime_get_real; 33525db26a9SVinicius Costa Gomes break; 33625db26a9SVinicius Costa Gomes case CLOCK_MONOTONIC: 33725db26a9SVinicius Costa Gomes q->get_time = ktime_get; 33825db26a9SVinicius Costa Gomes break; 33925db26a9SVinicius Costa Gomes case CLOCK_BOOTTIME: 34025db26a9SVinicius Costa Gomes q->get_time = ktime_get_boottime; 34125db26a9SVinicius Costa Gomes break; 34225db26a9SVinicius Costa Gomes case CLOCK_TAI: 34325db26a9SVinicius Costa Gomes q->get_time = ktime_get_clocktai; 34425db26a9SVinicius Costa Gomes break; 34525db26a9SVinicius Costa Gomes default: 34625db26a9SVinicius Costa Gomes NL_SET_ERR_MSG(extack, "Clockid is not supported"); 34725db26a9SVinicius Costa Gomes return -ENOTSUPP; 34825db26a9SVinicius Costa Gomes } 34925db26a9SVinicius Costa Gomes 35025db26a9SVinicius Costa Gomes qdisc_watchdog_init_clockid(&q->watchdog, sch, q->clockid); 35125db26a9SVinicius Costa Gomes 35225db26a9SVinicius Costa Gomes return 0; 35325db26a9SVinicius Costa Gomes } 35425db26a9SVinicius Costa Gomes 35525db26a9SVinicius Costa Gomes static void timesortedlist_clear(struct Qdisc *sch) 35625db26a9SVinicius Costa Gomes { 35725db26a9SVinicius Costa Gomes struct etf_sched_data *q = qdisc_priv(sch); 35825db26a9SVinicius Costa Gomes struct rb_node *p = rb_first(&q->head); 35925db26a9SVinicius Costa Gomes 36025db26a9SVinicius Costa Gomes while (p) { 36125db26a9SVinicius Costa Gomes struct sk_buff *skb = rb_to_skb(p); 36225db26a9SVinicius Costa Gomes 36325db26a9SVinicius Costa Gomes p = rb_next(p); 36425db26a9SVinicius Costa Gomes 36525db26a9SVinicius Costa Gomes rb_erase(&skb->rbnode, &q->head); 36625db26a9SVinicius Costa Gomes rtnl_kfree_skbs(skb, skb); 36725db26a9SVinicius Costa Gomes sch->q.qlen--; 36825db26a9SVinicius Costa Gomes } 36925db26a9SVinicius Costa Gomes } 37025db26a9SVinicius Costa Gomes 37125db26a9SVinicius Costa Gomes static void etf_reset(struct Qdisc *sch) 37225db26a9SVinicius Costa Gomes { 37325db26a9SVinicius Costa Gomes struct etf_sched_data *q = qdisc_priv(sch); 37425db26a9SVinicius Costa Gomes 37525db26a9SVinicius Costa Gomes /* Only cancel watchdog if it's been initialized. */ 37625db26a9SVinicius Costa Gomes if (q->watchdog.qdisc == sch) 37725db26a9SVinicius Costa Gomes qdisc_watchdog_cancel(&q->watchdog); 37825db26a9SVinicius Costa Gomes 37925db26a9SVinicius Costa Gomes /* No matter which mode we are on, it's safe to clear both lists. */ 38025db26a9SVinicius Costa Gomes timesortedlist_clear(sch); 38125db26a9SVinicius Costa Gomes __qdisc_reset_queue(&sch->q); 38225db26a9SVinicius Costa Gomes 38325db26a9SVinicius Costa Gomes sch->qstats.backlog = 0; 38425db26a9SVinicius Costa Gomes sch->q.qlen = 0; 38525db26a9SVinicius Costa Gomes 38625db26a9SVinicius Costa Gomes q->last = 0; 38725db26a9SVinicius Costa Gomes } 38825db26a9SVinicius Costa Gomes 38925db26a9SVinicius Costa Gomes static void etf_destroy(struct Qdisc *sch) 39025db26a9SVinicius Costa Gomes { 39125db26a9SVinicius Costa Gomes struct etf_sched_data *q = qdisc_priv(sch); 39288cab771SJesus Sanchez-Palencia struct net_device *dev = qdisc_dev(sch); 39325db26a9SVinicius Costa Gomes 39425db26a9SVinicius Costa Gomes /* Only cancel watchdog if it's been initialized. */ 39525db26a9SVinicius Costa Gomes if (q->watchdog.qdisc == sch) 39625db26a9SVinicius Costa Gomes qdisc_watchdog_cancel(&q->watchdog); 39788cab771SJesus Sanchez-Palencia 39888cab771SJesus Sanchez-Palencia etf_disable_offload(dev, q); 39925db26a9SVinicius Costa Gomes } 40025db26a9SVinicius Costa Gomes 40125db26a9SVinicius Costa Gomes static int etf_dump(struct Qdisc *sch, struct sk_buff *skb) 40225db26a9SVinicius Costa Gomes { 40325db26a9SVinicius Costa Gomes struct etf_sched_data *q = qdisc_priv(sch); 40425db26a9SVinicius Costa Gomes struct tc_etf_qopt opt = { }; 40525db26a9SVinicius Costa Gomes struct nlattr *nest; 40625db26a9SVinicius Costa Gomes 40725db26a9SVinicius Costa Gomes nest = nla_nest_start(skb, TCA_OPTIONS); 40825db26a9SVinicius Costa Gomes if (!nest) 40925db26a9SVinicius Costa Gomes goto nla_put_failure; 41025db26a9SVinicius Costa Gomes 41125db26a9SVinicius Costa Gomes opt.delta = q->delta; 41225db26a9SVinicius Costa Gomes opt.clockid = q->clockid; 41388cab771SJesus Sanchez-Palencia if (q->offload) 41488cab771SJesus Sanchez-Palencia opt.flags |= TC_ETF_OFFLOAD_ON; 41588cab771SJesus Sanchez-Palencia 41625db26a9SVinicius Costa Gomes if (q->deadline_mode) 41725db26a9SVinicius Costa Gomes opt.flags |= TC_ETF_DEADLINE_MODE_ON; 41825db26a9SVinicius Costa Gomes 41925db26a9SVinicius Costa Gomes if (nla_put(skb, TCA_ETF_PARMS, sizeof(opt), &opt)) 42025db26a9SVinicius Costa Gomes goto nla_put_failure; 42125db26a9SVinicius Costa Gomes 42225db26a9SVinicius Costa Gomes return nla_nest_end(skb, nest); 42325db26a9SVinicius Costa Gomes 42425db26a9SVinicius Costa Gomes nla_put_failure: 42525db26a9SVinicius Costa Gomes nla_nest_cancel(skb, nest); 42625db26a9SVinicius Costa Gomes return -1; 42725db26a9SVinicius Costa Gomes } 42825db26a9SVinicius Costa Gomes 42925db26a9SVinicius Costa Gomes static struct Qdisc_ops etf_qdisc_ops __read_mostly = { 43025db26a9SVinicius Costa Gomes .id = "etf", 43125db26a9SVinicius Costa Gomes .priv_size = sizeof(struct etf_sched_data), 43225db26a9SVinicius Costa Gomes .enqueue = etf_enqueue_timesortedlist, 43325db26a9SVinicius Costa Gomes .dequeue = etf_dequeue_timesortedlist, 43425db26a9SVinicius Costa Gomes .peek = etf_peek_timesortedlist, 43525db26a9SVinicius Costa Gomes .init = etf_init, 43625db26a9SVinicius Costa Gomes .reset = etf_reset, 43725db26a9SVinicius Costa Gomes .destroy = etf_destroy, 43825db26a9SVinicius Costa Gomes .dump = etf_dump, 43925db26a9SVinicius Costa Gomes .owner = THIS_MODULE, 44025db26a9SVinicius Costa Gomes }; 44125db26a9SVinicius Costa Gomes 44225db26a9SVinicius Costa Gomes static int __init etf_module_init(void) 44325db26a9SVinicius Costa Gomes { 44425db26a9SVinicius Costa Gomes return register_qdisc(&etf_qdisc_ops); 44525db26a9SVinicius Costa Gomes } 44625db26a9SVinicius Costa Gomes 44725db26a9SVinicius Costa Gomes static void __exit etf_module_exit(void) 44825db26a9SVinicius Costa Gomes { 44925db26a9SVinicius Costa Gomes unregister_qdisc(&etf_qdisc_ops); 45025db26a9SVinicius Costa Gomes } 45125db26a9SVinicius Costa Gomes module_init(etf_module_init) 45225db26a9SVinicius Costa Gomes module_exit(etf_module_exit) 45325db26a9SVinicius Costa Gomes MODULE_LICENSE("GPL"); 454