Blame view

net/sched/sch_etf.c 11.7 KB
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
1
2
3
4
5
6
7
8
9
10
11
12
13
  // SPDX-License-Identifier: GPL-2.0
  
  /* net/sched/sch_etf.c  Earliest TxTime First queueing discipline.
   *
   * Authors:	Jesus Sanchez-Palencia <jesus.sanchez-palencia@intel.com>
   *		Vinicius Costa Gomes <vinicius.gomes@intel.com>
   */
  
  #include <linux/module.h>
  #include <linux/types.h>
  #include <linux/kernel.h>
  #include <linux/string.h>
  #include <linux/errno.h>
4b15c7075   Jesus Sanchez-Palencia   net/sched: Make e...
14
  #include <linux/errqueue.h>
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
15
16
17
18
19
20
21
22
23
  #include <linux/rbtree.h>
  #include <linux/skbuff.h>
  #include <linux/posix-timers.h>
  #include <net/netlink.h>
  #include <net/sch_generic.h>
  #include <net/pkt_sched.h>
  #include <net/sock.h>
  
  #define DEADLINE_MODE_IS_ON(x) ((x)->flags & TC_ETF_DEADLINE_MODE_ON)
88cab7716   Jesus Sanchez-Palencia   net/sched: Add HW...
24
  #define OFFLOAD_IS_ON(x) ((x)->flags & TC_ETF_OFFLOAD_ON)
d14d2b206   Vedang Patel   etf: Add skip_soc...
25
  #define SKIP_SOCK_CHECK_IS_SET(x) ((x)->flags & TC_ETF_SKIP_SOCK_CHECK)
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
26
27
  
  struct etf_sched_data {
88cab7716   Jesus Sanchez-Palencia   net/sched: Add HW...
28
  	bool offload;
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
29
  	bool deadline_mode;
d14d2b206   Vedang Patel   etf: Add skip_soc...
30
  	bool skip_sock_check;
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
31
32
33
34
  	int clockid;
  	int queue;
  	s32 delta; /* in ns */
  	ktime_t last; /* The txtime of the last skb sent to the netdevice. */
09fd4860e   Jesus Sanchez-Palencia   etf: Use cached r...
35
  	struct rb_root_cached head;
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
  	struct qdisc_watchdog watchdog;
  	ktime_t (*get_time)(void);
  };
  
  static const struct nla_policy etf_policy[TCA_ETF_MAX + 1] = {
  	[TCA_ETF_PARMS]	= { .len = sizeof(struct tc_etf_qopt) },
  };
  
  static inline int validate_input_params(struct tc_etf_qopt *qopt,
  					struct netlink_ext_ack *extack)
  {
  	/* Check if params comply to the following rules:
  	 *	* Clockid and delta must be valid.
  	 *
  	 *	* Dynamic clockids are not supported.
  	 *
  	 *	* Delta must be a positive integer.
88cab7716   Jesus Sanchez-Palencia   net/sched: Add HW...
53
54
55
  	 *
  	 * Also note that for the HW offload case, we must
  	 * expect that system clocks have been synchronized to PHC.
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
  	 */
  	if (qopt->clockid < 0) {
  		NL_SET_ERR_MSG(extack, "Dynamic clockids are not supported");
  		return -ENOTSUPP;
  	}
  
  	if (qopt->clockid != CLOCK_TAI) {
  		NL_SET_ERR_MSG(extack, "Invalid clockid. CLOCK_TAI must be used");
  		return -EINVAL;
  	}
  
  	if (qopt->delta < 0) {
  		NL_SET_ERR_MSG(extack, "Delta must be positive");
  		return -EINVAL;
  	}
  
  	return 0;
  }
  
  static bool is_packet_valid(struct Qdisc *sch, struct sk_buff *nskb)
  {
  	struct etf_sched_data *q = qdisc_priv(sch);
  	ktime_t txtime = nskb->tstamp;
  	struct sock *sk = nskb->sk;
  	ktime_t now;
d14d2b206   Vedang Patel   etf: Add skip_soc...
81
82
  	if (q->skip_sock_check)
  		goto skip;
a1211bf9a   Eric Dumazet   sched: etf: do no...
83
  	if (!sk || !sk_fullsock(sk))
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
84
85
86
87
88
89
90
91
92
93
94
95
96
  		return false;
  
  	if (!sock_flag(sk, SOCK_TXTIME))
  		return false;
  
  	/* We don't perform crosstimestamping.
  	 * Drop if packet's clockid differs from qdisc's.
  	 */
  	if (sk->sk_clockid != q->clockid)
  		return false;
  
  	if (sk->sk_txtime_deadline_mode != q->deadline_mode)
  		return false;
d14d2b206   Vedang Patel   etf: Add skip_soc...
97
  skip:
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
98
99
100
101
102
103
104
105
106
107
108
  	now = q->get_time();
  	if (ktime_before(txtime, now) || ktime_before(txtime, q->last))
  		return false;
  
  	return true;
  }
  
  static struct sk_buff *etf_peek_timesortedlist(struct Qdisc *sch)
  {
  	struct etf_sched_data *q = qdisc_priv(sch);
  	struct rb_node *p;
09fd4860e   Jesus Sanchez-Palencia   etf: Use cached r...
109
  	p = rb_first_cached(&q->head);
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
110
111
112
113
114
115
116
117
118
119
120
  	if (!p)
  		return NULL;
  
  	return rb_to_skb(p);
  }
  
  static void reset_watchdog(struct Qdisc *sch)
  {
  	struct etf_sched_data *q = qdisc_priv(sch);
  	struct sk_buff *skb = etf_peek_timesortedlist(sch);
  	ktime_t next;
3fcbdaee3   Jesus Sanchez-Palencia   etf: Cancel timer...
121
122
  	if (!skb) {
  		qdisc_watchdog_cancel(&q->watchdog);
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
123
  		return;
3fcbdaee3   Jesus Sanchez-Palencia   etf: Cancel timer...
124
  	}
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
125
126
127
128
  
  	next = ktime_sub_ns(skb->tstamp, q->delta);
  	qdisc_watchdog_schedule_ns(&q->watchdog, ktime_to_ns(next));
  }
