1 /* 2 * lib/route/sch/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-2008 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_api 16 * @ingroup class_api 17 * @defgroup htb Hierachical Token Bucket (HTB) 18 * @{ 19 */ 20 21 #include <netlink-local.h> 22 #include <netlink-tc.h> 23 #include <netlink/netlink.h> 24 #include <netlink/cache.h> 25 #include <netlink/utils.h> 26 #include <netlink/route/tc.h> 27 #include <netlink/route/qdisc.h> 28 #include <netlink/route/qdisc-modules.h> 29 #include <netlink/route/class.h> 30 #include <netlink/route/class-modules.h> 31 #include <netlink/route/link.h> 32 #include <netlink/route/sch/htb.h> 33 34 /** @cond SKIP */ 35 #define SCH_HTB_HAS_RATE2QUANTUM 0x01 36 #define SCH_HTB_HAS_DEFCLS 0x02 37 38 #define SCH_HTB_HAS_PRIO 0x001 39 #define SCH_HTB_HAS_MTU 0x002 40 #define SCH_HTB_HAS_RATE 0x004 41 #define SCH_HTB_HAS_CEIL 0x008 42 #define SCH_HTB_HAS_RBUFFER 0x010 43 #define SCH_HTB_HAS_CBUFFER 0x020 44 #define SCH_HTB_HAS_QUANTUM 0x040 45 #define SCH_HTB_HAS_OVERHEAD 0x080 46 #define SCH_HTB_HAS_MPU 0x100 47 /** @endcond */ 48 49 static inline struct rtnl_htb_qdisc *htb_qdisc(struct rtnl_qdisc *qdisc) 50 { 51 if (qdisc->q_subdata == NULL) 52 qdisc->q_subdata = calloc(1, sizeof(struct rtnl_htb_qdisc)); 53 54 return (struct rtnl_htb_qdisc *) qdisc->q_subdata; 55 } 56 57 static struct nla_policy htb_policy[TCA_HTB_MAX+1] = { 58 [TCA_HTB_INIT] = { .minlen = sizeof(struct tc_htb_glob) }, 59 [TCA_HTB_PARMS] = { .minlen = sizeof(struct tc_htb_opt) }, 60 }; 61 62 static int htb_qdisc_msg_parser(struct rtnl_qdisc *qdisc) 63 { 64 int err; 65 struct nlattr *tb[TCA_HTB_MAX + 1]; 66 struct rtnl_htb_qdisc *d; 67 68 err = tca_parse(tb, TCA_HTB_MAX, (struct rtnl_tca *) qdisc, htb_policy); 69 if (err < 0) 70 return err; 71 72 d = htb_qdisc(qdisc); 73 74 if (tb[TCA_HTB_INIT]) { 75 struct tc_htb_glob opts; 76 77 nla_memcpy(&opts, tb[TCA_HTB_INIT], sizeof(opts)); 78 d->qh_rate2quantum = opts.rate2quantum; 79 d->qh_defcls = opts.defcls; 80 81 d->qh_mask = (SCH_HTB_HAS_RATE2QUANTUM | SCH_HTB_HAS_DEFCLS); 82 } 83 84 return 0; 85 } 86 87 static void htb_qdisc_free_data(struct rtnl_qdisc *qdisc) 88 { 89 free(qdisc->q_subdata); 90 } 91 92 static inline struct rtnl_htb_class *htb_class(struct rtnl_class *class) 93 { 94 if (class->c_subdata == NULL) 95 class->c_subdata = calloc(1, sizeof(struct rtnl_htb_class)); 96 97 return (struct rtnl_htb_class *) class->c_subdata; 98 } 99 100 static int htb_class_msg_parser(struct rtnl_class *class) 101 { 102 int err; 103 struct nlattr *tb[TCA_HTB_MAX + 1]; 104 struct rtnl_htb_class *d; 105 106 err = tca_parse(tb, TCA_HTB_MAX, (struct rtnl_tca *) class, htb_policy); 107 if (err < 0) 108 return err; 109 110 d = htb_class(class); 111 112 if (tb[TCA_HTB_PARMS]) { 113 struct tc_htb_opt opts; 114 115 nla_memcpy(&opts, tb[TCA_HTB_PARMS], sizeof(opts)); 116 d->ch_prio = opts.prio; 117 rtnl_copy_ratespec(&d->ch_rate, &opts.rate); 118 rtnl_copy_ratespec(&d->ch_ceil, &opts.ceil); 119 d->ch_rbuffer = rtnl_tc_calc_bufsize(opts.buffer, opts.rate.rate); 120 d->ch_cbuffer = rtnl_tc_calc_bufsize(opts.cbuffer, opts.ceil.rate); 121 d->ch_quantum = opts.quantum; 122 d->ch_overhead = (opts.rate.mpu >> 8) & 0xff; 123 d->ch_mpu = opts.rate.mpu & 0xff; 124 125 d->ch_mask = (SCH_HTB_HAS_PRIO | SCH_HTB_HAS_RATE | 126 SCH_HTB_HAS_CEIL | SCH_HTB_HAS_RBUFFER | 127 SCH_HTB_HAS_CBUFFER | SCH_HTB_HAS_QUANTUM | 128 SCH_HTB_HAS_OVERHEAD | SCH_HTB_HAS_MPU); 129 } 130 131 return 0; 132 } 133 134 static void htb_class_free_data(struct rtnl_class *class) 135 { 136 free(class->c_subdata); 137 } 138 139 static void htb_qdisc_dump_line(struct rtnl_qdisc *qdisc, 140 struct nl_dump_params *p) 141 { 142 struct rtnl_htb_qdisc *d = (struct rtnl_htb_qdisc *) qdisc->q_subdata; 143 144 if (d == NULL) 145 return; 146 147 if (d->qh_mask & SCH_HTB_HAS_RATE2QUANTUM) 148 nl_dump(p, " r2q %u", d->qh_rate2quantum); 149 150 if (d->qh_mask & SCH_HTB_HAS_DEFCLS) { 151 char buf[32]; 152 nl_dump(p, " default %s", 153 rtnl_tc_handle2str(d->qh_defcls, buf, sizeof(buf))); 154 } 155 } 156 157 static void htb_class_dump_line(struct rtnl_class *class, 158 struct nl_dump_params *p) 159 { 160 struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata; 161 162 if (d == NULL) 163 return; 164 165 if (d->ch_mask & SCH_HTB_HAS_RATE) { 166 double r, rbit; 167 char *ru, *rubit; 168 169 r = nl_cancel_down_bytes(d->ch_rate.rs_rate, &ru); 170 rbit = nl_cancel_down_bits(d->ch_rate.rs_rate*8, &rubit); 171 172 nl_dump(p, " rate %.2f%s/s (%.0f%s) log %u", 173 r, ru, rbit, rubit, 1<<d->ch_rate.rs_cell_log); 174 } 175 } 176 177 static void htb_class_dump_details(struct rtnl_class *class, 178 struct nl_dump_params *p) 179 { 180 struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata; 181 182 if (d == NULL) 183 return; 184 185 /* line 1 */ 186 if (d->ch_mask & SCH_HTB_HAS_CEIL) { 187 double r, rbit; 188 char *ru, *rubit; 189 190 r = nl_cancel_down_bytes(d->ch_ceil.rs_rate, &ru); 191 rbit = nl_cancel_down_bits(d->ch_ceil.rs_rate*8, &rubit); 192 193 nl_dump(p, " ceil %.2f%s/s (%.0f%s) log %u", 194 r, ru, rbit, rubit, 1<<d->ch_ceil.rs_cell_log); 195 } 196 197 if (d->ch_mask & SCH_HTB_HAS_PRIO) 198 nl_dump(p, " prio %u", d->ch_prio); 199 200 if (d->ch_mask & SCH_HTB_HAS_MTU) 201 nl_dump(p, " mtu %u", d->ch_mtu); 202 203 if (d->ch_mask & SCH_HTB_HAS_RBUFFER) { 204 double b; 205 char *bu; 206 207 b = nl_cancel_down_bytes(d->ch_rbuffer, &bu); 208 nl_dump(p, " rbuffer %.2f%s", b, bu); 209 } 210 211 if (d->ch_mask & SCH_HTB_HAS_CBUFFER) { 212 double b; 213 char *bu; 214 215 b = nl_cancel_down_bytes(d->ch_cbuffer, &bu); 216 nl_dump(p, " cbuffer %.2f%s", b, bu); 217 } 218 219 if (d->ch_mask & SCH_HTB_HAS_QUANTUM) 220 nl_dump(p, " quantum %u", d->ch_quantum); 221 222 if (d->ch_mask & SCH_HTB_HAS_OVERHEAD) 223 nl_dump(p, " overhead %u", d->ch_overhead); 224 225 if (d->ch_mask & SCH_HTB_HAS_MPU) 226 nl_dump(p, " mpu %u", d->ch_mpu); 227 } 228 229 static struct nl_msg *htb_qdisc_get_opts(struct rtnl_qdisc *qdisc) 230 { 231 struct rtnl_htb_qdisc *d = (struct rtnl_htb_qdisc *) qdisc->q_subdata; 232 struct tc_htb_glob opts; 233 struct nl_msg *msg; 234 235 if (d == NULL) 236 return NULL; 237 238 msg = nlmsg_alloc(); 239 if (msg == NULL) 240 return NULL; 241 242 memset(&opts, 0, sizeof(opts)); 243 opts.version = TC_HTB_PROTOVER; 244 245 if (d->qh_mask & SCH_HTB_HAS_RATE2QUANTUM) 246 opts.rate2quantum = d->qh_rate2quantum; 247 if (d->qh_mask & SCH_HTB_HAS_DEFCLS) 248 opts.defcls = d->qh_defcls; 249 250 nla_put(msg, TCA_HTB_INIT, sizeof(opts), &opts); 251 252 return msg; 253 } 254 255 static uint8_t compute_cell(uint32_t rate, uint32_t mtu) 256 { 257 uint8_t cell_log = 0; 258 while (mtu > 255) { 259 mtu >>= 1; 260 cell_log++; 261 } 262 263 return cell_log; 264 } 265 266 static struct nl_msg *htb_class_get_opts(struct rtnl_class *class) 267 { 268 struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata; 269 uint32_t mtu, rtable[RTNL_TC_RTABLE_SIZE], ctable[RTNL_TC_RTABLE_SIZE]; 270 struct tc_htb_opt opts; 271 struct nl_msg *msg; 272 int buffer, cbuffer; 273 uint8_t overhead = 0, mpu = 0; 274 275 if (d == NULL) 276 return NULL; 277 278 msg = nlmsg_alloc(); 279 memset(&opts, 0, sizeof(opts)); 280 281 /* if not set, zero (0) is used as priority */ 282 if (d->ch_mask & SCH_HTB_HAS_PRIO) 283 opts.prio = d->ch_prio; 284 285 if (d->ch_mask & SCH_HTB_HAS_MTU) 286 mtu = d->ch_mtu; 287 else 288 mtu = 1600; /* eth packet len */ 289 290 if (!(d->ch_mask & SCH_HTB_HAS_RATE)) 291 BUG(); 292 293 rtnl_rcopy_ratespec(&opts.rate, &d->ch_rate); 294 /* if cell_log not set, compute default value */ 295 if (opts.rate.cell_log == UINT8_MAX) 296 opts.rate.cell_log = compute_cell(opts.rate.rate, mtu); 297 298 /* if not set, configured rate is used as ceil, which implies no borrowing */ 299 if (d->ch_mask & SCH_HTB_HAS_CEIL) 300 rtnl_rcopy_ratespec(&opts.ceil, &d->ch_ceil); 301 else 302 memcpy(&opts.ceil, &opts.rate, sizeof(struct tc_ratespec)); 303 /* if cell_log not set, compute default value */ 304 if (opts.ceil.cell_log == UINT8_MAX) 305 opts.ceil.cell_log = compute_cell(opts.ceil.rate, mtu); 306 307 if (d->ch_mask & SCH_HTB_HAS_RBUFFER) 308 buffer = d->ch_rbuffer; 309 else 310 buffer = opts.rate.rate / nl_get_hz() + mtu; 311 312 opts.buffer = rtnl_tc_calc_txtime(buffer, opts.rate.rate); 313 314 if (d->ch_mask & SCH_HTB_HAS_CBUFFER) 315 cbuffer = d->ch_cbuffer; 316 else 317 cbuffer = opts.ceil.rate / nl_get_hz() + mtu; 318 319 opts.cbuffer = rtnl_tc_calc_txtime(cbuffer, opts.ceil.rate); 320 321 if (d->ch_mask & SCH_HTB_HAS_QUANTUM) 322 opts.quantum = d->ch_quantum; 323 324 if (d->ch_mask & SCH_HTB_HAS_OVERHEAD) 325 overhead = d->ch_overhead; 326 327 if (d->ch_mask & SCH_HTB_HAS_MPU) 328 mpu = d->ch_mpu; 329 330 opts.rate.mpu = mpu | (overhead << 8); 331 opts.ceil.mpu = mpu | (overhead << 8); 332 333 nla_put(msg, TCA_HTB_PARMS, sizeof(opts), &opts); 334 335 rtnl_tc_build_rate_table(rtable, mpu, overhead, 336 1 << opts.rate.cell_log, 337 opts.rate.rate); 338 nla_put(msg, TCA_HTB_RTAB, sizeof(rtable), &rtable); 339 340 rtnl_tc_build_rate_table(ctable, mpu, overhead, 341 1 << opts.ceil.cell_log, 342 opts.ceil.rate); 343 nla_put(msg, TCA_HTB_CTAB, sizeof(ctable), &ctable); 344 345 return msg; 346 } 347 348 /** 349 * @name Attribute Modifications 350 * @{ 351 */ 352 353 void rtnl_htb_set_rate2quantum(struct rtnl_qdisc *qdisc, uint32_t rate2quantum) 354 { 355 struct rtnl_htb_qdisc *d = htb_qdisc(qdisc); 356 if (d == NULL) 357 return; 358 359 d->qh_rate2quantum = rate2quantum; 360 d->qh_mask |= SCH_HTB_HAS_RATE2QUANTUM; 361 } 362 363 /** 364 * Set default class of the htb qdisc to the specified value 365 * @arg qdisc qdisc to change 366 * @arg defcls new default class 367 */ 368 void rtnl_htb_set_defcls(struct rtnl_qdisc *qdisc, uint32_t defcls) 369 { 370 struct rtnl_htb_qdisc *d = htb_qdisc(qdisc); 371 if (d == NULL) 372 return; 373 374 d->qh_defcls = defcls; 375 d->qh_mask |= SCH_HTB_HAS_DEFCLS; 376 } 377 378 void rtnl_htb_set_prio(struct rtnl_class *class, uint32_t prio) 379 { 380 struct rtnl_htb_class *d = htb_class(class); 381 if (d == NULL) 382 return; 383 384 d->ch_prio = prio; 385 d->ch_mask |= SCH_HTB_HAS_PRIO; 386 } 387 388 /** 389 * Set MTU of the data link. 390 * @arg class HTB class to be modified. 391 * @arg mtu New MTU in bytes. 392 * 393 * Sets MTU of the data link controlled by the HTB class. 394 * If not set, the Ethernet MTU (1600) is used. 395 */ 396 void rtnl_htb_set_mtu(struct rtnl_class *class, uint32_t mtu) 397 { 398 struct rtnl_htb_class *d = htb_class(class); 399 if (d == NULL) 400 return; 401 402 d->ch_mtu = mtu; 403 d->ch_mask |= SCH_HTB_HAS_MTU; 404 } 405 406 /** 407 * Set rate of HTB class. 408 * @arg class HTB class to be modified. 409 * @arg rate New rate in bytes per second. 410 */ 411 void rtnl_htb_set_rate(struct rtnl_class *class, uint32_t rate) 412 { 413 struct rtnl_htb_class *d = htb_class(class); 414 if (d == NULL) 415 return; 416 417 d->ch_rate.rs_cell_log = UINT8_MAX; /* use default value */ 418 d->ch_rate.rs_rate = rate; 419 d->ch_mask |= SCH_HTB_HAS_RATE; 420 } 421 422 /** 423 * Set ceil of HTB class. 424 * @arg class HTB class to be modified. 425 * @arg ceil New ceil in bytes per second. 426 */ 427 void rtnl_htb_set_ceil(struct rtnl_class *class, uint32_t ceil) 428 { 429 struct rtnl_htb_class *d = htb_class(class); 430 if (d == NULL) 431 return; 432 433 d->ch_ceil.rs_cell_log = UINT8_MAX; /* use default value */ 434 d->ch_ceil.rs_rate = ceil; 435 d->ch_mask |= SCH_HTB_HAS_CEIL; 436 } 437 438 /** 439 * Set size of the rate bucket of HTB class. 440 * @arg class HTB class to be modified. 441 * @arg rbuffer New size in bytes. 442 */ 443 void rtnl_htb_set_rbuffer(struct rtnl_class *class, uint32_t rbuffer) 444 { 445 struct rtnl_htb_class *d = htb_class(class); 446 if (d == NULL) 447 return; 448 449 d->ch_rbuffer = rbuffer; 450 d->ch_mask |= SCH_HTB_HAS_RBUFFER; 451 } 452 453 /** 454 * Set size of the ceil bucket of HTB class. 455 * @arg class HTB class to be modified. 456 * @arg cbuffer New size in bytes. 457 */ 458 void rtnl_htb_set_cbuffer(struct rtnl_class *class, uint32_t cbuffer) 459 { 460 struct rtnl_htb_class *d = htb_class(class); 461 if (d == NULL) 462 return; 463 464 d->ch_cbuffer = cbuffer; 465 d->ch_mask |= SCH_HTB_HAS_CBUFFER; 466 } 467 468 /** 469 * Set how much bytes to serve from leaf at once of HTB class {use r2q}. 470 * @arg class HTB class to be modified. 471 * @arg quantum New size in bytes. 472 */ 473 void rtnl_htb_set_quantum(struct rtnl_class *class, uint32_t quantum) 474 { 475 struct rtnl_htb_class *d = htb_class(class); 476 if (d == NULL) 477 return; 478 479 d->ch_quantum = quantum; 480 d->ch_mask |= SCH_HTB_HAS_QUANTUM; 481 } 482 483 /** 484 * Set per-packet size overhead used in rate computations of HTB class. 485 * @arg class HTB class to be modified. 486 * @arg overhead Size in bytes. 487 */ 488 void rtnl_htb_set_overhead(struct rtnl_class *class, uint8_t overhead) 489 { 490 struct rtnl_htb_class *d = htb_class(class); 491 if (d == NULL) 492 return; 493 494 d->ch_overhead = overhead; 495 d->ch_mask |= SCH_HTB_HAS_OVERHEAD; 496 } 497 498 /** 499 * Set the minimum packet size used in rate computations of HTB class. 500 * @arg class HTB class to be modified. 501 * @arg mpu Size in bytes. 502 */ 503 void rtnl_htb_set_mpu(struct rtnl_class *class, uint8_t mpu) 504 { 505 struct rtnl_htb_class *d = htb_class(class); 506 if (d == NULL) 507 return; 508 509 d->ch_mpu = mpu; 510 d->ch_mask |= SCH_HTB_HAS_MPU; 511 } 512 513 /** @} */ 514 515 static struct rtnl_qdisc_ops htb_qdisc_ops = { 516 .qo_kind = "htb", 517 .qo_msg_parser = htb_qdisc_msg_parser, 518 .qo_free_data = htb_qdisc_free_data, 519 .qo_dump[NL_DUMP_LINE] = htb_qdisc_dump_line, 520 .qo_get_opts = htb_qdisc_get_opts, 521 }; 522 523 static struct rtnl_class_ops htb_class_ops = { 524 .co_kind = "htb", 525 .co_msg_parser = htb_class_msg_parser, 526 .co_free_data = htb_class_free_data, 527 .co_dump = { 528 [NL_DUMP_LINE] = htb_class_dump_line, 529 [NL_DUMP_DETAILS] = htb_class_dump_details, 530 }, 531 .co_get_opts = htb_class_get_opts, 532 }; 533 534 static void __init htb_init(void) 535 { 536 rtnl_qdisc_register(&htb_qdisc_ops); 537 rtnl_class_register(&htb_class_ops); 538 } 539 540 static void __exit htb_exit(void) 541 { 542 rtnl_qdisc_unregister(&htb_qdisc_ops); 543 rtnl_class_unregister(&htb_class_ops); 544 } 545 546 /** @} */ 547