1/* 2 * lib/route/sch/tbf.c TBF 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@suug.ch> 10 */ 11 12/** 13 * @ingroup qdisc_api 14 * @defgroup tbf Token Bucket Filter (TBF) 15 * @{ 16 */ 17 18#include <netlink-local.h> 19#include <netlink-tc.h> 20#include <netlink/netlink.h> 21#include <netlink/cache.h> 22#include <netlink/utils.h> 23#include <netlink/route/tc.h> 24#include <netlink/route/qdisc.h> 25#include <netlink/route/qdisc-modules.h> 26#include <netlink/route/class.h> 27#include <netlink/route/class-modules.h> 28#include <netlink/route/link.h> 29#include <netlink/route/sch/tbf.h> 30 31/** @cond SKIP */ 32#define TBF_ATTR_LIMIT 0x01 33#define TBF_ATTR_RATE 0x02 34#define TBF_ATTR_PEAKRATE 0x10 35#define TBF_ATTR_MPU 0x80 36/** @endcond */ 37 38static inline struct rtnl_tbf *tbf_qdisc(struct rtnl_qdisc *qdisc) 39{ 40 return (struct rtnl_tbf *) qdisc->q_subdata; 41} 42 43static inline struct rtnl_tbf *tbf_alloc(struct rtnl_qdisc *qdisc) 44{ 45 if (!qdisc->q_subdata) 46 qdisc->q_subdata = calloc(1, sizeof(struct rtnl_tbf)); 47 48 return tbf_qdisc(qdisc); 49} 50 51static struct nla_policy tbf_policy[TCA_TBF_MAX+1] = { 52 [TCA_TBF_PARMS] = { .minlen = sizeof(struct tc_tbf_qopt) }, 53}; 54 55static int tbf_msg_parser(struct rtnl_qdisc *q) 56{ 57 int err; 58 struct nlattr *tb[TCA_TBF_MAX + 1]; 59 struct rtnl_tbf *tbf; 60 61 err = tca_parse(tb, TCA_TBF_MAX, (struct rtnl_tca *) q, tbf_policy); 62 if (err < 0) 63 return err; 64 65 tbf = tbf_alloc(q); 66 if (!tbf) 67 return -NLE_NOMEM; 68 69 if (tb[TCA_TBF_PARMS]) { 70 struct tc_tbf_qopt opts; 71 int bufsize; 72 73 nla_memcpy(&opts, tb[TCA_TBF_PARMS], sizeof(opts)); 74 tbf->qt_limit = opts.limit; 75 tbf->qt_mpu = opts.rate.mpu; 76 77 rtnl_copy_ratespec(&tbf->qt_rate, &opts.rate); 78 tbf->qt_rate_txtime = opts.buffer; 79 bufsize = rtnl_tc_calc_bufsize(nl_ticks2us(opts.buffer), 80 opts.rate.rate); 81 tbf->qt_rate_bucket = bufsize; 82 83 rtnl_copy_ratespec(&tbf->qt_peakrate, &opts.peakrate); 84 tbf->qt_peakrate_txtime = opts.mtu; 85 bufsize = rtnl_tc_calc_bufsize(nl_ticks2us(opts.mtu), 86 opts.peakrate.rate); 87 tbf->qt_peakrate_bucket = bufsize; 88 89 tbf->qt_mask = (TBF_ATTR_LIMIT | TBF_ATTR_MPU | TBF_ATTR_RATE | 90 TBF_ATTR_PEAKRATE); 91 } 92 93 return 0; 94} 95 96static void tbf_free_data(struct rtnl_qdisc *qdisc) 97{ 98 free(qdisc->q_subdata); 99} 100 101static void tbf_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p) 102{ 103 double r, rbit, lim; 104 char *ru, *rubit, *limu; 105 struct rtnl_tbf *tbf = tbf_qdisc(qdisc); 106 107 if (!tbf) 108 return; 109 110 r = nl_cancel_down_bytes(tbf->qt_rate.rs_rate, &ru); 111 rbit = nl_cancel_down_bits(tbf->qt_rate.rs_rate*8, &rubit); 112 lim = nl_cancel_down_bytes(tbf->qt_limit, &limu); 113 114 nl_dump(p, " rate %.2f%s/s (%.0f%s) limit %.2f%s", 115 r, ru, rbit, rubit, lim, limu); 116} 117 118static void tbf_dump_details(struct rtnl_qdisc *qdisc, struct nl_dump_params *p) 119{ 120 struct rtnl_tbf *tbf = tbf_qdisc(qdisc); 121 122 if (!tbf) 123 return; 124 125 if (1) { 126 char *bu, *cu; 127 double bs = nl_cancel_down_bytes(tbf->qt_rate_bucket, &bu); 128 double cl = nl_cancel_down_bytes(1 << tbf->qt_rate.rs_cell_log, 129 &cu); 130 131 nl_dump(p, "mpu %u rate-bucket-size %1.f%s " 132 "rate-cell-size %.1f%s\n", 133 tbf->qt_mpu, bs, bu, cl, cu); 134 135 } 136 137 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) { 138 char *pru, *prbu, *bsu, *clu; 139 double pr, prb, bs, cl; 140 141 pr = nl_cancel_down_bytes(tbf->qt_peakrate.rs_rate, &pru); 142 prb = nl_cancel_down_bits(tbf->qt_peakrate.rs_rate * 8, &prbu); 143 bs = nl_cancel_down_bits(tbf->qt_peakrate_bucket, &bsu); 144 cl = nl_cancel_down_bits(1 << tbf->qt_peakrate.rs_cell_log, 145 &clu); 146 147 nl_dump_line(p, " peak-rate %.2f%s/s (%.0f%s) " 148 "bucket-size %.1f%s cell-size %.1f%s" 149 "latency %.1f%s", 150 pr, pru, prb, prbu, bs, bsu, cl, clu); 151 } 152} 153 154static struct nl_msg *tbf_get_opts(struct rtnl_qdisc *qdisc) 155{ 156 struct tc_tbf_qopt opts; 157 struct rtnl_tbf *tbf; 158 struct nl_msg *msg; 159 uint32_t rtab[RTNL_TC_RTABLE_SIZE]; 160 uint32_t ptab[RTNL_TC_RTABLE_SIZE]; 161 int required = TBF_ATTR_RATE | TBF_ATTR_LIMIT; 162 163 memset(&opts, 0, sizeof(opts)); 164 165 tbf = tbf_qdisc(qdisc); 166 if (!tbf) 167 return NULL; 168 169 if (!(tbf->qt_mask & required) != required) 170 return NULL; 171 172 opts.limit = tbf->qt_limit; 173 opts.buffer = tbf->qt_rate_txtime; 174 tbf->qt_rate.rs_mpu = tbf->qt_mpu; 175 rtnl_rcopy_ratespec(&opts.rate, &tbf->qt_rate); 176 177 rtnl_tc_build_rate_table(rtab, tbf->qt_mpu & 0xff, tbf->qt_mpu >> 8, 178 1 << tbf->qt_rate.rs_cell_log, 179 tbf->qt_rate.rs_rate); 180 181 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) { 182 opts.mtu = tbf->qt_peakrate_txtime; 183 tbf->qt_peakrate.rs_mpu = tbf->qt_mpu; 184 rtnl_rcopy_ratespec(&opts.peakrate, &tbf->qt_peakrate); 185 186 rtnl_tc_build_rate_table(ptab, tbf->qt_mpu & 0xff, 187 tbf->qt_mpu >> 8, 188 1 << tbf->qt_peakrate.rs_cell_log, 189 tbf->qt_peakrate.rs_rate); 190 } 191 192 msg = nlmsg_alloc(); 193 if (!msg) 194 goto nla_put_failure; 195 196 NLA_PUT(msg, TCA_TBF_PARMS, sizeof(opts), &opts); 197 NLA_PUT(msg, TCA_TBF_RTAB, sizeof(rtab), rtab); 198 199 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) 200 NLA_PUT(msg, TCA_TBF_PTAB, sizeof(ptab), ptab); 201 202 return msg; 203 204nla_put_failure: 205 nlmsg_free(msg); 206 return NULL; 207} 208 209/** 210 * @name Attribute Access 211 * @{ 212 */ 213 214/** 215 * Set limit of TBF qdisc. 216 * @arg qdisc TBF qdisc to be modified. 217 * @arg limit New limit in bytes. 218 * @return 0 on success or a negative error code. 219 */ 220int rtnl_qdisc_tbf_set_limit(struct rtnl_qdisc *qdisc, int limit) 221{ 222 struct rtnl_tbf *tbf; 223 224 tbf = tbf_alloc(qdisc); 225 if (!tbf) 226 return -NLE_NOMEM; 227 228 tbf->qt_limit = limit; 229 tbf->qt_mask |= TBF_ATTR_LIMIT; 230 231 return 0; 232} 233 234static inline double calc_limit(struct rtnl_ratespec *spec, int latency, 235 int bucket) 236{ 237 double limit; 238 239 limit = (double) spec->rs_rate * ((double) latency / 1000000.); 240 limit += bucket; 241 242 return limit; 243} 244 245/** 246 * Set limit of TBF qdisc by latency. 247 * @arg qdisc TBF qdisc to be modified. 248 * @arg latency Latency in micro seconds. 249 * 250 * Calculates and sets the limit based on the desired latency and the 251 * configured rate and peak rate. In order for this operation to succeed, 252 * the rate and if required the peak rate must have been set in advance. 253 * 254 * @f[ 255 * limit_n = \frac{{rate_n} \times {latency}}{10^6}+{bucketsize}_n 256 * @f] 257 * @f[ 258 * limit = min(limit_{rate},limit_{peak}) 259 * @f] 260 * 261 * @return 0 on success or a negative error code. 262 */ 263int rtnl_qdisc_tbf_set_limit_by_latency(struct rtnl_qdisc *qdisc, int latency) 264{ 265 struct rtnl_tbf *tbf; 266 double limit, limit2; 267 268 tbf = tbf_alloc(qdisc); 269 if (!tbf) 270 return -NLE_NOMEM; 271 272 if (!(tbf->qt_mask & TBF_ATTR_RATE)) 273 return -NLE_MISSING_ATTR; 274 275 limit = calc_limit(&tbf->qt_rate, latency, tbf->qt_rate_bucket); 276 277 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) { 278 limit2 = calc_limit(&tbf->qt_peakrate, latency, 279 tbf->qt_peakrate_bucket); 280 281 if (limit2 < limit) 282 limit = limit2; 283 } 284 285 return rtnl_qdisc_tbf_set_limit(qdisc, (int) limit); 286} 287 288/** 289 * Get limit of TBF qdisc. 290 * @arg qdisc TBF qdisc. 291 * @return Limit in bytes or a negative error code. 292 */ 293int rtnl_qdisc_tbf_get_limit(struct rtnl_qdisc *qdisc) 294{ 295 struct rtnl_tbf *tbf; 296 297 tbf = tbf_qdisc(qdisc); 298 if (tbf && (tbf->qt_mask & TBF_ATTR_LIMIT)) 299 return tbf->qt_limit; 300 else 301 return -NLE_NOATTR; 302} 303 304/** 305 * Set MPU of TBF qdisc. 306 * @arg qdisc TBF qdisc to be modified. 307 * @arg mpu New MPU in bytes. 308 * @return 0 on success or a negative error code. 309 */ 310int rtnl_qdisc_tbf_set_mpu(struct rtnl_qdisc *qdisc, int mpu) 311{ 312 struct rtnl_tbf *tbf; 313 314 tbf = tbf_alloc(qdisc); 315 if (!tbf) 316 return -NLE_NOMEM; 317 318 tbf->qt_mpu = mpu; 319 tbf->qt_mask |= TBF_ATTR_MPU; 320 321 return 0; 322} 323 324/** 325 * Get MPU of TBF qdisc. 326 * @arg qdisc TBF qdisc. 327 * @return MPU in bytes or a negative error code. 328 */ 329int rtnl_qdisc_tbf_get_mpu(struct rtnl_qdisc *qdisc) 330{ 331 struct rtnl_tbf *tbf; 332 333 tbf = tbf_qdisc(qdisc); 334 if (tbf && (tbf->qt_mask & TBF_ATTR_MPU)) 335 return tbf->qt_mpu; 336 else 337 return -NLE_NOATTR; 338} 339 340static inline int calc_cell_log(int cell, int bucket) 341{ 342 if (cell > 0) 343 cell = rtnl_tc_calc_cell_log(cell); 344 else { 345 cell = 0; 346 347 if (!bucket) 348 bucket = 2047; /* defaults to cell_log=3 */ 349 350 while ((bucket >> cell) > 255) 351 cell++; 352 } 353 354 return cell; 355} 356 357/** 358 * Set rate of TBF qdisc. 359 * @arg qdisc TBF qdisc to be modified. 360 * @arg rate New rate in bytes per second. 361 * @arg bucket Size of bucket in bytes. 362 * @arg cell Size of a rate cell or 0 to get default value. 363 * @return 0 on success or a negative error code. 364 */ 365int rtnl_qdisc_tbf_set_rate(struct rtnl_qdisc *qdisc, int rate, int bucket, 366 int cell) 367{ 368 struct rtnl_tbf *tbf; 369 int cell_log; 370 371 tbf = tbf_alloc(qdisc); 372 if (!tbf) 373 return -NLE_NOMEM; 374 375 cell_log = calc_cell_log(cell, bucket); 376 if (cell_log < 0) 377 return cell_log; 378 379 tbf->qt_rate.rs_rate = rate; 380 tbf->qt_rate_bucket = bucket; 381 tbf->qt_rate.rs_cell_log = cell_log; 382 tbf->qt_rate_txtime = rtnl_tc_calc_txtime(bucket, rate); 383 tbf->qt_mask |= TBF_ATTR_RATE; 384 385 return 0; 386} 387 388/** 389 * Get rate of TBF qdisc. 390 * @arg qdisc TBF qdisc. 391 * @return Rate in bytes per seconds or a negative error code. 392 */ 393int rtnl_qdisc_tbf_get_rate(struct rtnl_qdisc *qdisc) 394{ 395 struct rtnl_tbf *tbf; 396 397 tbf = tbf_qdisc(qdisc); 398 if (tbf && (tbf->qt_mask & TBF_ATTR_RATE)) 399 return tbf->qt_rate.rs_rate; 400 else 401 return -1; 402} 403 404/** 405 * Get rate bucket size of TBF qdisc. 406 * @arg qdisc TBF qdisc. 407 * @return Size of rate bucket or a negative error code. 408 */ 409int rtnl_qdisc_tbf_get_rate_bucket(struct rtnl_qdisc *qdisc) 410{ 411 struct rtnl_tbf *tbf; 412 413 tbf = tbf_qdisc(qdisc); 414 if (tbf && (tbf->qt_mask & TBF_ATTR_RATE)) 415 return tbf->qt_rate_bucket; 416 else 417 return -1; 418} 419 420/** 421 * Get rate cell size of TBF qdisc. 422 * @arg qdisc TBF qdisc. 423 * @return Size of rate cell in bytes or a negative error code. 424 */ 425int rtnl_qdisc_tbf_get_rate_cell(struct rtnl_qdisc *qdisc) 426{ 427 struct rtnl_tbf *tbf; 428 429 tbf = tbf_qdisc(qdisc); 430 if (tbf && (tbf->qt_mask & TBF_ATTR_RATE)) 431 return (1 << tbf->qt_rate.rs_cell_log); 432 else 433 return -1; 434} 435 436/** 437 * Set peak rate of TBF qdisc. 438 * @arg qdisc TBF qdisc to be modified. 439 * @arg rate New peak rate in bytes per second. 440 * @arg bucket Size of peakrate bucket. 441 * @arg cell Size of a peakrate cell or 0 to get default value. 442 * @return 0 on success or a negative error code. 443 */ 444int rtnl_qdisc_tbf_set_peakrate(struct rtnl_qdisc *qdisc, int rate, int bucket, 445 int cell) 446{ 447 struct rtnl_tbf *tbf; 448 int cell_log; 449 450 tbf = tbf_alloc(qdisc); 451 if (!tbf) 452 return -NLE_NOMEM; 453 454 cell_log = calc_cell_log(cell, bucket); 455 if (cell_log < 0) 456 return cell_log; 457 458 tbf->qt_peakrate.rs_rate = rate; 459 tbf->qt_peakrate_bucket = bucket; 460 tbf->qt_peakrate.rs_cell_log = cell_log; 461 tbf->qt_peakrate_txtime = rtnl_tc_calc_txtime(bucket, rate); 462 463 tbf->qt_mask |= TBF_ATTR_PEAKRATE; 464 465 return 0; 466} 467 468/** 469 * Get peak rate of TBF qdisc. 470 * @arg qdisc TBF qdisc. 471 * @return Peak rate in bytes per seconds or a negative error code. 472 */ 473int rtnl_qdisc_tbf_get_peakrate(struct rtnl_qdisc *qdisc) 474{ 475 struct rtnl_tbf *tbf; 476 477 tbf = tbf_qdisc(qdisc); 478 if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE)) 479 return tbf->qt_peakrate.rs_rate; 480 else 481 return -1; 482} 483 484/** 485 * Get peak rate bucket size of TBF qdisc. 486 * @arg qdisc TBF qdisc. 487 * @return Size of peak rate bucket or a negative error code. 488 */ 489int rtnl_qdisc_tbf_get_peakrate_bucket(struct rtnl_qdisc *qdisc) 490{ 491 struct rtnl_tbf *tbf; 492 493 tbf = tbf_qdisc(qdisc); 494 if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE)) 495 return tbf->qt_peakrate_bucket; 496 else 497 return -1; 498} 499 500/** 501 * Get peak rate cell size of TBF qdisc. 502 * @arg qdisc TBF qdisc. 503 * @return Size of peak rate cell in bytes or a negative error code. 504 */ 505int rtnl_qdisc_tbf_get_peakrate_cell(struct rtnl_qdisc *qdisc) 506{ 507 struct rtnl_tbf *tbf; 508 509 tbf = tbf_qdisc(qdisc); 510 if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE)) 511 return (1 << tbf->qt_peakrate.rs_cell_log); 512 else 513 return -1; 514} 515 516/** @} */ 517 518static struct rtnl_qdisc_ops tbf_qdisc_ops = { 519 .qo_kind = "tbf", 520 .qo_msg_parser = tbf_msg_parser, 521 .qo_dump = { 522 [NL_DUMP_LINE] = tbf_dump_line, 523 [NL_DUMP_DETAILS] = tbf_dump_details, 524 }, 525 .qo_free_data = tbf_free_data, 526 .qo_get_opts = tbf_get_opts, 527}; 528 529static void __init tbf_init(void) 530{ 531 rtnl_qdisc_register(&tbf_qdisc_ops); 532} 533 534static void __exit tbf_exit(void) 535{ 536 rtnl_qdisc_unregister(&tbf_qdisc_ops); 537} 538 539/** @} */ 540