4b15c7075   Jesus Sanchez-Palencia   net/sched: Make e...
129
130
131
132
133
  static void report_sock_error(struct sk_buff *skb, u32 err, u8 code)
  {
  	struct sock_exterr_skb *serr;
  	struct sk_buff *clone;
  	ktime_t txtime = skb->tstamp;
a1211bf9a   Eric Dumazet   sched: etf: do no...
134
  	struct sock *sk = skb->sk;
4b15c7075   Jesus Sanchez-Palencia   net/sched: Make e...
135

a1211bf9a   Eric Dumazet   sched: etf: do no...
136
  	if (!sk || !sk_fullsock(sk) || !(sk->sk_txtime_report_errors))
4b15c7075   Jesus Sanchez-Palencia   net/sched: Make e...
137
138
139
140
141
142
143
144
145
146
147
148
149
150
  		return;
  
  	clone = skb_clone(skb, GFP_ATOMIC);
  	if (!clone)
  		return;
  
  	serr = SKB_EXT_ERR(clone);
  	serr->ee.ee_errno = err;
  	serr->ee.ee_origin = SO_EE_ORIGIN_TXTIME;
  	serr->ee.ee_type = 0;
  	serr->ee.ee_code = code;
  	serr->ee.ee_pad = 0;
  	serr->ee.ee_data = (txtime >> 32); /* high part of tstamp */
  	serr->ee.ee_info = txtime; /* low part of tstamp */
a1211bf9a   Eric Dumazet   sched: etf: do no...
151
  	if (sock_queue_err_skb(sk, clone))
4b15c7075   Jesus Sanchez-Palencia   net/sched: Make e...
152
153
  		kfree_skb(clone);
  }
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
154
155
156
157
  static int etf_enqueue_timesortedlist(struct sk_buff *nskb, struct Qdisc *sch,
  				      struct sk_buff **to_free)
  {
  	struct etf_sched_data *q = qdisc_priv(sch);
09fd4860e   Jesus Sanchez-Palencia   etf: Use cached r...
158
  	struct rb_node **p = &q->head.rb_root.rb_node, *parent = NULL;
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
159
  	ktime_t txtime = nskb->tstamp;
09fd4860e   Jesus Sanchez-Palencia   etf: Use cached r...
160
  	bool leftmost = true;
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
161

4b15c7075   Jesus Sanchez-Palencia   net/sched: Make e...
162
163
164
  	if (!is_packet_valid(sch, nskb)) {
  		report_sock_error(nskb, EINVAL,
  				  SO_EE_CODE_TXTIME_INVALID_PARAM);
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
165
  		return qdisc_drop(nskb, sch, to_free);
4b15c7075   Jesus Sanchez-Palencia   net/sched: Make e...
166
  	}
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
167
168
169
170
171
172
  
  	while (*p) {
  		struct sk_buff *skb;
  
  		parent = *p;
  		skb = rb_to_skb(parent);
28aa7c86c   Vinicius Costa Gomes   sched: etf: Fix o...
173
  		if (ktime_compare(txtime, skb->tstamp) >= 0) {
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
174
  			p = &parent->rb_right;
09fd4860e   Jesus Sanchez-Palencia   etf: Use cached r...
175
176
  			leftmost = false;
  		} else {
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
177
  			p = &parent->rb_left;
09fd4860e   Jesus Sanchez-Palencia   etf: Use cached r...
178
  		}
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
179
180
  	}
  	rb_link_node(&nskb->rbnode, parent, p);
09fd4860e   Jesus Sanchez-Palencia   etf: Use cached r...
181
  	rb_insert_color_cached(&nskb->rbnode, &q->head, leftmost);
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
182
183
184
185
186
187
188
189
190
  
  	qdisc_qstats_backlog_inc(sch, nskb);
  	sch->q.qlen++;
  
  	/* Now we may need to re-arm the qdisc watchdog for the next packet. */
  	reset_watchdog(sch);
  
  	return NET_XMIT_SUCCESS;
  }
