libnl
3.2.3
|
00001 /* 00002 * lib/route/qdisc/htb.c HTB Qdisc 00003 * 00004 * This library is free software; you can redistribute it and/or 00005 * modify it under the terms of the GNU Lesser General Public 00006 * License as published by the Free Software Foundation version 2.1 00007 * of the License. 00008 * 00009 * Copyright (c) 2003-2011 Thomas Graf <tgraf@suug.ch> 00010 * Copyright (c) 2005-2006 Petr Gotthard <petr.gotthard@siemens.com> 00011 * Copyright (c) 2005-2006 Siemens AG Oesterreich 00012 */ 00013 00014 /** 00015 * @ingroup qdisc 00016 * @ingroup class 00017 * @defgroup qdisc_htb Hierachical Token Bucket (HTB) 00018 * @{ 00019 */ 00020 00021 #include <netlink-local.h> 00022 #include <netlink-tc.h> 00023 #include <netlink/netlink.h> 00024 #include <netlink/cache.h> 00025 #include <netlink/utils.h> 00026 #include <netlink/route/tc-api.h> 00027 #include <netlink/route/qdisc.h> 00028 #include <netlink/route/class.h> 00029 #include <netlink/route/link.h> 00030 #include <netlink/route/qdisc/htb.h> 00031 00032 /** @cond SKIP */ 00033 #define SCH_HTB_HAS_RATE2QUANTUM 0x01 00034 #define SCH_HTB_HAS_DEFCLS 0x02 00035 00036 #define SCH_HTB_HAS_PRIO 0x001 00037 #define SCH_HTB_HAS_RATE 0x002 00038 #define SCH_HTB_HAS_CEIL 0x004 00039 #define SCH_HTB_HAS_RBUFFER 0x008 00040 #define SCH_HTB_HAS_CBUFFER 0x010 00041 #define SCH_HTB_HAS_QUANTUM 0x020 00042 #define SCH_HTB_HAS_LEVEL 0x040 00043 /** @endcond */ 00044 00045 static struct nla_policy htb_policy[TCA_HTB_MAX+1] = { 00046 [TCA_HTB_INIT] = { .minlen = sizeof(struct tc_htb_glob) }, 00047 [TCA_HTB_PARMS] = { .minlen = sizeof(struct tc_htb_opt) }, 00048 }; 00049 00050 static int htb_qdisc_msg_parser(struct rtnl_tc *tc, void *data) 00051 { 00052 struct nlattr *tb[TCA_HTB_MAX + 1]; 00053 struct rtnl_htb_qdisc *htb = data; 00054 int err; 00055 00056 if ((err = tca_parse(tb, TCA_HTB_MAX, tc, htb_policy)) < 0) 00057 return err; 00058 00059 if (tb[TCA_HTB_INIT]) { 00060 struct tc_htb_glob opts; 00061 00062 nla_memcpy(&opts, tb[TCA_HTB_INIT], sizeof(opts)); 00063 htb->qh_rate2quantum = opts.rate2quantum; 00064 htb->qh_defcls = opts.defcls; 00065 htb->qh_direct_pkts = opts.direct_pkts; 00066 00067 htb->qh_mask = (SCH_HTB_HAS_RATE2QUANTUM | SCH_HTB_HAS_DEFCLS); 00068 } 00069 00070 return 0; 00071 } 00072 00073 static int htb_class_msg_parser(struct rtnl_tc *tc, void *data) 00074 { 00075 struct nlattr *tb[TCA_HTB_MAX + 1]; 00076 struct rtnl_htb_class *htb = data; 00077 int err; 00078 00079 if ((err = tca_parse(tb, TCA_HTB_MAX, tc, htb_policy)) < 0) 00080 return err; 00081 00082 if (tb[TCA_HTB_PARMS]) { 00083 struct tc_htb_opt opts; 00084 00085 nla_memcpy(&opts, tb[TCA_HTB_PARMS], sizeof(opts)); 00086 htb->ch_prio = opts.prio; 00087 rtnl_copy_ratespec(&htb->ch_rate, &opts.rate); 00088 rtnl_copy_ratespec(&htb->ch_ceil, &opts.ceil); 00089 htb->ch_rbuffer = rtnl_tc_calc_bufsize(opts.buffer, 00090 opts.rate.rate); 00091 htb->ch_cbuffer = rtnl_tc_calc_bufsize(opts.cbuffer, 00092 opts.ceil.rate); 00093 htb->ch_quantum = opts.quantum; 00094 htb->ch_level = opts.level; 00095 00096 rtnl_tc_set_mpu(tc, htb->ch_rate.rs_mpu); 00097 rtnl_tc_set_overhead(tc, htb->ch_rate.rs_overhead); 00098 00099 htb->ch_mask = (SCH_HTB_HAS_PRIO | SCH_HTB_HAS_RATE | 00100 SCH_HTB_HAS_CEIL | SCH_HTB_HAS_RBUFFER | 00101 SCH_HTB_HAS_CBUFFER | SCH_HTB_HAS_QUANTUM | 00102 SCH_HTB_HAS_LEVEL); 00103 } 00104 00105 return 0; 00106 } 00107 00108 static void htb_qdisc_dump_line(struct rtnl_tc *tc, void *data, 00109 struct nl_dump_params *p) 00110 { 00111 struct rtnl_htb_qdisc *htb = data; 00112 00113 if (!htb) 00114 return; 00115 00116 if (htb->qh_mask & SCH_HTB_HAS_RATE2QUANTUM) 00117 nl_dump(p, " r2q %u", htb->qh_rate2quantum); 00118 00119 if (htb->qh_mask & SCH_HTB_HAS_DEFCLS) { 00120 char buf[64]; 00121 nl_dump(p, " default-class %s", 00122 rtnl_tc_handle2str(htb->qh_defcls, buf, sizeof(buf))); 00123 } 00124 } 00125 00126 static void htb_class_dump_line(struct rtnl_tc *tc, void *data, 00127 struct nl_dump_params *p) 00128 { 00129 struct rtnl_htb_class *htb = data; 00130 00131 if (!htb) 00132 return; 00133 00134 if (htb->ch_mask & SCH_HTB_HAS_RATE) { 00135 double r, rbit; 00136 char *ru, *rubit; 00137 00138 r = nl_cancel_down_bytes(htb->ch_rate.rs_rate, &ru); 00139 rbit = nl_cancel_down_bits(htb->ch_rate.rs_rate*8, &rubit); 00140 00141 nl_dump(p, " rate %.2f%s/s (%.0f%s) log %u", 00142 r, ru, rbit, rubit, 1<<htb->ch_rate.rs_cell_log); 00143 } 00144 } 00145 00146 static void htb_class_dump_details(struct rtnl_tc *tc, void *data, 00147 struct nl_dump_params *p) 00148 { 00149 struct rtnl_htb_class *htb = data; 00150 00151 if (!htb) 00152 return; 00153 00154 /* line 1 */ 00155 if (htb->ch_mask & SCH_HTB_HAS_CEIL) { 00156 double r, rbit; 00157 char *ru, *rubit; 00158 00159 r = nl_cancel_down_bytes(htb->ch_ceil.rs_rate, &ru); 00160 rbit = nl_cancel_down_bits(htb->ch_ceil.rs_rate*8, &rubit); 00161 00162 nl_dump(p, " ceil %.2f%s/s (%.0f%s) log %u", 00163 r, ru, rbit, rubit, 1<<htb->ch_ceil.rs_cell_log); 00164 } 00165 00166 if (htb->ch_mask & SCH_HTB_HAS_PRIO) 00167 nl_dump(p, " prio %u", htb->ch_prio); 00168 00169 if (htb->ch_mask & SCH_HTB_HAS_RBUFFER) { 00170 double b; 00171 char *bu; 00172 00173 b = nl_cancel_down_bytes(htb->ch_rbuffer, &bu); 00174 nl_dump(p, " rbuffer %.2f%s", b, bu); 00175 } 00176 00177 if (htb->ch_mask & SCH_HTB_HAS_CBUFFER) { 00178 double b; 00179 char *bu; 00180 00181 b = nl_cancel_down_bytes(htb->ch_cbuffer, &bu); 00182 nl_dump(p, " cbuffer %.2f%s", b, bu); 00183 } 00184 00185 if (htb->ch_mask & SCH_HTB_HAS_QUANTUM) 00186 nl_dump(p, " quantum %u", htb->ch_quantum); 00187 } 00188 00189 static int htb_qdisc_msg_fill(struct rtnl_tc *tc, void *data, 00190 struct nl_msg *msg) 00191 { 00192 struct rtnl_htb_qdisc *htb = data; 00193 struct tc_htb_glob opts = {0}; 00194 00195 opts.version = TC_HTB_PROTOVER; 00196 opts.rate2quantum = 10; 00197 00198 if (htb) { 00199 if (htb->qh_mask & SCH_HTB_HAS_RATE2QUANTUM) 00200 opts.rate2quantum = htb->qh_rate2quantum; 00201 00202 if (htb->qh_mask & SCH_HTB_HAS_DEFCLS) 00203 opts.defcls = htb->qh_defcls; 00204 } 00205 00206 return nla_put(msg, TCA_HTB_INIT, sizeof(opts), &opts); 00207 } 00208 00209 static int htb_class_msg_fill(struct rtnl_tc *tc, void *data, 00210 struct nl_msg *msg) 00211 { 00212 struct rtnl_htb_class *htb = data; 00213 uint32_t mtu, rtable[RTNL_TC_RTABLE_SIZE], ctable[RTNL_TC_RTABLE_SIZE]; 00214 struct tc_htb_opt opts; 00215 int buffer, cbuffer; 00216 00217 if (!htb || !(htb->ch_mask & SCH_HTB_HAS_RATE)) 00218 BUG(); 00219 00220 memset(&opts, 0, sizeof(opts)); 00221 00222 /* if not set, zero (0) is used as priority */ 00223 if (htb->ch_mask & SCH_HTB_HAS_PRIO) 00224 opts.prio = htb->ch_prio; 00225 00226 mtu = rtnl_tc_get_mtu(tc); 00227 00228 rtnl_tc_build_rate_table(tc, &htb->ch_rate, rtable); 00229 rtnl_rcopy_ratespec(&opts.rate, &htb->ch_rate); 00230 00231 if (htb->ch_mask & SCH_HTB_HAS_CEIL) { 00232 rtnl_tc_build_rate_table(tc, &htb->ch_ceil, ctable); 00233 rtnl_rcopy_ratespec(&opts.ceil, &htb->ch_ceil); 00234 } else { 00235 /* 00236 * If not set, configured rate is used as ceil, which implies 00237 * no borrowing. 00238 */ 00239 memcpy(&opts.ceil, &opts.rate, sizeof(struct tc_ratespec)); 00240 } 00241 00242 if (htb->ch_mask & SCH_HTB_HAS_RBUFFER) 00243 buffer = htb->ch_rbuffer; 00244 else 00245 buffer = opts.rate.rate / nl_get_user_hz() + mtu; /* XXX */ 00246 00247 opts.buffer = rtnl_tc_calc_txtime(buffer, opts.rate.rate); 00248 00249 if (htb->ch_mask & SCH_HTB_HAS_CBUFFER) 00250 cbuffer = htb->ch_cbuffer; 00251 else 00252 cbuffer = opts.ceil.rate / nl_get_user_hz() + mtu; /* XXX */ 00253 00254 opts.cbuffer = rtnl_tc_calc_txtime(cbuffer, opts.ceil.rate); 00255 00256 if (htb->ch_mask & SCH_HTB_HAS_QUANTUM) 00257 opts.quantum = htb->ch_quantum; 00258 00259 NLA_PUT(msg, TCA_HTB_PARMS, sizeof(opts), &opts); 00260 NLA_PUT(msg, TCA_HTB_RTAB, sizeof(rtable), &rtable); 00261 NLA_PUT(msg, TCA_HTB_CTAB, sizeof(ctable), &ctable); 00262 00263 return 0; 00264 00265 nla_put_failure: 00266 return -NLE_MSGSIZE; 00267 } 00268 00269 static struct rtnl_tc_ops htb_qdisc_ops; 00270 static struct rtnl_tc_ops htb_class_ops; 00271 00272 static struct rtnl_htb_qdisc *htb_qdisc_data(struct rtnl_qdisc *qdisc) 00273 { 00274 return rtnl_tc_data_check(TC_CAST(qdisc), &htb_qdisc_ops); 00275 } 00276 00277 static struct rtnl_htb_class *htb_class_data(struct rtnl_class *class) 00278 { 00279 return rtnl_tc_data_check(TC_CAST(class), &htb_class_ops); 00280 } 00281 00282 /** 00283 * @name Attribute Modifications 00284 * @{ 00285 */ 00286 00287 /** 00288 * Return rate/quantum ratio of HTB qdisc 00289 * @arg qdisc htb qdisc object 00290 * 00291 * @return rate/quantum ratio or 0 if unspecified 00292 */ 00293 uint32_t rtnl_htb_get_rate2quantum(struct rtnl_qdisc *qdisc) 00294 { 00295 struct rtnl_htb_qdisc *htb; 00296 00297 if ((htb = htb_qdisc_data(qdisc)) && 00298 htb->qh_mask & SCH_HTB_HAS_RATE2QUANTUM) 00299 return htb->qh_rate2quantum; 00300 00301 return 0; 00302 } 00303 00304 int rtnl_htb_set_rate2quantum(struct rtnl_qdisc *qdisc, uint32_t rate2quantum) 00305 { 00306 struct rtnl_htb_qdisc *htb; 00307 00308 if (!(htb = htb_qdisc_data(qdisc))) 00309 return -NLE_OPNOTSUPP; 00310 00311 htb->qh_rate2quantum = rate2quantum; 00312 htb->qh_mask |= SCH_HTB_HAS_RATE2QUANTUM; 00313 00314 return 0; 00315 } 00316 00317 /** 00318 * Return default class of HTB qdisc 00319 * @arg qdisc htb qdisc object 00320 * 00321 * Returns the classid of the class where all unclassified traffic 00322 * goes to. 00323 * 00324 * @return classid or TC_H_UNSPEC if unspecified. 00325 */ 00326 uint32_t rtnl_htb_get_defcls(struct rtnl_qdisc *qdisc) 00327 { 00328 struct rtnl_htb_qdisc *htb; 00329 00330 if ((htb = htb_qdisc_data(qdisc)) && 00331 htb->qh_mask & SCH_HTB_HAS_DEFCLS) 00332 return htb->qh_defcls; 00333 00334 return TC_H_UNSPEC; 00335 } 00336 00337 /** 00338 * Set default class of the htb qdisc to the specified value 00339 * @arg qdisc qdisc to change 00340 * @arg defcls new default class 00341 */ 00342 int rtnl_htb_set_defcls(struct rtnl_qdisc *qdisc, uint32_t defcls) 00343 { 00344 struct rtnl_htb_qdisc *htb; 00345 00346 if (!(htb = htb_qdisc_data(qdisc))) 00347 return -NLE_OPNOTSUPP; 00348 00349 htb->qh_defcls = defcls; 00350 htb->qh_mask |= SCH_HTB_HAS_DEFCLS; 00351 00352 return 0; 00353 } 00354 00355 uint32_t rtnl_htb_get_prio(struct rtnl_class *class) 00356 { 00357 struct rtnl_htb_class *htb; 00358 00359 if ((htb = htb_class_data(class)) && htb->ch_mask & SCH_HTB_HAS_PRIO) 00360 return htb->ch_prio; 00361 00362 return 0; 00363 } 00364 00365 int rtnl_htb_set_prio(struct rtnl_class *class, uint32_t prio) 00366 { 00367 struct rtnl_htb_class *htb; 00368 00369 if (!(htb = htb_class_data(class))) 00370 return -NLE_OPNOTSUPP; 00371 00372 htb->ch_prio = prio; 00373 htb->ch_mask |= SCH_HTB_HAS_PRIO; 00374 00375 return 0; 00376 } 00377 00378 /** 00379 * Return rate of HTB class 00380 * @arg class htb class object 00381 * 00382 * @return Rate in bytes/s or 0 if unspecified. 00383 */ 00384 uint32_t rtnl_htb_get_rate(struct rtnl_class *class) 00385 { 00386 struct rtnl_htb_class *htb; 00387 00388 if ((htb = htb_class_data(class)) && htb->ch_mask & SCH_HTB_HAS_RATE) 00389 return htb->ch_rate.rs_rate; 00390 00391 return 0; 00392 } 00393 00394 /** 00395 * Set rate of HTB class 00396 * @arg class htb class object 00397 * @arg rate new rate in bytes per second 00398 * 00399 * @return 0 on success or a negative error code. 00400 */ 00401 int rtnl_htb_set_rate(struct rtnl_class *class, uint32_t rate) 00402 { 00403 struct rtnl_htb_class *htb; 00404 00405 if (!(htb = htb_class_data(class))) 00406 return -NLE_OPNOTSUPP; 00407 00408 htb->ch_rate.rs_cell_log = UINT8_MAX; /* use default value */ 00409 htb->ch_rate.rs_rate = rate; 00410 htb->ch_mask |= SCH_HTB_HAS_RATE; 00411 00412 return 0; 00413 } 00414 00415 /** 00416 * Return ceil rate of HTB class 00417 * @arg class htb class object 00418 * 00419 * @return Ceil rate in bytes/s or 0 if unspecified 00420 */ 00421 uint32_t rtnl_htb_get_ceil(struct rtnl_class *class) 00422 { 00423 struct rtnl_htb_class *htb; 00424 00425 if ((htb = htb_class_data(class)) && htb->ch_mask & SCH_HTB_HAS_CEIL) 00426 return htb->ch_ceil.rs_rate; 00427 00428 return 0; 00429 } 00430 00431 /** 00432 * Set ceil rate of HTB class 00433 * @arg class htb class object 00434 * @arg ceil new ceil rate number of bytes per second 00435 * 00436 * @return 0 on success or a negative error code. 00437 */ 00438 int rtnl_htb_set_ceil(struct rtnl_class *class, uint32_t ceil) 00439 { 00440 struct rtnl_htb_class *htb; 00441 00442 if (!(htb = htb_class_data(class))) 00443 return -NLE_OPNOTSUPP; 00444 00445 htb->ch_ceil.rs_cell_log = UINT8_MAX; /* use default value */ 00446 htb->ch_ceil.rs_rate = ceil; 00447 htb->ch_mask |= SCH_HTB_HAS_CEIL; 00448 00449 return 0; 00450 } 00451 00452 /** 00453 * Return burst buffer size of HTB class 00454 * @arg class htb class object 00455 * 00456 * @return Burst buffer size or 0 if unspecified 00457 */ 00458 uint32_t rtnl_htb_get_rbuffer(struct rtnl_class *class) 00459 { 00460 struct rtnl_htb_class *htb; 00461 00462 if ((htb = htb_class_data(class)) && 00463 htb->ch_mask & SCH_HTB_HAS_RBUFFER) 00464 return htb->ch_rbuffer; 00465 00466 return 0; 00467 } 00468 00469 /** 00470 * Set size of the rate bucket of HTB class. 00471 * @arg class HTB class to be modified. 00472 * @arg rbuffer New size in bytes. 00473 */ 00474 int rtnl_htb_set_rbuffer(struct rtnl_class *class, uint32_t rbuffer) 00475 { 00476 struct rtnl_htb_class *htb; 00477 00478 if (!(htb = htb_class_data(class))) 00479 return -NLE_OPNOTSUPP; 00480 00481 htb->ch_rbuffer = rbuffer; 00482 htb->ch_mask |= SCH_HTB_HAS_RBUFFER; 00483 00484 return 0; 00485 } 00486 00487 /** 00488 * Return ceil burst buffer size of HTB class 00489 * @arg class htb class object 00490 * 00491 * @return Ceil burst buffer size or 0 if unspecified 00492 */ 00493 uint32_t rtnl_htb_get_cbuffer(struct rtnl_class *class) 00494 { 00495 struct rtnl_htb_class *htb; 00496 00497 if ((htb = htb_class_data(class)) && 00498 htb->ch_mask & SCH_HTB_HAS_CBUFFER) 00499 return htb->ch_cbuffer; 00500 00501 return 0; 00502 } 00503 00504 /** 00505 * Set size of the ceil bucket of HTB class. 00506 * @arg class HTB class to be modified. 00507 * @arg cbuffer New size in bytes. 00508 */ 00509 int rtnl_htb_set_cbuffer(struct rtnl_class *class, uint32_t cbuffer) 00510 { 00511 struct rtnl_htb_class *htb; 00512 00513 if (!(htb = htb_class_data(class))) 00514 return -NLE_OPNOTSUPP; 00515 00516 htb->ch_cbuffer = cbuffer; 00517 htb->ch_mask |= SCH_HTB_HAS_CBUFFER; 00518 00519 return 0; 00520 } 00521 00522 /** 00523 * Return quantum of HTB class 00524 * @arg class htb class object 00525 * 00526 * See XXX[quantum def] 00527 * 00528 * @return Quantum or 0 if unspecified. 00529 */ 00530 uint32_t rtnl_htb_get_quantum(struct rtnl_class *class) 00531 { 00532 struct rtnl_htb_class *htb; 00533 00534 if ((htb = htb_class_data(class)) && 00535 htb->ch_mask & SCH_HTB_HAS_QUANTUM) 00536 return htb->ch_quantum; 00537 00538 return 0; 00539 } 00540 00541 /** 00542 * Set quantum of HTB class (overwrites value calculated based on r2q) 00543 * @arg class htb class object 00544 * @arg quantum new quantum in number of bytes 00545 * 00546 * See XXX[quantum def] 00547 * 00548 * @return 0 on success or a negative error code. 00549 */ 00550 int rtnl_htb_set_quantum(struct rtnl_class *class, uint32_t quantum) 00551 { 00552 struct rtnl_htb_class *htb; 00553 00554 if (!(htb = htb_class_data(class))) 00555 return -NLE_OPNOTSUPP; 00556 00557 htb->ch_quantum = quantum; 00558 htb->ch_mask |= SCH_HTB_HAS_QUANTUM; 00559 00560 return 0; 00561 } 00562 00563 /** 00564 * Return level of HTB class 00565 * @arg class htb class object 00566 * 00567 * Returns the level of the HTB class. Leaf classes are assigned level 00568 * 0, root classes have level (TC_HTB_MAXDEPTH - 1). Interior classes 00569 * have a level of one less than their parent. 00570 * 00571 * @return Level or -NLE_OPNOTSUPP 00572 */ 00573 int rtnl_htb_get_level(struct rtnl_class *class) 00574 { 00575 struct rtnl_htb_class *htb; 00576 00577 if ((htb = htb_class_data(class)) && htb->ch_mask & SCH_HTB_HAS_LEVEL) 00578 return htb->ch_level; 00579 00580 return -NLE_OPNOTSUPP; 00581 } 00582 00583 /** 00584 * Set level of HTB class 00585 * @arg class htb class object 00586 * @arg level new level of HTB class 00587 * 00588 * Sets the level of a HTB class. Note that changing the level of a HTB 00589 * class does not change the level of its in kernel counterpart. This 00590 * function is provided only to create HTB objects which can be compared 00591 * against or filtered upon. 00592 * 00593 * @return 0 on success or a negative error code. 00594 */ 00595 int rtnl_htb_set_level(struct rtnl_class *class, int level) 00596 { 00597 struct rtnl_htb_class *htb; 00598 00599 if (!(htb = htb_class_data(class))) 00600 return -NLE_OPNOTSUPP; 00601 00602 htb->ch_level = level; 00603 htb->ch_mask |= SCH_HTB_HAS_LEVEL; 00604 00605 return 0; 00606 } 00607 00608 /** @} */ 00609 00610 static struct rtnl_tc_ops htb_qdisc_ops = { 00611 .to_kind = "htb", 00612 .to_type = RTNL_TC_TYPE_QDISC, 00613 .to_size = sizeof(struct rtnl_htb_qdisc), 00614 .to_msg_parser = htb_qdisc_msg_parser, 00615 .to_dump[NL_DUMP_LINE] = htb_qdisc_dump_line, 00616 .to_msg_fill = htb_qdisc_msg_fill, 00617 }; 00618 00619 static struct rtnl_tc_ops htb_class_ops = { 00620 .to_kind = "htb", 00621 .to_type = RTNL_TC_TYPE_CLASS, 00622 .to_size = sizeof(struct rtnl_htb_class), 00623 .to_msg_parser = htb_class_msg_parser, 00624 .to_dump = { 00625 [NL_DUMP_LINE] = htb_class_dump_line, 00626 [NL_DUMP_DETAILS] = htb_class_dump_details, 00627 }, 00628 .to_msg_fill = htb_class_msg_fill, 00629 }; 00630 00631 static void __init htb_init(void) 00632 { 00633 rtnl_tc_register(&htb_qdisc_ops); 00634 rtnl_tc_register(&htb_class_ops); 00635 } 00636 00637 static void __exit htb_exit(void) 00638 { 00639 rtnl_tc_unregister(&htb_qdisc_ops); 00640 rtnl_tc_unregister(&htb_class_ops); 00641 } 00642 00643 /** @} */