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