37342bdaf   Jesus Sanchez-Palencia   etf: Drop all exp...
191
192
  static void timesortedlist_drop(struct Qdisc *sch, struct sk_buff *skb,
  				ktime_t now)
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
193
194
  {
  	struct etf_sched_data *q = qdisc_priv(sch);
cbeeb8efe   Jesus Sanchez-Palencia   etf: Split timers...
195
  	struct sk_buff *to_free = NULL;
37342bdaf   Jesus Sanchez-Palencia   etf: Drop all exp...
196
  	struct sk_buff *tmp = NULL;
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
197

37342bdaf   Jesus Sanchez-Palencia   etf: Drop all exp...
198
199
200
  	skb_rbtree_walk_from_safe(skb, tmp) {
  		if (ktime_after(skb->tstamp, now))
  			break;
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
201

37342bdaf   Jesus Sanchez-Palencia   etf: Drop all exp...
202
  		rb_erase_cached(&skb->rbnode, &q->head);
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
203

37342bdaf   Jesus Sanchez-Palencia   etf: Drop all exp...
204
205
206
207
208
209
  		/* The rbnode field in the skb re-uses these fields, now that
  		 * we are done with the rbnode, reset them.
  		 */
  		skb->next = NULL;
  		skb->prev = NULL;
  		skb->dev = qdisc_dev(sch);
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
210

37342bdaf   Jesus Sanchez-Palencia   etf: Drop all exp...
211
  		report_sock_error(skb, ECANCELED, SO_EE_CODE_TXTIME_MISSED);
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
212

37342bdaf   Jesus Sanchez-Palencia   etf: Drop all exp...
213
214
215
216
217
  		qdisc_qstats_backlog_dec(sch, skb);
  		qdisc_drop(skb, sch, &to_free);
  		qdisc_qstats_overlimit(sch);
  		sch->q.qlen--;
  	}
4b15c7075   Jesus Sanchez-Palencia   net/sched: Make e...
218

37342bdaf   Jesus Sanchez-Palencia   etf: Drop all exp...
219
  	kfree_skb_list(to_free);
cbeeb8efe   Jesus Sanchez-Palencia   etf: Split timers...
220
  }
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
221

