1 /*
2 * lib/route/qdisc/htb.c HTB Qdisc
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation version 2.1
7 * of the License.
8 *
9 * Copyright (c) 2003-2011 Thomas Graf <tgraf@suug.ch>
10 * Copyright (c) 2005-2006 Petr Gotthard <petr.gotthard@siemens.com>
11 * Copyright (c) 2005-2006 Siemens AG Oesterreich
12 */
13
14 /**
15 * @ingroup qdisc
16 * @ingroup class
17 * @defgroup qdisc_htb Hierachical Token Bucket (HTB)
18 * @{
19 */
20
21 #include <netlink-private/netlink.h>
22 #include <netlink-private/tc.h>
23 #include <netlink/netlink.h>
24 #include <netlink/cache.h>
25 #include <netlink/utils.h>
26 #include <netlink-private/route/tc-api.h>
27 #include <netlink/route/qdisc.h>
28 #include <netlink/route/class.h>
29 #include <netlink/route/link.h>
30 #include <netlink/route/qdisc/htb.h>
31
32 /** @cond SKIP */
33 #define SCH_HTB_HAS_RATE2QUANTUM 0x01
34 #define SCH_HTB_HAS_DEFCLS 0x02
35
36 #define SCH_HTB_HAS_PRIO 0x001
37 #define SCH_HTB_HAS_RATE 0x002
38 #define SCH_HTB_HAS_CEIL 0x004
39 #define SCH_HTB_HAS_RBUFFER 0x008
40 #define SCH_HTB_HAS_CBUFFER 0x010
41 #define SCH_HTB_HAS_QUANTUM 0x020
42 #define SCH_HTB_HAS_LEVEL 0x040
43 /** @endcond */
44
45 static struct nla_policy htb_policy[TCA_HTB_MAX+1] = {
46 [TCA_HTB_INIT] = { .minlen = sizeof(struct tc_htb_glob) },
47 [TCA_HTB_PARMS] = { .minlen = sizeof(struct tc_htb_opt) },
48 };
49
htb_qdisc_msg_parser(struct rtnl_tc * tc,void * data)50 static int htb_qdisc_msg_parser(struct rtnl_tc *tc, void *data)
51 {
52 struct nlattr *tb[TCA_HTB_MAX + 1];
53 struct rtnl_htb_qdisc *htb = data;
54 int err;
55
56 if ((err = tca_parse(tb, TCA_HTB_MAX, tc, htb_policy)) < 0)
57 return err;
58
59 if (tb[TCA_HTB_INIT]) {
60 struct tc_htb_glob opts;
61
62 nla_memcpy(&opts, tb[TCA_HTB_INIT], sizeof(opts));
63 htb->qh_rate2quantum = opts.rate2quantum;
64 htb->qh_defcls = opts.defcls;
65 htb->qh_direct_pkts = opts.direct_pkts;
66
67 htb->qh_mask = (SCH_HTB_HAS_RATE2QUANTUM | SCH_HTB_HAS_DEFCLS);
68 }
69
70 return 0;
71 }
72
htb_class_msg_parser(struct rtnl_tc * tc,void * data)73 static int htb_class_msg_parser(struct rtnl_tc *tc, void *data)
74 {
75 struct nlattr *tb[TCA_HTB_MAX + 1];
76 struct rtnl_htb_class *htb = data;
77 int err;
78
79 if ((err = tca_parse(tb, TCA_HTB_MAX, tc, htb_policy)) < 0)
80 return err;
81
82 if (tb[TCA_HTB_PARMS]) {
83 struct tc_htb_opt opts;
84
85 nla_memcpy(&opts, tb[TCA_HTB_PARMS], sizeof(opts));
86 htb->ch_prio = opts.prio;
87 rtnl_copy_ratespec(&htb->ch_rate, &opts.rate);
88 rtnl_copy_ratespec(&htb->ch_ceil, &opts.ceil);
89 htb->ch_rbuffer = rtnl_tc_calc_bufsize(nl_ticks2us(opts.buffer),
90 opts.rate.rate);
91 htb->ch_cbuffer = rtnl_tc_calc_bufsize(nl_ticks2us(opts.cbuffer),
92 opts.ceil.rate);
93 htb->ch_quantum = opts.quantum;
94 htb->ch_level = opts.level;
95
96 rtnl_tc_set_mpu(tc, htb->ch_rate.rs_mpu);
97 rtnl_tc_set_overhead(tc, htb->ch_rate.rs_overhead);
98
99 htb->ch_mask = (SCH_HTB_HAS_PRIO | SCH_HTB_HAS_RATE |
100 SCH_HTB_HAS_CEIL | SCH_HTB_HAS_RBUFFER |
101 SCH_HTB_HAS_CBUFFER | SCH_HTB_HAS_QUANTUM |
102 SCH_HTB_HAS_LEVEL);
103 }
104
105 return 0;
106 }
107
htb_qdisc_dump_line(struct rtnl_tc * tc,void * data,struct nl_dump_params * p)108 static void htb_qdisc_dump_line(struct rtnl_tc *tc, void *data,
109 struct nl_dump_params *p)
110 {
111 struct rtnl_htb_qdisc *htb = data;
112
113 if (!htb)
114 return;
115
116 if (htb->qh_mask & SCH_HTB_HAS_RATE2QUANTUM)
117 nl_dump(p, " r2q %u", htb->qh_rate2quantum);
118
119 if (htb->qh_mask & SCH_HTB_HAS_DEFCLS) {
120 char buf[64];
121 nl_dump(p, " default-class %s",
122 rtnl_tc_handle2str(htb->qh_defcls, buf, sizeof(buf)));
123 }
124 }
125
htb_class_dump_line(struct rtnl_tc * tc,void * data,struct nl_dump_params * p)126 static void htb_class_dump_line(struct rtnl_tc *tc, void *data,
127 struct nl_dump_params *p)
128 {
129 struct rtnl_htb_class *htb = data;
130
131 if (!htb)
132 return;
133
134 if (htb->ch_mask & SCH_HTB_HAS_RATE) {
135 double r, rbit;
136 char *ru, *rubit;
137
138 r = nl_cancel_down_bytes(htb->ch_rate.rs_rate, &ru);
139 rbit = nl_cancel_down_bits(htb->ch_rate.rs_rate*8, &rubit);
140
141 nl_dump(p, " rate %.2f%s/s (%.0f%s) log %u",
142 r, ru, rbit, rubit, 1<<htb->ch_rate.rs_cell_log);
143 }
144 }
145
htb_class_dump_details(struct rtnl_tc * tc,void * data,struct nl_dump_params * p)146 static void htb_class_dump_details(struct rtnl_tc *tc, void *data,
147 struct nl_dump_params *p)
148 {
149 struct rtnl_htb_class *htb = data;
150
151 if (!htb)
152 return;
153
154 /* line 1 */
155 if (htb->ch_mask & SCH_HTB_HAS_CEIL) {
156 double r, rbit;
157 char *ru, *rubit;
158
159 r = nl_cancel_down_bytes(htb->ch_ceil.rs_rate, &ru);
160 rbit = nl_cancel_down_bits(htb->ch_ceil.rs_rate*8, &rubit);
161
162 nl_dump(p, " ceil %.2f%s/s (%.0f%s) log %u",
163 r, ru, rbit, rubit, 1<<htb->ch_ceil.rs_cell_log);
164 }
165
166 if (htb->ch_mask & SCH_HTB_HAS_PRIO)
167 nl_dump(p, " prio %u", htb->ch_prio);
168
169 if (htb->ch_mask & SCH_HTB_HAS_RBUFFER) {
170 double b;
171 char *bu;
172
173 b = nl_cancel_down_bytes(htb->ch_rbuffer, &bu);
174 nl_dump(p, " rbuffer %.2f%s", b, bu);
175 }
176
177 if (htb->ch_mask & SCH_HTB_HAS_CBUFFER) {
178 double b;
179 char *bu;
180
181 b = nl_cancel_down_bytes(htb->ch_cbuffer, &bu);
182 nl_dump(p, " cbuffer %.2f%s", b, bu);
183 }
184
185 if (htb->ch_mask & SCH_HTB_HAS_QUANTUM)
186 nl_dump(p, " quantum %u", htb->ch_quantum);
187 }
188
htb_qdisc_msg_fill(struct rtnl_tc * tc,void * data,struct nl_msg * msg)189 static int htb_qdisc_msg_fill(struct rtnl_tc *tc, void *data,
190 struct nl_msg *msg)
191 {
192 struct rtnl_htb_qdisc *htb = data;
193 struct tc_htb_glob opts = {
194 .version = TC_HTB_PROTOVER,
195 .rate2quantum = 10,
196 };
197
198 if (htb) {
199 if (htb->qh_mask & SCH_HTB_HAS_RATE2QUANTUM)
200 opts.rate2quantum = htb->qh_rate2quantum;
201
202 if (htb->qh_mask & SCH_HTB_HAS_DEFCLS)
203 opts.defcls = htb->qh_defcls;
204 }
205
206 return nla_put(msg, TCA_HTB_INIT, sizeof(opts), &opts);
207 }
208
htb_class_msg_fill(struct rtnl_tc * tc,void * data,struct nl_msg * msg)209 static int htb_class_msg_fill(struct rtnl_tc *tc, void *data,
210 struct nl_msg *msg)
211 {
212 struct rtnl_htb_class *htb = data;
213 uint32_t mtu, rtable[RTNL_TC_RTABLE_SIZE], ctable[RTNL_TC_RTABLE_SIZE];
214 struct tc_htb_opt opts;
215 int buffer, cbuffer;
216
217 if (!htb || !(htb->ch_mask & SCH_HTB_HAS_RATE))
218 BUG();
219
220 memset(&opts, 0, sizeof(opts));
221
222 /* if not set, zero (0) is used as priority */
223 if (htb->ch_mask & SCH_HTB_HAS_PRIO)
224 opts.prio = htb->ch_prio;
225
226 mtu = rtnl_tc_get_mtu(tc);
227
228 rtnl_tc_build_rate_table(tc, &htb->ch_rate, rtable);
229 rtnl_rcopy_ratespec(&opts.rate, &htb->ch_rate);
230
231 if (htb->ch_mask & SCH_HTB_HAS_CEIL) {
232 rtnl_tc_build_rate_table(tc, &htb->ch_ceil, ctable);
233 rtnl_rcopy_ratespec(&opts.ceil, &htb->ch_ceil);
234 } else {
235 /*
236 * If not set, configured rate is used as ceil, which implies
237 * no borrowing.
238 */
239 memcpy(&opts.ceil, &opts.rate, sizeof(struct tc_ratespec));
240 }
241
242 if (htb->ch_mask & SCH_HTB_HAS_RBUFFER)
243 buffer = htb->ch_rbuffer;
244 else
245 buffer = opts.rate.rate / nl_get_psched_hz() + mtu; /* XXX */
246
247 opts.buffer = nl_us2ticks(rtnl_tc_calc_txtime(buffer, opts.rate.rate));
248
249 if (htb->ch_mask & SCH_HTB_HAS_CBUFFER)
250 cbuffer = htb->ch_cbuffer;
251 else
252 cbuffer = opts.ceil.rate / nl_get_psched_hz() + mtu; /* XXX */
253
254 opts.cbuffer = nl_us2ticks(rtnl_tc_calc_txtime(cbuffer, opts.ceil.rate));
255
256 if (htb->ch_mask & SCH_HTB_HAS_QUANTUM)
257 opts.quantum = htb->ch_quantum;
258
259 NLA_PUT(msg, TCA_HTB_PARMS, sizeof(opts), &opts);
260 NLA_PUT(msg, TCA_HTB_RTAB, sizeof(rtable), &rtable);
261 NLA_PUT(msg, TCA_HTB_CTAB, sizeof(ctable), &ctable);
262
263 return 0;
264
265 nla_put_failure:
266 return -NLE_MSGSIZE;
267 }
268
269 static struct rtnl_tc_ops htb_qdisc_ops;
270 static struct rtnl_tc_ops htb_class_ops;
271
htb_qdisc_data(struct rtnl_qdisc * qdisc)272 static struct rtnl_htb_qdisc *htb_qdisc_data(struct rtnl_qdisc *qdisc)
273 {
274 return rtnl_tc_data_check(TC_CAST(qdisc), &htb_qdisc_ops);
275 }
276
htb_class_data(struct rtnl_class * class)277 static struct rtnl_htb_class *htb_class_data(struct rtnl_class *class)
278 {
279 return rtnl_tc_data_check(TC_CAST(class), &htb_class_ops);
280 }
281
282 /**
283 * @name Attribute Modifications
284 * @{
285 */
286
287 /**
288 * Return rate/quantum ratio of HTB qdisc
289 * @arg qdisc htb qdisc object
290 *
291 * @return rate/quantum ratio or 0 if unspecified
292 */
rtnl_htb_get_rate2quantum(struct rtnl_qdisc * qdisc)293 uint32_t rtnl_htb_get_rate2quantum(struct rtnl_qdisc *qdisc)
294 {
295 struct rtnl_htb_qdisc *htb;
296
297 if ((htb = htb_qdisc_data(qdisc)) &&
298 htb->qh_mask & SCH_HTB_HAS_RATE2QUANTUM)
299 return htb->qh_rate2quantum;
300
301 return 0;
302 }
303
rtnl_htb_set_rate2quantum(struct rtnl_qdisc * qdisc,uint32_t rate2quantum)304 int rtnl_htb_set_rate2quantum(struct rtnl_qdisc *qdisc, uint32_t rate2quantum)
305 {
306 struct rtnl_htb_qdisc *htb;
307
308 if (!(htb = htb_qdisc_data(qdisc)))
309 return -NLE_OPNOTSUPP;
310
311 htb->qh_rate2quantum = rate2quantum;
312 htb->qh_mask |= SCH_HTB_HAS_RATE2QUANTUM;
313
314 return 0;
315 }
316
317 /**
318 * Return default class of HTB qdisc
319 * @arg qdisc htb qdisc object
320 *
321 * Returns the classid of the class where all unclassified traffic
322 * goes to.
323 *
324 * @return classid or TC_H_UNSPEC if unspecified.
325 */
rtnl_htb_get_defcls(struct rtnl_qdisc * qdisc)326 uint32_t rtnl_htb_get_defcls(struct rtnl_qdisc *qdisc)
327 {
328 struct rtnl_htb_qdisc *htb;
329
330 if ((htb = htb_qdisc_data(qdisc)) &&
331 htb->qh_mask & SCH_HTB_HAS_DEFCLS)
332 return htb->qh_defcls;
333
334 return TC_H_UNSPEC;
335 }
336
337 /**
338 * Set default class of the htb qdisc to the specified value
339 * @arg qdisc qdisc to change
340 * @arg defcls new default class
341 */
rtnl_htb_set_defcls(struct rtnl_qdisc * qdisc,uint32_t defcls)342 int rtnl_htb_set_defcls(struct rtnl_qdisc *qdisc, uint32_t defcls)
343 {
344 struct rtnl_htb_qdisc *htb;
345
346 if (!(htb = htb_qdisc_data(qdisc)))
347 return -NLE_OPNOTSUPP;
348
349 htb->qh_defcls = defcls;
350 htb->qh_mask |= SCH_HTB_HAS_DEFCLS;
351
352 return 0;
353 }
354
rtnl_htb_get_prio(struct rtnl_class * class)355 uint32_t rtnl_htb_get_prio(struct rtnl_class *class)
356 {
357 struct rtnl_htb_class *htb;
358
359 if ((htb = htb_class_data(class)) && htb->ch_mask & SCH_HTB_HAS_PRIO)
360 return htb->ch_prio;
361
362 return 0;
363 }
364
rtnl_htb_set_prio(struct rtnl_class * class,uint32_t prio)365 int rtnl_htb_set_prio(struct rtnl_class *class, uint32_t prio)
366 {
367 struct rtnl_htb_class *htb;
368
369 if (!(htb = htb_class_data(class)))
370 return -NLE_OPNOTSUPP;
371
372 htb->ch_prio = prio;
373 htb->ch_mask |= SCH_HTB_HAS_PRIO;
374
375 return 0;
376 }
377
378 /**
379 * Return rate of HTB class
380 * @arg class htb class object
381 *
382 * @return Rate in bytes/s or 0 if unspecified.
383 */
rtnl_htb_get_rate(struct rtnl_class * class)384 uint32_t rtnl_htb_get_rate(struct rtnl_class *class)
385 {
386 struct rtnl_htb_class *htb;
387
388 if ((htb = htb_class_data(class)) && htb->ch_mask & SCH_HTB_HAS_RATE)
389 return htb->ch_rate.rs_rate;
390
391 return 0;
392 }
393
394 /**
395 * Set rate of HTB class
396 * @arg class htb class object
397 * @arg rate new rate in bytes per second
398 *
399 * @return 0 on success or a negative error code.
400 */
rtnl_htb_set_rate(struct rtnl_class * class,uint32_t rate)401 int rtnl_htb_set_rate(struct rtnl_class *class, uint32_t rate)
402 {
403 struct rtnl_htb_class *htb;
404
405 if (!(htb = htb_class_data(class)))
406 return -NLE_OPNOTSUPP;
407
408 htb->ch_rate.rs_cell_log = UINT8_MAX; /* use default value */
409 htb->ch_rate.rs_rate = rate;
410 htb->ch_mask |= SCH_HTB_HAS_RATE;
411
412 return 0;
413 }
414
415 /**
416 * Return ceil rate of HTB class
417 * @arg class htb class object
418 *
419 * @return Ceil rate in bytes/s or 0 if unspecified
420 */
rtnl_htb_get_ceil(struct rtnl_class * class)421 uint32_t rtnl_htb_get_ceil(struct rtnl_class *class)
422 {
423 struct rtnl_htb_class *htb;
424
425 if ((htb = htb_class_data(class)) && htb->ch_mask & SCH_HTB_HAS_CEIL)
426 return htb->ch_ceil.rs_rate;
427
428 return 0;
429 }
430
431 /**
432 * Set ceil rate of HTB class
433 * @arg class htb class object
434 * @arg ceil new ceil rate number of bytes per second
435 *
436 * @return 0 on success or a negative error code.
437 */
rtnl_htb_set_ceil(struct rtnl_class * class,uint32_t ceil)438 int rtnl_htb_set_ceil(struct rtnl_class *class, uint32_t ceil)
439 {
440 struct rtnl_htb_class *htb;
441
442 if (!(htb = htb_class_data(class)))
443 return -NLE_OPNOTSUPP;
444
445 htb->ch_ceil.rs_cell_log = UINT8_MAX; /* use default value */
446 htb->ch_ceil.rs_rate = ceil;
447 htb->ch_mask |= SCH_HTB_HAS_CEIL;
448
449 return 0;
450 }
451
452 /**
453 * Return burst buffer size of HTB class
454 * @arg class htb class object
455 *
456 * @return Burst buffer size or 0 if unspecified
457 */
rtnl_htb_get_rbuffer(struct rtnl_class * class)458 uint32_t rtnl_htb_get_rbuffer(struct rtnl_class *class)
459 {
460 struct rtnl_htb_class *htb;
461
462 if ((htb = htb_class_data(class)) &&
463 htb->ch_mask & SCH_HTB_HAS_RBUFFER)
464 return htb->ch_rbuffer;
465
466 return 0;
467 }
468
469 /**
470 * Set size of the rate bucket of HTB class.
471 * @arg class HTB class to be modified.
472 * @arg rbuffer New size in bytes.
473 */
rtnl_htb_set_rbuffer(struct rtnl_class * class,uint32_t rbuffer)474 int rtnl_htb_set_rbuffer(struct rtnl_class *class, uint32_t rbuffer)
475 {
476 struct rtnl_htb_class *htb;
477
478 if (!(htb = htb_class_data(class)))
479 return -NLE_OPNOTSUPP;
480
481 htb->ch_rbuffer = rbuffer;
482 htb->ch_mask |= SCH_HTB_HAS_RBUFFER;
483
484 return 0;
485 }
486
487 /**
488 * Return ceil burst buffer size of HTB class
489 * @arg class htb class object
490 *
491 * @return Ceil burst buffer size or 0 if unspecified
492 */
rtnl_htb_get_cbuffer(struct rtnl_class * class)493 uint32_t rtnl_htb_get_cbuffer(struct rtnl_class *class)
494 {
495 struct rtnl_htb_class *htb;
496
497 if ((htb = htb_class_data(class)) &&
498 htb->ch_mask & SCH_HTB_HAS_CBUFFER)
499 return htb->ch_cbuffer;
500
501 return 0;
502 }
503
504 /**
505 * Set size of the ceil bucket of HTB class.
506 * @arg class HTB class to be modified.
507 * @arg cbuffer New size in bytes.
508 */
rtnl_htb_set_cbuffer(struct rtnl_class * class,uint32_t cbuffer)509 int rtnl_htb_set_cbuffer(struct rtnl_class *class, uint32_t cbuffer)
510 {
511 struct rtnl_htb_class *htb;
512
513 if (!(htb = htb_class_data(class)))
514 return -NLE_OPNOTSUPP;
515
516 htb->ch_cbuffer = cbuffer;
517 htb->ch_mask |= SCH_HTB_HAS_CBUFFER;
518
519 return 0;
520 }
521
522 /**
523 * Return quantum of HTB class
524 * @arg class htb class object
525 *
526 * See XXX[quantum def]
527 *
528 * @return Quantum or 0 if unspecified.
529 */
rtnl_htb_get_quantum(struct rtnl_class * class)530 uint32_t rtnl_htb_get_quantum(struct rtnl_class *class)
531 {
532 struct rtnl_htb_class *htb;
533
534 if ((htb = htb_class_data(class)) &&
535 htb->ch_mask & SCH_HTB_HAS_QUANTUM)
536 return htb->ch_quantum;
537
538 return 0;
539 }
540
541 /**
542 * Set quantum of HTB class (overwrites value calculated based on r2q)
543 * @arg class htb class object
544 * @arg quantum new quantum in number of bytes
545 *
546 * See XXX[quantum def]
547 *
548 * @return 0 on success or a negative error code.
549 */
rtnl_htb_set_quantum(struct rtnl_class * class,uint32_t quantum)550 int rtnl_htb_set_quantum(struct rtnl_class *class, uint32_t quantum)
551 {
552 struct rtnl_htb_class *htb;
553
554 if (!(htb = htb_class_data(class)))
555 return -NLE_OPNOTSUPP;
556
557 htb->ch_quantum = quantum;
558 htb->ch_mask |= SCH_HTB_HAS_QUANTUM;
559
560 return 0;
561 }
562
563 /**
564 * Return level of HTB class
565 * @arg class htb class object
566 *
567 * Returns the level of the HTB class. Leaf classes are assigned level
568 * 0, root classes have level (TC_HTB_MAXDEPTH - 1). Interior classes
569 * have a level of one less than their parent.
570 *
571 * @return Level or -NLE_OPNOTSUPP
572 */
rtnl_htb_get_level(struct rtnl_class * class)573 int rtnl_htb_get_level(struct rtnl_class *class)
574 {
575 struct rtnl_htb_class *htb;
576
577 if ((htb = htb_class_data(class)) && htb->ch_mask & SCH_HTB_HAS_LEVEL)
578 return htb->ch_level;
579
580 return -NLE_OPNOTSUPP;
581 }
582
583 /**
584 * Set level of HTB class
585 * @arg class htb class object
586 * @arg level new level of HTB class
587 *
588 * Sets the level of a HTB class. Note that changing the level of a HTB
589 * class does not change the level of its in kernel counterpart. This
590 * function is provided only to create HTB objects which can be compared
591 * against or filtered upon.
592 *
593 * @return 0 on success or a negative error code.
594 */
rtnl_htb_set_level(struct rtnl_class * class,int level)595 int rtnl_htb_set_level(struct rtnl_class *class, int level)
596 {
597 struct rtnl_htb_class *htb;
598
599 if (!(htb = htb_class_data(class)))
600 return -NLE_OPNOTSUPP;
601
602 htb->ch_level = level;
603 htb->ch_mask |= SCH_HTB_HAS_LEVEL;
604
605 return 0;
606 }
607
608 /** @} */
609
610 static struct rtnl_tc_ops htb_qdisc_ops = {
611 .to_kind = "htb",
612 .to_type = RTNL_TC_TYPE_QDISC,
613 .to_size = sizeof(struct rtnl_htb_qdisc),
614 .to_msg_parser = htb_qdisc_msg_parser,
615 .to_dump[NL_DUMP_LINE] = htb_qdisc_dump_line,
616 .to_msg_fill = htb_qdisc_msg_fill,
617 };
618
619 static struct rtnl_tc_ops htb_class_ops = {
620 .to_kind = "htb",
621 .to_type = RTNL_TC_TYPE_CLASS,
622 .to_size = sizeof(struct rtnl_htb_class),
623 .to_msg_parser = htb_class_msg_parser,
624 .to_dump = {
625 [NL_DUMP_LINE] = htb_class_dump_line,
626 [NL_DUMP_DETAILS] = htb_class_dump_details,
627 },
628 .to_msg_fill = htb_class_msg_fill,
629 };
630
htb_init(void)631 static void __init htb_init(void)
632 {
633 rtnl_tc_register(&htb_qdisc_ops);
634 rtnl_tc_register(&htb_class_ops);
635 }
636
htb_exit(void)637 static void __exit htb_exit(void)
638 {
639 rtnl_tc_unregister(&htb_qdisc_ops);
640 rtnl_tc_unregister(&htb_class_ops);
641 }
642
643 /** @} */
644