1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun
3*4882a593Smuzhiyun /* net/sched/sch_etf.c Earliest TxTime First queueing discipline.
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Authors: Jesus Sanchez-Palencia <jesus.sanchez-palencia@intel.com>
6*4882a593Smuzhiyun * Vinicius Costa Gomes <vinicius.gomes@intel.com>
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include <linux/module.h>
10*4882a593Smuzhiyun #include <linux/types.h>
11*4882a593Smuzhiyun #include <linux/kernel.h>
12*4882a593Smuzhiyun #include <linux/string.h>
13*4882a593Smuzhiyun #include <linux/errno.h>
14*4882a593Smuzhiyun #include <linux/errqueue.h>
15*4882a593Smuzhiyun #include <linux/rbtree.h>
16*4882a593Smuzhiyun #include <linux/skbuff.h>
17*4882a593Smuzhiyun #include <linux/posix-timers.h>
18*4882a593Smuzhiyun #include <net/netlink.h>
19*4882a593Smuzhiyun #include <net/sch_generic.h>
20*4882a593Smuzhiyun #include <net/pkt_sched.h>
21*4882a593Smuzhiyun #include <net/sock.h>
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun #define DEADLINE_MODE_IS_ON(x) ((x)->flags & TC_ETF_DEADLINE_MODE_ON)
24*4882a593Smuzhiyun #define OFFLOAD_IS_ON(x) ((x)->flags & TC_ETF_OFFLOAD_ON)
25*4882a593Smuzhiyun #define SKIP_SOCK_CHECK_IS_SET(x) ((x)->flags & TC_ETF_SKIP_SOCK_CHECK)
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun struct etf_sched_data {
28*4882a593Smuzhiyun bool offload;
29*4882a593Smuzhiyun bool deadline_mode;
30*4882a593Smuzhiyun bool skip_sock_check;
31*4882a593Smuzhiyun int clockid;
32*4882a593Smuzhiyun int queue;
33*4882a593Smuzhiyun s32 delta; /* in ns */
34*4882a593Smuzhiyun ktime_t last; /* The txtime of the last skb sent to the netdevice. */
35*4882a593Smuzhiyun struct rb_root_cached head;
36*4882a593Smuzhiyun struct qdisc_watchdog watchdog;
37*4882a593Smuzhiyun ktime_t (*get_time)(void);
38*4882a593Smuzhiyun };
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun static const struct nla_policy etf_policy[TCA_ETF_MAX + 1] = {
41*4882a593Smuzhiyun [TCA_ETF_PARMS] = { .len = sizeof(struct tc_etf_qopt) },
42*4882a593Smuzhiyun };
43*4882a593Smuzhiyun
validate_input_params(struct tc_etf_qopt * qopt,struct netlink_ext_ack * extack)44*4882a593Smuzhiyun static inline int validate_input_params(struct tc_etf_qopt *qopt,
45*4882a593Smuzhiyun struct netlink_ext_ack *extack)
46*4882a593Smuzhiyun {
47*4882a593Smuzhiyun /* Check if params comply to the following rules:
48*4882a593Smuzhiyun * * Clockid and delta must be valid.
49*4882a593Smuzhiyun *
50*4882a593Smuzhiyun * * Dynamic clockids are not supported.
51*4882a593Smuzhiyun *
52*4882a593Smuzhiyun * * Delta must be a positive integer.
53*4882a593Smuzhiyun *
54*4882a593Smuzhiyun * Also note that for the HW offload case, we must
55*4882a593Smuzhiyun * expect that system clocks have been synchronized to PHC.
56*4882a593Smuzhiyun */
57*4882a593Smuzhiyun if (qopt->clockid < 0) {
58*4882a593Smuzhiyun NL_SET_ERR_MSG(extack, "Dynamic clockids are not supported");
59*4882a593Smuzhiyun return -ENOTSUPP;
60*4882a593Smuzhiyun }
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun if (qopt->clockid != CLOCK_TAI) {
63*4882a593Smuzhiyun NL_SET_ERR_MSG(extack, "Invalid clockid. CLOCK_TAI must be used");
64*4882a593Smuzhiyun return -EINVAL;
65*4882a593Smuzhiyun }
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun if (qopt->delta < 0) {
68*4882a593Smuzhiyun NL_SET_ERR_MSG(extack, "Delta must be positive");
69*4882a593Smuzhiyun return -EINVAL;
70*4882a593Smuzhiyun }
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun return 0;
73*4882a593Smuzhiyun }
74*4882a593Smuzhiyun
is_packet_valid(struct Qdisc * sch,struct sk_buff * nskb)75*4882a593Smuzhiyun static bool is_packet_valid(struct Qdisc *sch, struct sk_buff *nskb)
76*4882a593Smuzhiyun {
77*4882a593Smuzhiyun struct etf_sched_data *q = qdisc_priv(sch);
78*4882a593Smuzhiyun ktime_t txtime = nskb->tstamp;
79*4882a593Smuzhiyun struct sock *sk = nskb->sk;
80*4882a593Smuzhiyun ktime_t now;
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun if (q->skip_sock_check)
83*4882a593Smuzhiyun goto skip;
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun if (!sk || !sk_fullsock(sk))
86*4882a593Smuzhiyun return false;
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun if (!sock_flag(sk, SOCK_TXTIME))
89*4882a593Smuzhiyun return false;
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun /* We don't perform crosstimestamping.
92*4882a593Smuzhiyun * Drop if packet's clockid differs from qdisc's.
93*4882a593Smuzhiyun */
94*4882a593Smuzhiyun if (sk->sk_clockid != q->clockid)
95*4882a593Smuzhiyun return false;
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun if (sk->sk_txtime_deadline_mode != q->deadline_mode)
98*4882a593Smuzhiyun return false;
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun skip:
101*4882a593Smuzhiyun now = q->get_time();
102*4882a593Smuzhiyun if (ktime_before(txtime, now) || ktime_before(txtime, q->last))
103*4882a593Smuzhiyun return false;
104*4882a593Smuzhiyun
105*4882a593Smuzhiyun return true;
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun
etf_peek_timesortedlist(struct Qdisc * sch)108*4882a593Smuzhiyun static struct sk_buff *etf_peek_timesortedlist(struct Qdisc *sch)
109*4882a593Smuzhiyun {
110*4882a593Smuzhiyun struct etf_sched_data *q = qdisc_priv(sch);
111*4882a593Smuzhiyun struct rb_node *p;
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun p = rb_first_cached(&q->head);
114*4882a593Smuzhiyun if (!p)
115*4882a593Smuzhiyun return NULL;
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun return rb_to_skb(p);
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun
reset_watchdog(struct Qdisc * sch)120*4882a593Smuzhiyun static void reset_watchdog(struct Qdisc *sch)
121*4882a593Smuzhiyun {
122*4882a593Smuzhiyun struct etf_sched_data *q = qdisc_priv(sch);
123*4882a593Smuzhiyun struct sk_buff *skb = etf_peek_timesortedlist(sch);
124*4882a593Smuzhiyun ktime_t next;
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun if (!skb) {
127*4882a593Smuzhiyun qdisc_watchdog_cancel(&q->watchdog);
128*4882a593Smuzhiyun return;
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun next = ktime_sub_ns(skb->tstamp, q->delta);
132*4882a593Smuzhiyun qdisc_watchdog_schedule_ns(&q->watchdog, ktime_to_ns(next));
133*4882a593Smuzhiyun }
134*4882a593Smuzhiyun
report_sock_error(struct sk_buff * skb,u32 err,u8 code)135*4882a593Smuzhiyun static void report_sock_error(struct sk_buff *skb, u32 err, u8 code)
136*4882a593Smuzhiyun {
137*4882a593Smuzhiyun struct sock_exterr_skb *serr;
138*4882a593Smuzhiyun struct sk_buff *clone;
139*4882a593Smuzhiyun ktime_t txtime = skb->tstamp;
140*4882a593Smuzhiyun struct sock *sk = skb->sk;
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun if (!sk || !sk_fullsock(sk) || !(sk->sk_txtime_report_errors))
143*4882a593Smuzhiyun return;
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun clone = skb_clone(skb, GFP_ATOMIC);
146*4882a593Smuzhiyun if (!clone)
147*4882a593Smuzhiyun return;
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun serr = SKB_EXT_ERR(clone);
150*4882a593Smuzhiyun serr->ee.ee_errno = err;
151*4882a593Smuzhiyun serr->ee.ee_origin = SO_EE_ORIGIN_TXTIME;
152*4882a593Smuzhiyun serr->ee.ee_type = 0;
153*4882a593Smuzhiyun serr->ee.ee_code = code;
154*4882a593Smuzhiyun serr->ee.ee_pad = 0;
155*4882a593Smuzhiyun serr->ee.ee_data = (txtime >> 32); /* high part of tstamp */
156*4882a593Smuzhiyun serr->ee.ee_info = txtime; /* low part of tstamp */
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun if (sock_queue_err_skb(sk, clone))
159*4882a593Smuzhiyun kfree_skb(clone);
160*4882a593Smuzhiyun }
161*4882a593Smuzhiyun
etf_enqueue_timesortedlist(struct sk_buff * nskb,struct Qdisc * sch,struct sk_buff ** to_free)162*4882a593Smuzhiyun static int etf_enqueue_timesortedlist(struct sk_buff *nskb, struct Qdisc *sch,
163*4882a593Smuzhiyun struct sk_buff **to_free)
164*4882a593Smuzhiyun {
165*4882a593Smuzhiyun struct etf_sched_data *q = qdisc_priv(sch);
166*4882a593Smuzhiyun struct rb_node **p = &q->head.rb_root.rb_node, *parent = NULL;
167*4882a593Smuzhiyun ktime_t txtime = nskb->tstamp;
168*4882a593Smuzhiyun bool leftmost = true;
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun if (!is_packet_valid(sch, nskb)) {
171*4882a593Smuzhiyun report_sock_error(nskb, EINVAL,
172*4882a593Smuzhiyun SO_EE_CODE_TXTIME_INVALID_PARAM);
173*4882a593Smuzhiyun return qdisc_drop(nskb, sch, to_free);
174*4882a593Smuzhiyun }
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun while (*p) {
177*4882a593Smuzhiyun struct sk_buff *skb;
178*4882a593Smuzhiyun
179*4882a593Smuzhiyun parent = *p;
180*4882a593Smuzhiyun skb = rb_to_skb(parent);
181*4882a593Smuzhiyun if (ktime_compare(txtime, skb->tstamp) >= 0) {
182*4882a593Smuzhiyun p = &parent->rb_right;
183*4882a593Smuzhiyun leftmost = false;
184*4882a593Smuzhiyun } else {
185*4882a593Smuzhiyun p = &parent->rb_left;
186*4882a593Smuzhiyun }
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun rb_link_node(&nskb->rbnode, parent, p);
189*4882a593Smuzhiyun rb_insert_color_cached(&nskb->rbnode, &q->head, leftmost);
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun qdisc_qstats_backlog_inc(sch, nskb);
192*4882a593Smuzhiyun sch->q.qlen++;
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun /* Now we may need to re-arm the qdisc watchdog for the next packet. */
195*4882a593Smuzhiyun reset_watchdog(sch);
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun return NET_XMIT_SUCCESS;
198*4882a593Smuzhiyun }
199*4882a593Smuzhiyun
timesortedlist_drop(struct Qdisc * sch,struct sk_buff * skb,ktime_t now)200*4882a593Smuzhiyun static void timesortedlist_drop(struct Qdisc *sch, struct sk_buff *skb,
201*4882a593Smuzhiyun ktime_t now)
202*4882a593Smuzhiyun {
203*4882a593Smuzhiyun struct etf_sched_data *q = qdisc_priv(sch);
204*4882a593Smuzhiyun struct sk_buff *to_free = NULL;
205*4882a593Smuzhiyun struct sk_buff *tmp = NULL;
206*4882a593Smuzhiyun
207*4882a593Smuzhiyun skb_rbtree_walk_from_safe(skb, tmp) {
208*4882a593Smuzhiyun if (ktime_after(skb->tstamp, now))
209*4882a593Smuzhiyun break;
210*4882a593Smuzhiyun
211*4882a593Smuzhiyun rb_erase_cached(&skb->rbnode, &q->head);
212*4882a593Smuzhiyun
213*4882a593Smuzhiyun /* The rbnode field in the skb re-uses these fields, now that
214*4882a593Smuzhiyun * we are done with the rbnode, reset them.
215*4882a593Smuzhiyun */
216*4882a593Smuzhiyun skb->next = NULL;
217*4882a593Smuzhiyun skb->prev = NULL;
218*4882a593Smuzhiyun skb->dev = qdisc_dev(sch);
219*4882a593Smuzhiyun
220*4882a593Smuzhiyun report_sock_error(skb, ECANCELED, SO_EE_CODE_TXTIME_MISSED);
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun qdisc_qstats_backlog_dec(sch, skb);
223*4882a593Smuzhiyun qdisc_drop(skb, sch, &to_free);
224*4882a593Smuzhiyun qdisc_qstats_overlimit(sch);
225*4882a593Smuzhiyun sch->q.qlen--;
226*4882a593Smuzhiyun }
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun kfree_skb_list(to_free);
229*4882a593Smuzhiyun }
230*4882a593Smuzhiyun
timesortedlist_remove(struct Qdisc * sch,struct sk_buff * skb)231*4882a593Smuzhiyun static void timesortedlist_remove(struct Qdisc *sch, struct sk_buff *skb)
232*4882a593Smuzhiyun {
233*4882a593Smuzhiyun struct etf_sched_data *q = qdisc_priv(sch);
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun rb_erase_cached(&skb->rbnode, &q->head);
236*4882a593Smuzhiyun
237*4882a593Smuzhiyun /* The rbnode field in the skb re-uses these fields, now that
238*4882a593Smuzhiyun * we are done with the rbnode, reset them.
239*4882a593Smuzhiyun */
240*4882a593Smuzhiyun skb->next = NULL;
241*4882a593Smuzhiyun skb->prev = NULL;
242*4882a593Smuzhiyun skb->dev = qdisc_dev(sch);
243*4882a593Smuzhiyun
244*4882a593Smuzhiyun qdisc_qstats_backlog_dec(sch, skb);
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun qdisc_bstats_update(sch, skb);
247*4882a593Smuzhiyun
248*4882a593Smuzhiyun q->last = skb->tstamp;
249*4882a593Smuzhiyun
250*4882a593Smuzhiyun sch->q.qlen--;
251*4882a593Smuzhiyun }
252*4882a593Smuzhiyun
etf_dequeue_timesortedlist(struct Qdisc * sch)253*4882a593Smuzhiyun static struct sk_buff *etf_dequeue_timesortedlist(struct Qdisc *sch)
254*4882a593Smuzhiyun {
255*4882a593Smuzhiyun struct etf_sched_data *q = qdisc_priv(sch);
256*4882a593Smuzhiyun struct sk_buff *skb;
257*4882a593Smuzhiyun ktime_t now, next;
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun skb = etf_peek_timesortedlist(sch);
260*4882a593Smuzhiyun if (!skb)
261*4882a593Smuzhiyun return NULL;
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun now = q->get_time();
264*4882a593Smuzhiyun
265*4882a593Smuzhiyun /* Drop if packet has expired while in queue. */
266*4882a593Smuzhiyun if (ktime_before(skb->tstamp, now)) {
267*4882a593Smuzhiyun timesortedlist_drop(sch, skb, now);
268*4882a593Smuzhiyun skb = NULL;
269*4882a593Smuzhiyun goto out;
270*4882a593Smuzhiyun }
271*4882a593Smuzhiyun
272*4882a593Smuzhiyun /* When in deadline mode, dequeue as soon as possible and change the
273*4882a593Smuzhiyun * txtime from deadline to (now + delta).
274*4882a593Smuzhiyun */
275*4882a593Smuzhiyun if (q->deadline_mode) {
276*4882a593Smuzhiyun timesortedlist_remove(sch, skb);
277*4882a593Smuzhiyun skb->tstamp = now;
278*4882a593Smuzhiyun goto out;
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun next = ktime_sub_ns(skb->tstamp, q->delta);
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun /* Dequeue only if now is within the [txtime - delta, txtime] range. */
284*4882a593Smuzhiyun if (ktime_after(now, next))
285*4882a593Smuzhiyun timesortedlist_remove(sch, skb);
286*4882a593Smuzhiyun else
287*4882a593Smuzhiyun skb = NULL;
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun out:
290*4882a593Smuzhiyun /* Now we may need to re-arm the qdisc watchdog for the next packet. */
291*4882a593Smuzhiyun reset_watchdog(sch);
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun return skb;
294*4882a593Smuzhiyun }
295*4882a593Smuzhiyun
etf_disable_offload(struct net_device * dev,struct etf_sched_data * q)296*4882a593Smuzhiyun static void etf_disable_offload(struct net_device *dev,
297*4882a593Smuzhiyun struct etf_sched_data *q)
298*4882a593Smuzhiyun {
299*4882a593Smuzhiyun struct tc_etf_qopt_offload etf = { };
300*4882a593Smuzhiyun const struct net_device_ops *ops;
301*4882a593Smuzhiyun int err;
302*4882a593Smuzhiyun
303*4882a593Smuzhiyun if (!q->offload)
304*4882a593Smuzhiyun return;
305*4882a593Smuzhiyun
306*4882a593Smuzhiyun ops = dev->netdev_ops;
307*4882a593Smuzhiyun if (!ops->ndo_setup_tc)
308*4882a593Smuzhiyun return;
309*4882a593Smuzhiyun
310*4882a593Smuzhiyun etf.queue = q->queue;
311*4882a593Smuzhiyun etf.enable = 0;
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun err = ops->ndo_setup_tc(dev, TC_SETUP_QDISC_ETF, &etf);
314*4882a593Smuzhiyun if (err < 0)
315*4882a593Smuzhiyun pr_warn("Couldn't disable ETF offload for queue %d\n",
316*4882a593Smuzhiyun etf.queue);
317*4882a593Smuzhiyun }
318*4882a593Smuzhiyun
etf_enable_offload(struct net_device * dev,struct etf_sched_data * q,struct netlink_ext_ack * extack)319*4882a593Smuzhiyun static int etf_enable_offload(struct net_device *dev, struct etf_sched_data *q,
320*4882a593Smuzhiyun struct netlink_ext_ack *extack)
321*4882a593Smuzhiyun {
322*4882a593Smuzhiyun const struct net_device_ops *ops = dev->netdev_ops;
323*4882a593Smuzhiyun struct tc_etf_qopt_offload etf = { };
324*4882a593Smuzhiyun int err;
325*4882a593Smuzhiyun
326*4882a593Smuzhiyun if (q->offload)
327*4882a593Smuzhiyun return 0;
328*4882a593Smuzhiyun
329*4882a593Smuzhiyun if (!ops->ndo_setup_tc) {
330*4882a593Smuzhiyun NL_SET_ERR_MSG(extack, "Specified device does not support ETF offload");
331*4882a593Smuzhiyun return -EOPNOTSUPP;
332*4882a593Smuzhiyun }
333*4882a593Smuzhiyun
334*4882a593Smuzhiyun etf.queue = q->queue;
335*4882a593Smuzhiyun etf.enable = 1;
336*4882a593Smuzhiyun
337*4882a593Smuzhiyun err = ops->ndo_setup_tc(dev, TC_SETUP_QDISC_ETF, &etf);
338*4882a593Smuzhiyun if (err < 0) {
339*4882a593Smuzhiyun NL_SET_ERR_MSG(extack, "Specified device failed to setup ETF hardware offload");
340*4882a593Smuzhiyun return err;
341*4882a593Smuzhiyun }
342*4882a593Smuzhiyun
343*4882a593Smuzhiyun return 0;
344*4882a593Smuzhiyun }
345*4882a593Smuzhiyun
etf_init(struct Qdisc * sch,struct nlattr * opt,struct netlink_ext_ack * extack)346*4882a593Smuzhiyun static int etf_init(struct Qdisc *sch, struct nlattr *opt,
347*4882a593Smuzhiyun struct netlink_ext_ack *extack)
348*4882a593Smuzhiyun {
349*4882a593Smuzhiyun struct etf_sched_data *q = qdisc_priv(sch);
350*4882a593Smuzhiyun struct net_device *dev = qdisc_dev(sch);
351*4882a593Smuzhiyun struct nlattr *tb[TCA_ETF_MAX + 1];
352*4882a593Smuzhiyun struct tc_etf_qopt *qopt;
353*4882a593Smuzhiyun int err;
354*4882a593Smuzhiyun
355*4882a593Smuzhiyun if (!opt) {
356*4882a593Smuzhiyun NL_SET_ERR_MSG(extack,
357*4882a593Smuzhiyun "Missing ETF qdisc options which are mandatory");
358*4882a593Smuzhiyun return -EINVAL;
359*4882a593Smuzhiyun }
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun err = nla_parse_nested_deprecated(tb, TCA_ETF_MAX, opt, etf_policy,
362*4882a593Smuzhiyun extack);
363*4882a593Smuzhiyun if (err < 0)
364*4882a593Smuzhiyun return err;
365*4882a593Smuzhiyun
366*4882a593Smuzhiyun if (!tb[TCA_ETF_PARMS]) {
367*4882a593Smuzhiyun NL_SET_ERR_MSG(extack, "Missing mandatory ETF parameters");
368*4882a593Smuzhiyun return -EINVAL;
369*4882a593Smuzhiyun }
370*4882a593Smuzhiyun
371*4882a593Smuzhiyun qopt = nla_data(tb[TCA_ETF_PARMS]);
372*4882a593Smuzhiyun
373*4882a593Smuzhiyun pr_debug("delta %d clockid %d offload %s deadline %s\n",
374*4882a593Smuzhiyun qopt->delta, qopt->clockid,
375*4882a593Smuzhiyun OFFLOAD_IS_ON(qopt) ? "on" : "off",
376*4882a593Smuzhiyun DEADLINE_MODE_IS_ON(qopt) ? "on" : "off");
377*4882a593Smuzhiyun
378*4882a593Smuzhiyun err = validate_input_params(qopt, extack);
379*4882a593Smuzhiyun if (err < 0)
380*4882a593Smuzhiyun return err;
381*4882a593Smuzhiyun
382*4882a593Smuzhiyun q->queue = sch->dev_queue - netdev_get_tx_queue(dev, 0);
383*4882a593Smuzhiyun
384*4882a593Smuzhiyun if (OFFLOAD_IS_ON(qopt)) {
385*4882a593Smuzhiyun err = etf_enable_offload(dev, q, extack);
386*4882a593Smuzhiyun if (err < 0)
387*4882a593Smuzhiyun return err;
388*4882a593Smuzhiyun }
389*4882a593Smuzhiyun
390*4882a593Smuzhiyun /* Everything went OK, save the parameters used. */
391*4882a593Smuzhiyun q->delta = qopt->delta;
392*4882a593Smuzhiyun q->clockid = qopt->clockid;
393*4882a593Smuzhiyun q->offload = OFFLOAD_IS_ON(qopt);
394*4882a593Smuzhiyun q->deadline_mode = DEADLINE_MODE_IS_ON(qopt);
395*4882a593Smuzhiyun q->skip_sock_check = SKIP_SOCK_CHECK_IS_SET(qopt);
396*4882a593Smuzhiyun
397*4882a593Smuzhiyun switch (q->clockid) {
398*4882a593Smuzhiyun case CLOCK_REALTIME:
399*4882a593Smuzhiyun q->get_time = ktime_get_real;
400*4882a593Smuzhiyun break;
401*4882a593Smuzhiyun case CLOCK_MONOTONIC:
402*4882a593Smuzhiyun q->get_time = ktime_get;
403*4882a593Smuzhiyun break;
404*4882a593Smuzhiyun case CLOCK_BOOTTIME:
405*4882a593Smuzhiyun q->get_time = ktime_get_boottime;
406*4882a593Smuzhiyun break;
407*4882a593Smuzhiyun case CLOCK_TAI:
408*4882a593Smuzhiyun q->get_time = ktime_get_clocktai;
409*4882a593Smuzhiyun break;
410*4882a593Smuzhiyun default:
411*4882a593Smuzhiyun NL_SET_ERR_MSG(extack, "Clockid is not supported");
412*4882a593Smuzhiyun return -ENOTSUPP;
413*4882a593Smuzhiyun }
414*4882a593Smuzhiyun
415*4882a593Smuzhiyun qdisc_watchdog_init_clockid(&q->watchdog, sch, q->clockid);
416*4882a593Smuzhiyun
417*4882a593Smuzhiyun return 0;
418*4882a593Smuzhiyun }
419*4882a593Smuzhiyun
timesortedlist_clear(struct Qdisc * sch)420*4882a593Smuzhiyun static void timesortedlist_clear(struct Qdisc *sch)
421*4882a593Smuzhiyun {
422*4882a593Smuzhiyun struct etf_sched_data *q = qdisc_priv(sch);
423*4882a593Smuzhiyun struct rb_node *p = rb_first_cached(&q->head);
424*4882a593Smuzhiyun
425*4882a593Smuzhiyun while (p) {
426*4882a593Smuzhiyun struct sk_buff *skb = rb_to_skb(p);
427*4882a593Smuzhiyun
428*4882a593Smuzhiyun p = rb_next(p);
429*4882a593Smuzhiyun
430*4882a593Smuzhiyun rb_erase_cached(&skb->rbnode, &q->head);
431*4882a593Smuzhiyun rtnl_kfree_skbs(skb, skb);
432*4882a593Smuzhiyun sch->q.qlen--;
433*4882a593Smuzhiyun }
434*4882a593Smuzhiyun }
435*4882a593Smuzhiyun
etf_reset(struct Qdisc * sch)436*4882a593Smuzhiyun static void etf_reset(struct Qdisc *sch)
437*4882a593Smuzhiyun {
438*4882a593Smuzhiyun struct etf_sched_data *q = qdisc_priv(sch);
439*4882a593Smuzhiyun
440*4882a593Smuzhiyun /* Only cancel watchdog if it's been initialized. */
441*4882a593Smuzhiyun if (q->watchdog.qdisc == sch)
442*4882a593Smuzhiyun qdisc_watchdog_cancel(&q->watchdog);
443*4882a593Smuzhiyun
444*4882a593Smuzhiyun /* No matter which mode we are on, it's safe to clear both lists. */
445*4882a593Smuzhiyun timesortedlist_clear(sch);
446*4882a593Smuzhiyun __qdisc_reset_queue(&sch->q);
447*4882a593Smuzhiyun
448*4882a593Smuzhiyun q->last = 0;
449*4882a593Smuzhiyun }
450*4882a593Smuzhiyun
etf_destroy(struct Qdisc * sch)451*4882a593Smuzhiyun static void etf_destroy(struct Qdisc *sch)
452*4882a593Smuzhiyun {
453*4882a593Smuzhiyun struct etf_sched_data *q = qdisc_priv(sch);
454*4882a593Smuzhiyun struct net_device *dev = qdisc_dev(sch);
455*4882a593Smuzhiyun
456*4882a593Smuzhiyun /* Only cancel watchdog if it's been initialized. */
457*4882a593Smuzhiyun if (q->watchdog.qdisc == sch)
458*4882a593Smuzhiyun qdisc_watchdog_cancel(&q->watchdog);
459*4882a593Smuzhiyun
460*4882a593Smuzhiyun etf_disable_offload(dev, q);
461*4882a593Smuzhiyun }
462*4882a593Smuzhiyun
etf_dump(struct Qdisc * sch,struct sk_buff * skb)463*4882a593Smuzhiyun static int etf_dump(struct Qdisc *sch, struct sk_buff *skb)
464*4882a593Smuzhiyun {
465*4882a593Smuzhiyun struct etf_sched_data *q = qdisc_priv(sch);
466*4882a593Smuzhiyun struct tc_etf_qopt opt = { };
467*4882a593Smuzhiyun struct nlattr *nest;
468*4882a593Smuzhiyun
469*4882a593Smuzhiyun nest = nla_nest_start_noflag(skb, TCA_OPTIONS);
470*4882a593Smuzhiyun if (!nest)
471*4882a593Smuzhiyun goto nla_put_failure;
472*4882a593Smuzhiyun
473*4882a593Smuzhiyun opt.delta = q->delta;
474*4882a593Smuzhiyun opt.clockid = q->clockid;
475*4882a593Smuzhiyun if (q->offload)
476*4882a593Smuzhiyun opt.flags |= TC_ETF_OFFLOAD_ON;
477*4882a593Smuzhiyun
478*4882a593Smuzhiyun if (q->deadline_mode)
479*4882a593Smuzhiyun opt.flags |= TC_ETF_DEADLINE_MODE_ON;
480*4882a593Smuzhiyun
481*4882a593Smuzhiyun if (q->skip_sock_check)
482*4882a593Smuzhiyun opt.flags |= TC_ETF_SKIP_SOCK_CHECK;
483*4882a593Smuzhiyun
484*4882a593Smuzhiyun if (nla_put(skb, TCA_ETF_PARMS, sizeof(opt), &opt))
485*4882a593Smuzhiyun goto nla_put_failure;
486*4882a593Smuzhiyun
487*4882a593Smuzhiyun return nla_nest_end(skb, nest);
488*4882a593Smuzhiyun
489*4882a593Smuzhiyun nla_put_failure:
490*4882a593Smuzhiyun nla_nest_cancel(skb, nest);
491*4882a593Smuzhiyun return -1;
492*4882a593Smuzhiyun }
493*4882a593Smuzhiyun
494*4882a593Smuzhiyun static struct Qdisc_ops etf_qdisc_ops __read_mostly = {
495*4882a593Smuzhiyun .id = "etf",
496*4882a593Smuzhiyun .priv_size = sizeof(struct etf_sched_data),
497*4882a593Smuzhiyun .enqueue = etf_enqueue_timesortedlist,
498*4882a593Smuzhiyun .dequeue = etf_dequeue_timesortedlist,
499*4882a593Smuzhiyun .peek = etf_peek_timesortedlist,
500*4882a593Smuzhiyun .init = etf_init,
501*4882a593Smuzhiyun .reset = etf_reset,
502*4882a593Smuzhiyun .destroy = etf_destroy,
503*4882a593Smuzhiyun .dump = etf_dump,
504*4882a593Smuzhiyun .owner = THIS_MODULE,
505*4882a593Smuzhiyun };
506*4882a593Smuzhiyun
etf_module_init(void)507*4882a593Smuzhiyun static int __init etf_module_init(void)
508*4882a593Smuzhiyun {
509*4882a593Smuzhiyun return register_qdisc(&etf_qdisc_ops);
510*4882a593Smuzhiyun }
511*4882a593Smuzhiyun
etf_module_exit(void)512*4882a593Smuzhiyun static void __exit etf_module_exit(void)
513*4882a593Smuzhiyun {
514*4882a593Smuzhiyun unregister_qdisc(&etf_qdisc_ops);
515*4882a593Smuzhiyun }
516*4882a593Smuzhiyun module_init(etf_module_init)
517*4882a593Smuzhiyun module_exit(etf_module_exit)
518*4882a593Smuzhiyun MODULE_LICENSE("GPL");
519