cbeeb8efe   Jesus Sanchez-Palencia   etf: Split timers...
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
  static void timesortedlist_remove(struct Qdisc *sch, struct sk_buff *skb)
  {
  	struct etf_sched_data *q = qdisc_priv(sch);
  
  	rb_erase_cached(&skb->rbnode, &q->head);
  
  	/* The rbnode field in the skb re-uses these fields, now that
  	 * we are done with the rbnode, reset them.
  	 */
  	skb->next = NULL;
  	skb->prev = NULL;
  	skb->dev = qdisc_dev(sch);
  
  	qdisc_qstats_backlog_dec(sch, skb);
  
  	qdisc_bstats_update(sch, skb);
  
  	q->last = skb->tstamp;
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
  
  	sch->q.qlen--;
  }
  
  static struct sk_buff *etf_dequeue_timesortedlist(struct Qdisc *sch)
  {
  	struct etf_sched_data *q = qdisc_priv(sch);
  	struct sk_buff *skb;
  	ktime_t now, next;
  
  	skb = etf_peek_timesortedlist(sch);
  	if (!skb)
  		return NULL;
  
  	now = q->get_time();
  
  	/* Drop if packet has expired while in queue. */
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
257
  	if (ktime_before(skb->tstamp, now)) {
37342bdaf   Jesus Sanchez-Palencia   etf: Drop all exp...
258
  		timesortedlist_drop(sch, skb, now);
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
259
260
261
262
263
264
265
266
  		skb = NULL;
  		goto out;
  	}
  
  	/* When in deadline mode, dequeue as soon as possible and change the
  	 * txtime from deadline to (now + delta).
  	 */
  	if (q->deadline_mode) {
cbeeb8efe   Jesus Sanchez-Palencia   etf: Split timers...
267
  		timesortedlist_remove(sch, skb);
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
268
269
270
271
272
273
274
275
  		skb->tstamp = now;
  		goto out;
  	}
  
  	next = ktime_sub_ns(skb->tstamp, q->delta);
  
  	/* Dequeue only if now is within the [txtime - delta, txtime] range. */
  	if (ktime_after(now, next))
cbeeb8efe   Jesus Sanchez-Palencia   etf: Split timers...
276
  		timesortedlist_remove(sch, skb);
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
277
278
279
280
281
282
283
284
285
  	else
  		skb = NULL;
  
  out:
  	/* Now we may need to re-arm the qdisc watchdog for the next packet. */
  	reset_watchdog(sch);
  
  	return skb;
  }
