libnl 1.1
|
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-2006 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 int htb_qdisc_dump_brief(struct rtnl_qdisc *qdisc, 00140 struct nl_dump_params *p, int line) 00141 { 00142 struct rtnl_htb_qdisc *d = (struct rtnl_htb_qdisc *) qdisc->q_subdata; 00143 00144 if (d == NULL) 00145 goto ignore; 00146 00147 if (d->qh_mask & SCH_HTB_HAS_RATE2QUANTUM) 00148 dp_dump(p, " r2q %u", d->qh_rate2quantum); 00149 00150 if (d->qh_mask & SCH_HTB_HAS_DEFCLS) { 00151 char buf[32]; 00152 dp_dump(p, " default %s", 00153 rtnl_tc_handle2str(d->qh_defcls, buf, sizeof(buf))); 00154 } 00155 00156 ignore: 00157 return line; 00158 } 00159 00160 static int htb_class_dump_brief(struct rtnl_class *class, 00161 struct nl_dump_params *p, int line) 00162 { 00163 struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata; 00164 00165 if (d == NULL) 00166 goto ignore; 00167 00168 if (d->ch_mask & SCH_HTB_HAS_RATE) { 00169 double r, rbit; 00170 char *ru, *rubit; 00171 00172 r = nl_cancel_down_bytes(d->ch_rate.rs_rate, &ru); 00173 rbit = nl_cancel_down_bits(d->ch_rate.rs_rate*8, &rubit); 00174 00175 dp_dump(p, " rate %.2f%s/s (%.0f%s) log %u", 00176 r, ru, rbit, rubit, 1<<d->ch_rate.rs_cell_log); 00177 } 00178 00179 ignore: 00180 return line; 00181 } 00182 00183 static int htb_class_dump_full(struct rtnl_class *class, 00184 struct nl_dump_params *p, int line) 00185 { 00186 struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata; 00187 00188 if (d == NULL) 00189 goto ignore; 00190 00191 /* line 1 */ 00192 if (d->ch_mask & SCH_HTB_HAS_CEIL) { 00193 double r, rbit; 00194 char *ru, *rubit; 00195 00196 r = nl_cancel_down_bytes(d->ch_ceil.rs_rate, &ru); 00197 rbit = nl_cancel_down_bits(d->ch_ceil.rs_rate*8, &rubit); 00198 00199 dp_dump(p, " ceil %.2f%s/s (%.0f%s) log %u", 00200 r, ru, rbit, rubit, 1<<d->ch_ceil.rs_cell_log); 00201 } 00202 00203 if (d->ch_mask & SCH_HTB_HAS_PRIO) 00204 dp_dump(p, " prio %u", d->ch_prio); 00205 00206 if (d->ch_mask & SCH_HTB_HAS_MTU) 00207 dp_dump(p, " mtu %u", d->ch_mtu); 00208 00209 if (d->ch_mask & SCH_HTB_HAS_RBUFFER) { 00210 double b; 00211 char *bu; 00212 00213 b = nl_cancel_down_bytes(d->ch_rbuffer, &bu); 00214 dp_dump(p, " rbuffer %.2f%s", b, bu); 00215 } 00216 00217 if (d->ch_mask & SCH_HTB_HAS_CBUFFER) { 00218 double b; 00219 char *bu; 00220 00221 b = nl_cancel_down_bytes(d->ch_cbuffer, &bu); 00222 dp_dump(p, " cbuffer %.2f%s", b, bu); 00223 } 00224 00225 if (d->ch_mask & SCH_HTB_HAS_QUANTUM) 00226 dp_dump(p, " quantum %u", d->ch_quantum); 00227 00228 if (d->ch_mask & SCH_HTB_HAS_OVERHEAD) 00229 dp_dump(p, " overhead %u", d->ch_overhead); 00230 00231 if (d->ch_mask & SCH_HTB_HAS_MPU) 00232 dp_dump(p, " mpu %u", d->ch_mpu); 00233 00234 ignore: 00235 return line; 00236 } 00237 00238 static struct nl_msg *htb_qdisc_get_opts(struct rtnl_qdisc *qdisc) 00239 { 00240 struct rtnl_htb_qdisc *d = (struct rtnl_htb_qdisc *) qdisc->q_subdata; 00241 struct tc_htb_glob opts; 00242 struct nl_msg *msg; 00243 00244 if (d == NULL) 00245 return NULL; 00246 00247 msg = nlmsg_alloc(); 00248 if (msg == NULL) 00249 return NULL; 00250 00251 memset(&opts, 0, sizeof(opts)); 00252 opts.version = TC_HTB_PROTOVER; 00253 00254 if (d->qh_mask & SCH_HTB_HAS_RATE2QUANTUM) 00255 opts.rate2quantum = d->qh_rate2quantum; 00256 if (d->qh_mask & SCH_HTB_HAS_DEFCLS) 00257 opts.defcls = d->qh_defcls; 00258 00259 nla_put(msg, TCA_HTB_INIT, sizeof(opts), &opts); 00260 00261 return msg; 00262 } 00263 00264 static uint8_t compute_cell(uint32_t rate, uint32_t mtu) 00265 { 00266 uint8_t cell_log = 0; 00267 while (mtu > 255) { 00268 mtu >>= 1; 00269 cell_log++; 00270 } 00271 00272 return cell_log; 00273 } 00274 00275 static struct nl_msg *htb_class_get_opts(struct rtnl_class *class) 00276 { 00277 struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata; 00278 uint32_t mtu, rtable[RTNL_TC_RTABLE_SIZE], ctable[RTNL_TC_RTABLE_SIZE]; 00279 struct tc_htb_opt opts; 00280 struct nl_msg *msg; 00281 int buffer, cbuffer; 00282 uint8_t overhead = 0, mpu = 0; 00283 00284 if (d == NULL) 00285 return NULL; 00286 00287 msg = nlmsg_alloc(); 00288 memset(&opts, 0, sizeof(opts)); 00289 00290 /* if not set, zero (0) is used as priority */ 00291 if (d->ch_mask & SCH_HTB_HAS_PRIO) 00292 opts.prio = d->ch_prio; 00293 00294 if (d->ch_mask & SCH_HTB_HAS_MTU) 00295 mtu = d->ch_mtu; 00296 else 00297 mtu = 1600; /* eth packet len */ 00298 00299 if (!(d->ch_mask & SCH_HTB_HAS_RATE)) 00300 BUG(); 00301 00302 rtnl_rcopy_ratespec(&opts.rate, &d->ch_rate); 00303 /* if cell_log not set, compute default value */ 00304 if (opts.rate.cell_log == UINT8_MAX) 00305 opts.rate.cell_log = compute_cell(opts.rate.rate, mtu); 00306 00307 /* if not set, configured rate is used as ceil, which implies no borrowing */ 00308 if (d->ch_mask & SCH_HTB_HAS_CEIL) 00309 rtnl_rcopy_ratespec(&opts.ceil, &d->ch_ceil); 00310 else 00311 memcpy(&opts.ceil, &opts.rate, sizeof(struct tc_ratespec)); 00312 /* if cell_log not set, compute default value */ 00313 if (opts.ceil.cell_log == UINT8_MAX) 00314 opts.ceil.cell_log = compute_cell(opts.ceil.rate, mtu); 00315 00316 if (d->ch_mask & SCH_HTB_HAS_RBUFFER) 00317 buffer = d->ch_rbuffer; 00318 else 00319 buffer = opts.rate.rate / nl_get_hz() + mtu; 00320 00321 opts.buffer = rtnl_tc_calc_txtime(buffer, opts.rate.rate); 00322 00323 if (d->ch_mask & SCH_HTB_HAS_CBUFFER) 00324 cbuffer = d->ch_cbuffer; 00325 else 00326 cbuffer = opts.ceil.rate / nl_get_hz() + mtu; 00327 00328 opts.cbuffer = rtnl_tc_calc_txtime(cbuffer, opts.ceil.rate); 00329 00330 if (d->ch_mask & SCH_HTB_HAS_QUANTUM) 00331 opts.quantum = d->ch_quantum; 00332 00333 if (d->ch_mask & SCH_HTB_HAS_OVERHEAD) 00334 overhead = d->ch_overhead; 00335 00336 if (d->ch_mask & SCH_HTB_HAS_MPU) 00337 mpu = d->ch_mpu; 00338 00339 opts.rate.mpu = mpu | (overhead << 8); 00340 opts.ceil.mpu = mpu | (overhead << 8); 00341 00342 nla_put(msg, TCA_HTB_PARMS, sizeof(opts), &opts); 00343 00344 rtnl_tc_build_rate_table(rtable, mpu, overhead, 00345 1 << opts.rate.cell_log, 00346 opts.rate.rate); 00347 nla_put(msg, TCA_HTB_RTAB, sizeof(rtable), &rtable); 00348 00349 rtnl_tc_build_rate_table(ctable, mpu, overhead, 00350 1 << opts.ceil.cell_log, 00351 opts.ceil.rate); 00352 nla_put(msg, TCA_HTB_CTAB, sizeof(ctable), &ctable); 00353 00354 return msg; 00355 } 00356 00357 /** 00358 * @name Attribute Modifications 00359 * @{ 00360 */ 00361 00362 void rtnl_htb_set_rate2quantum(struct rtnl_qdisc *qdisc, uint32_t rate2quantum) 00363 { 00364 struct rtnl_htb_qdisc *d = htb_qdisc(qdisc); 00365 if (d == NULL) 00366 return; 00367 00368 d->qh_rate2quantum = rate2quantum; 00369 d->qh_mask |= SCH_HTB_HAS_RATE2QUANTUM; 00370 } 00371 00372 /** 00373 * Set default class of the htb qdisc to the specified value 00374 * @arg qdisc qdisc to change 00375 * @arg defcls new default class 00376 */ 00377 void rtnl_htb_set_defcls(struct rtnl_qdisc *qdisc, uint32_t defcls) 00378 { 00379 struct rtnl_htb_qdisc *d = htb_qdisc(qdisc); 00380 if (d == NULL) 00381 return; 00382 00383 d->qh_defcls = defcls; 00384 d->qh_mask |= SCH_HTB_HAS_DEFCLS; 00385 } 00386 00387 void rtnl_htb_set_prio(struct rtnl_class *class, uint32_t prio) 00388 { 00389 struct rtnl_htb_class *d = htb_class(class); 00390 if (d == NULL) 00391 return; 00392 00393 d->ch_prio = prio; 00394 d->ch_mask |= SCH_HTB_HAS_PRIO; 00395 } 00396 00397 /** 00398 * Set MTU of the data link. 00399 * @arg class HTB class to be modified. 00400 * @arg mtu New MTU in bytes. 00401 * 00402 * Sets MTU of the data link controlled by the HTB class. 00403 * If not set, the Ethernet MTU (1600) is used. 00404 */ 00405 void rtnl_htb_set_mtu(struct rtnl_class *class, uint32_t mtu) 00406 { 00407 struct rtnl_htb_class *d = htb_class(class); 00408 if (d == NULL) 00409 return; 00410 00411 d->ch_mtu = mtu; 00412 d->ch_mask |= SCH_HTB_HAS_MTU; 00413 } 00414 00415 /** 00416 * Set rate of HTB class. 00417 * @arg class HTB class to be modified. 00418 * @arg rate New rate in bytes per second. 00419 */ 00420 void rtnl_htb_set_rate(struct rtnl_class *class, uint32_t rate) 00421 { 00422 struct rtnl_htb_class *d = htb_class(class); 00423 if (d == NULL) 00424 return; 00425 00426 d->ch_rate.rs_cell_log = UINT8_MAX; /* use default value */ 00427 d->ch_rate.rs_rate = rate; 00428 d->ch_mask |= SCH_HTB_HAS_RATE; 00429 } 00430 00431 /** 00432 * Set ceil of HTB class. 00433 * @arg class HTB class to be modified. 00434 * @arg ceil New ceil in bytes per second. 00435 */ 00436 void rtnl_htb_set_ceil(struct rtnl_class *class, uint32_t ceil) 00437 { 00438 struct rtnl_htb_class *d = htb_class(class); 00439 if (d == NULL) 00440 return; 00441 00442 d->ch_ceil.rs_cell_log = UINT8_MAX; /* use default value */ 00443 d->ch_ceil.rs_rate = ceil; 00444 d->ch_mask |= SCH_HTB_HAS_CEIL; 00445 } 00446 00447 /** 00448 * Set size of the rate bucket of HTB class. 00449 * @arg class HTB class to be modified. 00450 * @arg rbuffer New size in bytes. 00451 */ 00452 void rtnl_htb_set_rbuffer(struct rtnl_class *class, uint32_t rbuffer) 00453 { 00454 struct rtnl_htb_class *d = htb_class(class); 00455 if (d == NULL) 00456 return; 00457 00458 d->ch_rbuffer = rbuffer; 00459 d->ch_mask |= SCH_HTB_HAS_RBUFFER; 00460 } 00461 00462 /** 00463 * Set size of the ceil bucket of HTB class. 00464 * @arg class HTB class to be modified. 00465 * @arg cbuffer New size in bytes. 00466 */ 00467 void rtnl_htb_set_cbuffer(struct rtnl_class *class, uint32_t cbuffer) 00468 { 00469 struct rtnl_htb_class *d = htb_class(class); 00470 if (d == NULL) 00471 return; 00472 00473 d->ch_cbuffer = cbuffer; 00474 d->ch_mask |= SCH_HTB_HAS_CBUFFER; 00475 } 00476 00477 /** 00478 * Set how much bytes to serve from leaf at once of HTB class {use r2q}. 00479 * @arg class HTB class to be modified. 00480 * @arg quantum New size in bytes. 00481 */ 00482 void rtnl_htb_set_quantum(struct rtnl_class *class, uint32_t quantum) 00483 { 00484 struct rtnl_htb_class *d = htb_class(class); 00485 if (d == NULL) 00486 return; 00487 00488 d->ch_quantum = quantum; 00489 d->ch_mask |= SCH_HTB_HAS_QUANTUM; 00490 } 00491 00492 /** 00493 * Set per-packet size overhead used in rate computations of HTB class. 00494 * @arg class HTB class to be modified. 00495 * @arg overhead Size in bytes. 00496 */ 00497 void rtnl_htb_set_overhead(struct rtnl_class *class, uint8_t overhead) 00498 { 00499 struct rtnl_htb_class *d = htb_class(class); 00500 if (d == NULL) 00501 return; 00502 00503 d->ch_overhead = overhead; 00504 d->ch_mask |= SCH_HTB_HAS_OVERHEAD; 00505 } 00506 00507 /** 00508 * Set the minimum packet size used in rate computations of HTB class. 00509 * @arg class HTB class to be modified. 00510 * @arg mpu Size in bytes. 00511 */ 00512 void rtnl_htb_set_mpu(struct rtnl_class *class, uint8_t mpu) 00513 { 00514 struct rtnl_htb_class *d = htb_class(class); 00515 if (d == NULL) 00516 return; 00517 00518 d->ch_mpu = mpu; 00519 d->ch_mask |= SCH_HTB_HAS_MPU; 00520 } 00521 00522 /** @} */ 00523 00524 static struct rtnl_qdisc_ops htb_qdisc_ops = { 00525 .qo_kind = "htb", 00526 .qo_msg_parser = htb_qdisc_msg_parser, 00527 .qo_free_data = htb_qdisc_free_data, 00528 .qo_dump[NL_DUMP_BRIEF] = htb_qdisc_dump_brief, 00529 .qo_get_opts = htb_qdisc_get_opts, 00530 }; 00531 00532 static struct rtnl_class_ops htb_class_ops = { 00533 .co_kind = "htb", 00534 .co_msg_parser = htb_class_msg_parser, 00535 .co_free_data = htb_class_free_data, 00536 .co_dump[NL_DUMP_BRIEF] = htb_class_dump_brief, 00537 .co_dump[NL_DUMP_FULL] = htb_class_dump_full, 00538 .co_get_opts = htb_class_get_opts, 00539 }; 00540 00541 static void __init htb_init(void) 00542 { 00543 rtnl_qdisc_register(&htb_qdisc_ops); 00544 rtnl_class_register(&htb_class_ops); 00545 } 00546 00547 static void __exit htb_exit(void) 00548 { 00549 rtnl_qdisc_unregister(&htb_qdisc_ops); 00550 rtnl_class_unregister(&htb_class_ops); 00551 } 00552 00553 /** @} */