Home | History | Annotate | Download | only in qdisc
      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 (at) suug.ch>
     10  * Copyright (c) 2005-2006 Petr Gotthard <petr.gotthard (at) 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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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  */
    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 
    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  */
    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  */
    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 
    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 
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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 
    631 static void __init htb_init(void)
    632 {
    633 	rtnl_tc_register(&htb_qdisc_ops);
    634 	rtnl_tc_register(&htb_class_ops);
    635 }
    636 
    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