libnl 2.0
|
00001 /* 00002 * lib/route/sch/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-2008 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_api 00016 * @ingroup class_api 00017 * @defgroup 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.h> 00027 #include <netlink/route/qdisc.h> 00028 #include <netlink/route/qdisc-modules.h> 00029 #include <netlink/route/class.h> 00030 #include <netlink/route/class-modules.h> 00031 #include <netlink/route/link.h> 00032 #include <netlink/route/sch/htb.h> 00033 00034 /** @cond SKIP */ 00035 #define SCH_HTB_HAS_RATE2QUANTUM 0x01 00036 #define SCH_HTB_HAS_DEFCLS 0x02 00037 00038 #define SCH_HTB_HAS_PRIO 0x001 00039 #define SCH_HTB_HAS_MTU 0x002 00040 #define SCH_HTB_HAS_RATE 0x004 00041 #define SCH_HTB_HAS_CEIL 0x008 00042 #define SCH_HTB_HAS_RBUFFER 0x010 00043 #define SCH_HTB_HAS_CBUFFER 0x020 00044 #define SCH_HTB_HAS_QUANTUM 0x040 00045 #define SCH_HTB_HAS_OVERHEAD 0x080 00046 #define SCH_HTB_HAS_MPU 0x100 00047 /** @endcond */ 00048 00049 static inline struct rtnl_htb_qdisc *htb_qdisc(struct rtnl_qdisc *qdisc) 00050 { 00051 if (qdisc->q_subdata == NULL) 00052 qdisc->q_subdata = calloc(1, sizeof(struct rtnl_htb_qdisc)); 00053 00054 return (struct rtnl_htb_qdisc *) qdisc->q_subdata; 00055 } 00056 00057 static struct nla_policy htb_policy[TCA_HTB_MAX+1] = { 00058 [TCA_HTB_INIT] = { .minlen = sizeof(struct tc_htb_glob) }, 00059 [TCA_HTB_PARMS] = { .minlen = sizeof(struct tc_htb_opt) }, 00060 }; 00061 00062 static int htb_qdisc_msg_parser(struct rtnl_qdisc *qdisc) 00063 { 00064 int err; 00065 struct nlattr *tb[TCA_HTB_MAX + 1]; 00066 struct rtnl_htb_qdisc *d; 00067 00068 err = tca_parse(tb, TCA_HTB_MAX, (struct rtnl_tca *) qdisc, htb_policy); 00069 if (err < 0) 00070 return err; 00071 00072 d = htb_qdisc(qdisc); 00073 00074 if (tb[TCA_HTB_INIT]) { 00075 struct tc_htb_glob opts; 00076 00077 nla_memcpy(&opts, tb[TCA_HTB_INIT], sizeof(opts)); 00078 d->qh_rate2quantum = opts.rate2quantum; 00079 d->qh_defcls = opts.defcls; 00080 00081 d->qh_mask = (SCH_HTB_HAS_RATE2QUANTUM | SCH_HTB_HAS_DEFCLS); 00082 } 00083 00084 return 0; 00085 } 00086 00087 static void htb_qdisc_free_data(struct rtnl_qdisc *qdisc) 00088 { 00089 free(qdisc->q_subdata); 00090 } 00091 00092 static inline struct rtnl_htb_class *htb_class(struct rtnl_class *class) 00093 { 00094 if (class->c_subdata == NULL) 00095 class->c_subdata = calloc(1, sizeof(struct rtnl_htb_class)); 00096 00097 return (struct rtnl_htb_class *) class->c_subdata; 00098 } 00099 00100 static int htb_class_msg_parser(struct rtnl_class *class) 00101 { 00102 int err; 00103 struct nlattr *tb[TCA_HTB_MAX + 1]; 00104 struct rtnl_htb_class *d; 00105 00106 err = tca_parse(tb, TCA_HTB_MAX, (struct rtnl_tca *) class, htb_policy); 00107 if (err < 0) 00108 return err; 00109 00110 d = htb_class(class); 00111 00112 if (tb[TCA_HTB_PARMS]) { 00113 struct tc_htb_opt opts; 00114 00115 nla_memcpy(&opts, tb[TCA_HTB_PARMS], sizeof(opts)); 00116 d->ch_prio = opts.prio; 00117 rtnl_copy_ratespec(&d->ch_rate, &opts.rate); 00118 rtnl_copy_ratespec(&d->ch_ceil, &opts.ceil); 00119 d->ch_rbuffer = rtnl_tc_calc_bufsize(opts.buffer, opts.rate.rate); 00120 d->ch_cbuffer = rtnl_tc_calc_bufsize(opts.cbuffer, opts.ceil.rate); 00121 d->ch_quantum = opts.quantum; 00122 d->ch_overhead = (opts.rate.mpu >> 8) & 0xff; 00123 d->ch_mpu = opts.rate.mpu & 0xff; 00124 00125 d->ch_mask = (SCH_HTB_HAS_PRIO | SCH_HTB_HAS_RATE | 00126 SCH_HTB_HAS_CEIL | SCH_HTB_HAS_RBUFFER | 00127 SCH_HTB_HAS_CBUFFER | SCH_HTB_HAS_QUANTUM | 00128 SCH_HTB_HAS_OVERHEAD | SCH_HTB_HAS_MPU); 00129 } 00130 00131 return 0; 00132 } 00133 00134 static void htb_class_free_data(struct rtnl_class *class) 00135 { 00136 free(class->c_subdata); 00137 } 00138 00139 static void htb_qdisc_dump_line(struct rtnl_qdisc *qdisc, 00140 struct nl_dump_params *p) 00141 { 00142 struct rtnl_htb_qdisc *d = (struct rtnl_htb_qdisc *) qdisc->q_subdata; 00143 00144 if (d == NULL) 00145 return; 00146 00147 if (d->qh_mask & SCH_HTB_HAS_RATE2QUANTUM) 00148 nl_dump(p, " r2q %u", d->qh_rate2quantum); 00149 00150 if (d->qh_mask & SCH_HTB_HAS_DEFCLS) { 00151 char buf[32]; 00152 nl_dump(p, " default %s", 00153 rtnl_tc_handle2str(d->qh_defcls, buf, sizeof(buf))); 00154 } 00155 } 00156 00157 static void htb_class_dump_line(struct rtnl_class *class, 00158 struct nl_dump_params *p) 00159 { 00160 struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata; 00161 00162 if (d == NULL) 00163 return; 00164 00165 if (d->ch_mask & SCH_HTB_HAS_RATE) { 00166 double r, rbit; 00167 char *ru, *rubit; 00168 00169 r = nl_cancel_down_bytes(d->ch_rate.rs_rate, &ru); 00170 rbit = nl_cancel_down_bits(d->ch_rate.rs_rate*8, &rubit); 00171 00172 nl_dump(p, " rate %.2f%s/s (%.0f%s) log %u", 00173 r, ru, rbit, rubit, 1<<d->ch_rate.rs_cell_log); 00174 } 00175 } 00176 00177 static void htb_class_dump_details(struct rtnl_class *class, 00178 struct nl_dump_params *p) 00179 { 00180 struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata; 00181 00182 if (d == NULL) 00183 return; 00184 00185 /* line 1 */ 00186 if (d->ch_mask & SCH_HTB_HAS_CEIL) { 00187 double r, rbit; 00188 char *ru, *rubit; 00189 00190 r = nl_cancel_down_bytes(d->ch_ceil.rs_rate, &ru); 00191 rbit = nl_cancel_down_bits(d->ch_ceil.rs_rate*8, &rubit); 00192 00193 nl_dump(p, " ceil %.2f%s/s (%.0f%s) log %u", 00194 r, ru, rbit, rubit, 1<<d->ch_ceil.rs_cell_log); 00195 } 00196 00197 if (d->ch_mask & SCH_HTB_HAS_PRIO) 00198 nl_dump(p, " prio %u", d->ch_prio); 00199 00200 if (d->ch_mask & SCH_HTB_HAS_MTU) 00201 nl_dump(p, " mtu %u", d->ch_mtu); 00202 00203 if (d->ch_mask & SCH_HTB_HAS_RBUFFER) { 00204 double b; 00205 char *bu; 00206 00207 b = nl_cancel_down_bytes(d->ch_rbuffer, &bu); 00208 nl_dump(p, " rbuffer %.2f%s", b, bu); 00209 } 00210 00211 if (d->ch_mask & SCH_HTB_HAS_CBUFFER) { 00212 double b; 00213 char *bu; 00214 00215 b = nl_cancel_down_bytes(d->ch_cbuffer, &bu); 00216 nl_dump(p, " cbuffer %.2f%s", b, bu); 00217 } 00218 00219 if (d->ch_mask & SCH_HTB_HAS_QUANTUM) 00220 nl_dump(p, " quantum %u", d->ch_quantum); 00221 00222 if (d->ch_mask & SCH_HTB_HAS_OVERHEAD) 00223 nl_dump(p, " overhead %u", d->ch_overhead); 00224 00225 if (d->ch_mask & SCH_HTB_HAS_MPU) 00226 nl_dump(p, " mpu %u", d->ch_mpu); 00227 } 00228 00229 static struct nl_msg *htb_qdisc_get_opts(struct rtnl_qdisc *qdisc) 00230 { 00231 struct rtnl_htb_qdisc *d = (struct rtnl_htb_qdisc *) qdisc->q_subdata; 00232 struct tc_htb_glob opts; 00233 struct nl_msg *msg; 00234 00235 if (d == NULL) 00236 return NULL; 00237 00238 msg = nlmsg_alloc(); 00239 if (msg == NULL) 00240 return NULL; 00241 00242 memset(&opts, 0, sizeof(opts)); 00243 opts.version = TC_HTB_PROTOVER; 00244 00245 if (d->qh_mask & SCH_HTB_HAS_RATE2QUANTUM) 00246 opts.rate2quantum = d->qh_rate2quantum; 00247 if (d->qh_mask & SCH_HTB_HAS_DEFCLS) 00248 opts.defcls = d->qh_defcls; 00249 00250 nla_put(msg, TCA_HTB_INIT, sizeof(opts), &opts); 00251 00252 return msg; 00253 } 00254 00255 static uint8_t compute_cell(uint32_t rate, uint32_t mtu) 00256 { 00257 uint8_t cell_log = 0; 00258 while (mtu > 255) { 00259 mtu >>= 1; 00260 cell_log++; 00261 } 00262 00263 return cell_log; 00264 } 00265 00266 static struct nl_msg *htb_class_get_opts(struct rtnl_class *class) 00267 { 00268 struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata; 00269 uint32_t mtu, rtable[RTNL_TC_RTABLE_SIZE], ctable[RTNL_TC_RTABLE_SIZE]; 00270 struct tc_htb_opt opts; 00271 struct nl_msg *msg; 00272 int buffer, cbuffer; 00273 uint8_t overhead = 0, mpu = 0; 00274 00275 if (d == NULL) 00276 return NULL; 00277 00278 msg = nlmsg_alloc(); 00279 memset(&opts, 0, sizeof(opts)); 00280 00281 /* if not set, zero (0) is used as priority */ 00282 if (d->ch_mask & SCH_HTB_HAS_PRIO) 00283 opts.prio = d->ch_prio; 00284 00285 if (d->ch_mask & SCH_HTB_HAS_MTU) 00286 mtu = d->ch_mtu; 00287 else 00288 mtu = 1600; /* eth packet len */ 00289 00290 if (!(d->ch_mask & SCH_HTB_HAS_RATE)) 00291 BUG(); 00292 00293 rtnl_rcopy_ratespec(&opts.rate, &d->ch_rate); 00294 /* if cell_log not set, compute default value */ 00295 if (opts.rate.cell_log == UINT8_MAX) 00296 opts.rate.cell_log = compute_cell(opts.rate.rate, mtu); 00297 00298 /* if not set, configured rate is used as ceil, which implies no borrowing */ 00299 if (d->ch_mask & SCH_HTB_HAS_CEIL) 00300 rtnl_rcopy_ratespec(&opts.ceil, &d->ch_ceil); 00301 else 00302 memcpy(&opts.ceil, &opts.rate, sizeof(struct tc_ratespec)); 00303 /* if cell_log not set, compute default value */ 00304 if (opts.ceil.cell_log == UINT8_MAX) 00305 opts.ceil.cell_log = compute_cell(opts.ceil.rate, mtu); 00306 00307 if (d->ch_mask & SCH_HTB_HAS_RBUFFER) 00308 buffer = d->ch_rbuffer; 00309 else 00310 buffer = opts.rate.rate / nl_get_hz() + mtu; 00311 00312 opts.buffer = rtnl_tc_calc_txtime(buffer, opts.rate.rate); 00313 00314 if (d->ch_mask & SCH_HTB_HAS_CBUFFER) 00315 cbuffer = d->ch_cbuffer; 00316 else 00317 cbuffer = opts.ceil.rate / nl_get_hz() + mtu; 00318 00319 opts.cbuffer = rtnl_tc_calc_txtime(cbuffer, opts.ceil.rate); 00320 00321 if (d->ch_mask & SCH_HTB_HAS_QUANTUM) 00322 opts.quantum = d->ch_quantum; 00323 00324 if (d->ch_mask & SCH_HTB_HAS_OVERHEAD) 00325 overhead = d->ch_overhead; 00326 00327 if (d->ch_mask & SCH_HTB_HAS_MPU) 00328 mpu = d->ch_mpu; 00329 00330 opts.rate.mpu = mpu | (overhead << 8); 00331 opts.ceil.mpu = mpu | (overhead << 8); 00332 00333 nla_put(msg, TCA_HTB_PARMS, sizeof(opts), &opts); 00334 00335 rtnl_tc_build_rate_table(rtable, mpu, overhead, 00336 1 << opts.rate.cell_log, 00337 opts.rate.rate); 00338 nla_put(msg, TCA_HTB_RTAB, sizeof(rtable), &rtable); 00339 00340 rtnl_tc_build_rate_table(ctable, mpu, overhead, 00341 1 << opts.ceil.cell_log, 00342 opts.ceil.rate); 00343 nla_put(msg, TCA_HTB_CTAB, sizeof(ctable), &ctable); 00344 00345 return msg; 00346 } 00347 00348 /** 00349 * @name Attribute Modifications 00350 * @{ 00351 */ 00352 00353 void rtnl_htb_set_rate2quantum(struct rtnl_qdisc *qdisc, uint32_t rate2quantum) 00354 { 00355 struct rtnl_htb_qdisc *d = htb_qdisc(qdisc); 00356 if (d == NULL) 00357 return; 00358 00359 d->qh_rate2quantum = rate2quantum; 00360 d->qh_mask |= SCH_HTB_HAS_RATE2QUANTUM; 00361 } 00362 00363 /** 00364 * Set default class of the htb qdisc to the specified value 00365 * @arg qdisc qdisc to change 00366 * @arg defcls new default class 00367 */ 00368 void rtnl_htb_set_defcls(struct rtnl_qdisc *qdisc, uint32_t defcls) 00369 { 00370 struct rtnl_htb_qdisc *d = htb_qdisc(qdisc); 00371 if (d == NULL) 00372 return; 00373 00374 d->qh_defcls = defcls; 00375 d->qh_mask |= SCH_HTB_HAS_DEFCLS; 00376 } 00377 00378 void rtnl_htb_set_prio(struct rtnl_class *class, uint32_t prio) 00379 { 00380 struct rtnl_htb_class *d = htb_class(class); 00381 if (d == NULL) 00382 return; 00383 00384 d->ch_prio = prio; 00385 d->ch_mask |= SCH_HTB_HAS_PRIO; 00386 } 00387 00388 /** 00389 * Set MTU of the data link. 00390 * @arg class HTB class to be modified. 00391 * @arg mtu New MTU in bytes. 00392 * 00393 * Sets MTU of the data link controlled by the HTB class. 00394 * If not set, the Ethernet MTU (1600) is used. 00395 */ 00396 void rtnl_htb_set_mtu(struct rtnl_class *class, uint32_t mtu) 00397 { 00398 struct rtnl_htb_class *d = htb_class(class); 00399 if (d == NULL) 00400 return; 00401 00402 d->ch_mtu = mtu; 00403 d->ch_mask |= SCH_HTB_HAS_MTU; 00404 } 00405 00406 /** 00407 * Set rate of HTB class. 00408 * @arg class HTB class to be modified. 00409 * @arg rate New rate in bytes per second. 00410 */ 00411 void rtnl_htb_set_rate(struct rtnl_class *class, uint32_t rate) 00412 { 00413 struct rtnl_htb_class *d = htb_class(class); 00414 if (d == NULL) 00415 return; 00416 00417 d->ch_rate.rs_cell_log = UINT8_MAX; /* use default value */ 00418 d->ch_rate.rs_rate = rate; 00419 d->ch_mask |= SCH_HTB_HAS_RATE; 00420 } 00421 00422 /** 00423 * Set ceil of HTB class. 00424 * @arg class HTB class to be modified. 00425 * @arg ceil New ceil in bytes per second. 00426 */ 00427 void rtnl_htb_set_ceil(struct rtnl_class *class, uint32_t ceil) 00428 { 00429 struct rtnl_htb_class *d = htb_class(class); 00430 if (d == NULL) 00431 return; 00432 00433 d->ch_ceil.rs_cell_log = UINT8_MAX; /* use default value */ 00434 d->ch_ceil.rs_rate = ceil; 00435 d->ch_mask |= SCH_HTB_HAS_CEIL; 00436 } 00437 00438 /** 00439 * Set size of the rate bucket of HTB class. 00440 * @arg class HTB class to be modified. 00441 * @arg rbuffer New size in bytes. 00442 */ 00443 void rtnl_htb_set_rbuffer(struct rtnl_class *class, uint32_t rbuffer) 00444 { 00445 struct rtnl_htb_class *d = htb_class(class); 00446 if (d == NULL) 00447 return; 00448 00449 d->ch_rbuffer = rbuffer; 00450 d->ch_mask |= SCH_HTB_HAS_RBUFFER; 00451 } 00452 00453 /** 00454 * Set size of the ceil bucket of HTB class. 00455 * @arg class HTB class to be modified. 00456 * @arg cbuffer New size in bytes. 00457 */ 00458 void rtnl_htb_set_cbuffer(struct rtnl_class *class, uint32_t cbuffer) 00459 { 00460 struct rtnl_htb_class *d = htb_class(class); 00461 if (d == NULL) 00462 return; 00463 00464 d->ch_cbuffer = cbuffer; 00465 d->ch_mask |= SCH_HTB_HAS_CBUFFER; 00466 } 00467 00468 /** 00469 * Set how much bytes to serve from leaf at once of HTB class {use r2q}. 00470 * @arg class HTB class to be modified. 00471 * @arg quantum New size in bytes. 00472 */ 00473 void rtnl_htb_set_quantum(struct rtnl_class *class, uint32_t quantum) 00474 { 00475 struct rtnl_htb_class *d = htb_class(class); 00476 if (d == NULL) 00477 return; 00478 00479 d->ch_quantum = quantum; 00480 d->ch_mask |= SCH_HTB_HAS_QUANTUM; 00481 } 00482 00483 /** 00484 * Set per-packet size overhead used in rate computations of HTB class. 00485 * @arg class HTB class to be modified. 00486 * @arg overhead Size in bytes. 00487 */ 00488 void rtnl_htb_set_overhead(struct rtnl_class *class, uint8_t overhead) 00489 { 00490 struct rtnl_htb_class *d = htb_class(class); 00491 if (d == NULL) 00492 return; 00493 00494 d->ch_overhead = overhead; 00495 d->ch_mask |= SCH_HTB_HAS_OVERHEAD; 00496 } 00497 00498 /** 00499 * Set the minimum packet size used in rate computations of HTB class. 00500 * @arg class HTB class to be modified. 00501 * @arg mpu Size in bytes. 00502 */ 00503 void rtnl_htb_set_mpu(struct rtnl_class *class, uint8_t mpu) 00504 { 00505 struct rtnl_htb_class *d = htb_class(class); 00506 if (d == NULL) 00507 return; 00508 00509 d->ch_mpu = mpu; 00510 d->ch_mask |= SCH_HTB_HAS_MPU; 00511 } 00512 00513 /** @} */ 00514 00515 static struct rtnl_qdisc_ops htb_qdisc_ops = { 00516 .qo_kind = "htb", 00517 .qo_msg_parser = htb_qdisc_msg_parser, 00518 .qo_free_data = htb_qdisc_free_data, 00519 .qo_dump[NL_DUMP_LINE] = htb_qdisc_dump_line, 00520 .qo_get_opts = htb_qdisc_get_opts, 00521 }; 00522 00523 static struct rtnl_class_ops htb_class_ops = { 00524 .co_kind = "htb", 00525 .co_msg_parser = htb_class_msg_parser, 00526 .co_free_data = htb_class_free_data, 00527 .co_dump = { 00528 [NL_DUMP_LINE] = htb_class_dump_line, 00529 [NL_DUMP_DETAILS] = htb_class_dump_details, 00530 }, 00531 .co_get_opts = htb_class_get_opts, 00532 }; 00533 00534 static void __init htb_init(void) 00535 { 00536 rtnl_qdisc_register(&htb_qdisc_ops); 00537 rtnl_class_register(&htb_class_ops); 00538 } 00539 00540 static void __exit htb_exit(void) 00541 { 00542 rtnl_qdisc_unregister(&htb_qdisc_ops); 00543 rtnl_class_unregister(&htb_class_ops); 00544 } 00545 00546 /** @} */