88cab7716   Jesus Sanchez-Palencia   net/sched: Add HW...
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
  static void etf_disable_offload(struct net_device *dev,
  				struct etf_sched_data *q)
  {
  	struct tc_etf_qopt_offload etf = { };
  	const struct net_device_ops *ops;
  	int err;
  
  	if (!q->offload)
  		return;
  
  	ops = dev->netdev_ops;
  	if (!ops->ndo_setup_tc)
  		return;
  
  	etf.queue = q->queue;
  	etf.enable = 0;
  
  	err = ops->ndo_setup_tc(dev, TC_SETUP_QDISC_ETF, &etf);
  	if (err < 0)
  		pr_warn("Couldn't disable ETF offload for queue %d
  ",
  			etf.queue);
  }
  
  static int etf_enable_offload(struct net_device *dev, struct etf_sched_data *q,
  			      struct netlink_ext_ack *extack)
  {
  	const struct net_device_ops *ops = dev->netdev_ops;
  	struct tc_etf_qopt_offload etf = { };
  	int err;
  
  	if (q->offload)
  		return 0;
  
  	if (!ops->ndo_setup_tc) {
  		NL_SET_ERR_MSG(extack, "Specified device does not support ETF offload");
  		return -EOPNOTSUPP;
  	}
  
  	etf.queue = q->queue;
  	etf.enable = 1;
  
  	err = ops->ndo_setup_tc(dev, TC_SETUP_QDISC_ETF, &etf);
  	if (err < 0) {
  		NL_SET_ERR_MSG(extack, "Specified device failed to setup ETF hardware offload");
  		return err;
  	}
  
  	return 0;
  }
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
336
337
338
339
340
341
342
343
344
345
346
347
348
349
  static int etf_init(struct Qdisc *sch, struct nlattr *opt,
  		    struct netlink_ext_ack *extack)
  {
  	struct etf_sched_data *q = qdisc_priv(sch);
  	struct net_device *dev = qdisc_dev(sch);
  	struct nlattr *tb[TCA_ETF_MAX + 1];
  	struct tc_etf_qopt *qopt;
  	int err;
  
  	if (!opt) {
  		NL_SET_ERR_MSG(extack,
  			       "Missing ETF qdisc options which are mandatory");
  		return -EINVAL;
  	}
8cb081746   Johannes Berg   netlink: make val...
350
351
  	err = nla_parse_nested_deprecated(tb, TCA_ETF_MAX, opt, etf_policy,
  					  extack);
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
352
353
354
355
356
357
358
359
360
  	if (err < 0)
  		return err;
  
  	if (!tb[TCA_ETF_PARMS]) {
  		NL_SET_ERR_MSG(extack, "Missing mandatory ETF parameters");
  		return -EINVAL;
  	}
  
  	qopt = nla_data(tb[TCA_ETF_PARMS]);
88cab7716   Jesus Sanchez-Palencia   net/sched: Add HW...
361
362
  	pr_debug("delta %d clockid %d offload %s deadline %s
  ",
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
363
  		 qopt->delta, qopt->clockid,
88cab7716   Jesus Sanchez-Palencia   net/sched: Add HW...
364
  		 OFFLOAD_IS_ON(qopt) ? "on" : "off",
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
365
366
367
368
369
370
371
  		 DEADLINE_MODE_IS_ON(qopt) ? "on" : "off");
  
  	err = validate_input_params(qopt, extack);
  	if (err < 0)
  		return err;
  
  	q->queue = sch->dev_queue - netdev_get_tx_queue(dev, 0);
88cab7716   Jesus Sanchez-Palencia   net/sched: Add HW...
372
373
374
375
376
  	if (OFFLOAD_IS_ON(qopt)) {
  		err = etf_enable_offload(dev, q, extack);
  		if (err < 0)
  			return err;
  	}
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
377
378
379
  	/* Everything went OK, save the parameters used. */
  	q->delta = qopt->delta;
  	q->clockid = qopt->clockid;
88cab7716   Jesus Sanchez-Palencia   net/sched: Add HW...
380
  	q->offload = OFFLOAD_IS_ON(qopt);
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
381
  	q->deadline_mode = DEADLINE_MODE_IS_ON(qopt);
d14d2b206   Vedang Patel   etf: Add skip_soc...
382
  	q->skip_sock_check = SKIP_SOCK_CHECK_IS_SET(qopt);
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
  
  	switch (q->clockid) {
  	case CLOCK_REALTIME:
  		q->get_time = ktime_get_real;
  		break;
  	case CLOCK_MONOTONIC:
  		q->get_time = ktime_get;
  		break;
  	case CLOCK_BOOTTIME:
  		q->get_time = ktime_get_boottime;
  		break;
  	case CLOCK_TAI:
  		q->get_time = ktime_get_clocktai;
  		break;
  	default:
  		NL_SET_ERR_MSG(extack, "Clockid is not supported");
  		return -ENOTSUPP;
  	}
  
  	qdisc_watchdog_init_clockid(&q->watchdog, sch, q->clockid);
  
  	return 0;
  }
  
  static void timesortedlist_clear(struct Qdisc *sch)
  {
  	struct etf_sched_data *q = qdisc_priv(sch);
09fd4860e   Jesus Sanchez-Palencia   etf: Use cached r...
410
  	struct rb_node *p = rb_first_cached(&q->head);
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
411
412
413
414
415
  
  	while (p) {
  		struct sk_buff *skb = rb_to_skb(p);
  
  		p = rb_next(p);
09fd4860e   Jesus Sanchez-Palencia   etf: Use cached r...
416
  		rb_erase_cached(&skb->rbnode, &q->head);
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
  		rtnl_kfree_skbs(skb, skb);
  		sch->q.qlen--;
  	}
  }
  
  static void etf_reset(struct Qdisc *sch)
  {
  	struct etf_sched_data *q = qdisc_priv(sch);
  
  	/* Only cancel watchdog if it's been initialized. */
  	if (q->watchdog.qdisc == sch)
  		qdisc_watchdog_cancel(&q->watchdog);
  
  	/* No matter which mode we are on, it's safe to clear both lists. */
  	timesortedlist_clear(sch);
  	__qdisc_reset_queue(&sch->q);
  
  	sch->qstats.backlog = 0;
  	sch->q.qlen = 0;
  
  	q->last = 0;
  }
  
  static void etf_destroy(struct Qdisc *sch)
  {
  	struct etf_sched_data *q = qdisc_priv(sch);
88cab7716   Jesus Sanchez-Palencia   net/sched: Add HW...
443
  	struct net_device *dev = qdisc_dev(sch);
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
444
445
446
447
  
  	/* Only cancel watchdog if it's been initialized. */
  	if (q->watchdog.qdisc == sch)
  		qdisc_watchdog_cancel(&q->watchdog);
88cab7716   Jesus Sanchez-Palencia   net/sched: Add HW...
448
449
  
  	etf_disable_offload(dev, q);
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
450
451
452
453
454
455
456
  }
  
  static int etf_dump(struct Qdisc *sch, struct sk_buff *skb)
  {
  	struct etf_sched_data *q = qdisc_priv(sch);
  	struct tc_etf_qopt opt = { };
  	struct nlattr *nest;
ae0be8de9   Michal Kubecek   netlink: make nla...
457
  	nest = nla_nest_start_noflag(skb, TCA_OPTIONS);
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
458
459
460
461
462
  	if (!nest)
  		goto nla_put_failure;
  
  	opt.delta = q->delta;
  	opt.clockid = q->clockid;
88cab7716   Jesus Sanchez-Palencia   net/sched: Add HW...
463
464
  	if (q->offload)
  		opt.flags |= TC_ETF_OFFLOAD_ON;
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
465
466
  	if (q->deadline_mode)
  		opt.flags |= TC_ETF_DEADLINE_MODE_ON;
d14d2b206   Vedang Patel   etf: Add skip_soc...
467
468
  	if (q->skip_sock_check)
  		opt.flags |= TC_ETF_SKIP_SOCK_CHECK;
25db26a91   Vinicius Costa Gomes   net/sched: Introd...
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
  	if (nla_put(skb, TCA_ETF_PARMS, sizeof(opt), &opt))
  		goto nla_put_failure;
  
  	return nla_nest_end(skb, nest);
  
  nla_put_failure:
  	nla_nest_cancel(skb, nest);
  	return -1;
  }
  
  static struct Qdisc_ops etf_qdisc_ops __read_mostly = {
  	.id		=	"etf",
  	.priv_size	=	sizeof(struct etf_sched_data),
  	.enqueue	=	etf_enqueue_timesortedlist,
  	.dequeue	=	etf_dequeue_timesortedlist,
  	.peek		=	etf_peek_timesortedlist,
  	.init		=	etf_init,
  	.reset		=	etf_reset,
  	.destroy	=	etf_destroy,
  	.dump		=	etf_dump,
  	.owner		=	THIS_MODULE,
  };
  
  static int __init etf_module_init(void)
  {
  	return register_qdisc(&etf_qdisc_ops);
  }
  
  static void __exit etf_module_exit(void)
  {
  	unregister_qdisc(&etf_qdisc_ops);
  }
  module_init(etf_module_init)
  module_exit(etf_module_exit)
  MODULE_LICENSE("GPL");