libnl 1.1
|
00001 /* 00002 * lib/route/sch/tbf.c TBF 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 */ 00011 00012 /** 00013 * @ingroup qdisc_api 00014 * @defgroup tbf Token Bucket Filter (TBF) 00015 * @{ 00016 */ 00017 00018 #include <netlink-local.h> 00019 #include <netlink-tc.h> 00020 #include <netlink/netlink.h> 00021 #include <netlink/cache.h> 00022 #include <netlink/utils.h> 00023 #include <netlink/route/tc.h> 00024 #include <netlink/route/qdisc.h> 00025 #include <netlink/route/qdisc-modules.h> 00026 #include <netlink/route/class.h> 00027 #include <netlink/route/class-modules.h> 00028 #include <netlink/route/link.h> 00029 #include <netlink/route/sch/tbf.h> 00030 00031 /** @cond SKIP */ 00032 #define TBF_ATTR_LIMIT 0x01 00033 #define TBF_ATTR_RATE 0x02 00034 #define TBF_ATTR_PEAKRATE 0x10 00035 #define TBF_ATTR_MPU 0x80 00036 /** @endcond */ 00037 00038 static inline struct rtnl_tbf *tbf_qdisc(struct rtnl_qdisc *qdisc) 00039 { 00040 return (struct rtnl_tbf *) qdisc->q_subdata; 00041 } 00042 00043 static inline struct rtnl_tbf *tbf_alloc(struct rtnl_qdisc *qdisc) 00044 { 00045 if (!qdisc->q_subdata) 00046 qdisc->q_subdata = calloc(1, sizeof(struct rtnl_tbf)); 00047 00048 return tbf_qdisc(qdisc); 00049 } 00050 00051 static struct nla_policy tbf_policy[TCA_TBF_MAX+1] = { 00052 [TCA_TBF_PARMS] = { .minlen = sizeof(struct tc_tbf_qopt) }, 00053 }; 00054 00055 static int tbf_msg_parser(struct rtnl_qdisc *q) 00056 { 00057 int err; 00058 struct nlattr *tb[TCA_TBF_MAX + 1]; 00059 struct rtnl_tbf *tbf; 00060 00061 err = tca_parse(tb, TCA_TBF_MAX, (struct rtnl_tca *) q, tbf_policy); 00062 if (err < 0) 00063 return err; 00064 00065 tbf = tbf_qdisc(q); 00066 if (!tbf) 00067 return nl_errno(ENOMEM); 00068 00069 if (tb[TCA_TBF_PARMS]) { 00070 struct tc_tbf_qopt opts; 00071 int bufsize; 00072 00073 nla_memcpy(&opts, tb[TCA_TBF_PARMS], sizeof(opts)); 00074 tbf->qt_limit = opts.limit; 00075 tbf->qt_mpu = opts.rate.mpu; 00076 00077 rtnl_copy_ratespec(&tbf->qt_rate, &opts.rate); 00078 tbf->qt_rate_txtime = opts.buffer; 00079 bufsize = rtnl_tc_calc_bufsize(nl_ticks2us(opts.buffer), 00080 opts.rate.rate); 00081 tbf->qt_rate_bucket = bufsize; 00082 00083 rtnl_copy_ratespec(&tbf->qt_peakrate, &opts.peakrate); 00084 tbf->qt_peakrate_txtime = opts.mtu; 00085 bufsize = rtnl_tc_calc_bufsize(nl_ticks2us(opts.mtu), 00086 opts.peakrate.rate); 00087 tbf->qt_peakrate_bucket = bufsize; 00088 00089 tbf->qt_mask = (TBF_ATTR_LIMIT | TBF_ATTR_MPU | TBF_ATTR_RATE | 00090 TBF_ATTR_PEAKRATE); 00091 } 00092 00093 return 0; 00094 } 00095 00096 static int tbf_dump_brief(struct rtnl_qdisc *qdisc, struct nl_dump_params *p, 00097 int line) 00098 { 00099 double r, rbit, lim; 00100 char *ru, *rubit, *limu; 00101 struct rtnl_tbf *tbf = tbf_qdisc(qdisc); 00102 00103 if (!tbf) 00104 goto ignore; 00105 00106 r = nl_cancel_down_bytes(tbf->qt_rate.rs_rate, &ru); 00107 rbit = nl_cancel_down_bits(tbf->qt_rate.rs_rate*8, &rubit); 00108 lim = nl_cancel_down_bytes(tbf->qt_limit, &limu); 00109 00110 dp_dump(p, " rate %.2f%s/s (%.0f%s) limit %.2f%s", 00111 r, ru, rbit, rubit, lim, limu); 00112 00113 ignore: 00114 return line; 00115 } 00116 00117 static int tbf_dump_full(struct rtnl_qdisc *qdisc, struct nl_dump_params *p, 00118 int line) 00119 { 00120 struct rtnl_tbf *tbf = tbf_qdisc(qdisc); 00121 00122 if (!tbf) 00123 goto ignore; 00124 00125 if (1) { 00126 char *bu, *cu; 00127 double bs = nl_cancel_down_bytes(tbf->qt_rate_bucket, &bu); 00128 double cl = nl_cancel_down_bytes(1 << tbf->qt_rate.rs_cell_log, 00129 &cu); 00130 00131 dp_dump(p, "mpu %u rate-bucket-size %1.f%s " 00132 "rate-cell-size %.1f%s\n", 00133 tbf->qt_mpu, bs, bu, cl, cu); 00134 00135 } 00136 00137 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) { 00138 char *pru, *prbu, *bsu, *clu; 00139 double pr, prb, bs, cl; 00140 00141 pr = nl_cancel_down_bytes(tbf->qt_peakrate.rs_rate, &pru); 00142 prb = nl_cancel_down_bits(tbf->qt_peakrate.rs_rate * 8, &prbu); 00143 bs = nl_cancel_down_bits(tbf->qt_peakrate_bucket, &bsu); 00144 cl = nl_cancel_down_bits(1 << tbf->qt_peakrate.rs_cell_log, 00145 &clu); 00146 00147 dp_dump_line(p, line++, " peak-rate %.2f%s/s (%.0f%s) " 00148 "bucket-size %.1f%s cell-size %.1f%s", 00149 "latency %.1f%s", 00150 pr, pru, prb, prbu, bs, bsu, cl, clu); 00151 } 00152 00153 ignore: 00154 return line; 00155 } 00156 00157 static struct nl_msg *tbf_get_opts(struct rtnl_qdisc *qdisc) 00158 { 00159 struct tc_tbf_qopt opts; 00160 struct rtnl_tbf *tbf; 00161 struct nl_msg *msg; 00162 uint32_t rtab[RTNL_TC_RTABLE_SIZE]; 00163 uint32_t ptab[RTNL_TC_RTABLE_SIZE]; 00164 int required = TBF_ATTR_RATE | TBF_ATTR_LIMIT; 00165 00166 memset(&opts, 0, sizeof(opts)); 00167 00168 tbf = tbf_qdisc(qdisc); 00169 if (!tbf) 00170 return NULL; 00171 00172 if (!(tbf->qt_mask & required) != required) 00173 return NULL; 00174 00175 opts.limit = tbf->qt_limit; 00176 opts.buffer = tbf->qt_rate_txtime; 00177 tbf->qt_rate.rs_mpu = tbf->qt_mpu; 00178 rtnl_rcopy_ratespec(&opts.rate, &tbf->qt_rate); 00179 00180 rtnl_tc_build_rate_table(rtab, tbf->qt_mpu & 0xff, tbf->qt_mpu >> 8, 00181 1 << tbf->qt_rate.rs_cell_log, 00182 tbf->qt_rate.rs_rate); 00183 00184 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) { 00185 opts.mtu = tbf->qt_peakrate_txtime; 00186 tbf->qt_peakrate.rs_mpu = tbf->qt_mpu; 00187 rtnl_rcopy_ratespec(&opts.peakrate, &tbf->qt_peakrate); 00188 00189 rtnl_tc_build_rate_table(ptab, tbf->qt_mpu & 0xff, 00190 tbf->qt_mpu >> 8, 00191 1 << tbf->qt_peakrate.rs_cell_log, 00192 tbf->qt_peakrate.rs_rate); 00193 } 00194 00195 msg = nlmsg_alloc(); 00196 if (!msg) 00197 goto nla_put_failure; 00198 00199 NLA_PUT(msg, TCA_TBF_PARMS, sizeof(opts), &opts); 00200 NLA_PUT(msg, TCA_TBF_RTAB, sizeof(rtab), rtab); 00201 00202 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) 00203 NLA_PUT(msg, TCA_TBF_PTAB, sizeof(ptab), ptab); 00204 00205 return msg; 00206 00207 nla_put_failure: 00208 nlmsg_free(msg); 00209 return NULL; 00210 } 00211 00212 /** 00213 * @name Attribute Access 00214 * @{ 00215 */ 00216 00217 /** 00218 * Set limit of TBF qdisc. 00219 * @arg qdisc TBF qdisc to be modified. 00220 * @arg limit New limit in bytes. 00221 * @return 0 on success or a negative error code. 00222 */ 00223 int rtnl_qdisc_tbf_set_limit(struct rtnl_qdisc *qdisc, int limit) 00224 { 00225 struct rtnl_tbf *tbf; 00226 00227 tbf = tbf_alloc(qdisc); 00228 if (!tbf) 00229 return nl_errno(ENOMEM); 00230 00231 tbf->qt_limit = limit; 00232 tbf->qt_mask |= TBF_ATTR_LIMIT; 00233 00234 return 0; 00235 } 00236 00237 static inline double calc_limit(struct rtnl_ratespec *spec, int latency, 00238 int bucket) 00239 { 00240 double limit; 00241 00242 limit = (double) spec->rs_rate * ((double) latency / 1000000.); 00243 limit += bucket; 00244 00245 return limit; 00246 } 00247 00248 /** 00249 * Set limit of TBF qdisc by latency. 00250 * @arg qdisc TBF qdisc to be modified. 00251 * @arg latency Latency in micro seconds. 00252 * 00253 * Calculates and sets the limit based on the desired latency and the 00254 * configured rate and peak rate. In order for this operation to succeed, 00255 * the rate and if required the peak rate must have been set in advance. 00256 * 00257 * @f[ 00258 * limit_n = \frac{{rate_n} \times {latency}}{10^6}+{bucketsize}_n 00259 * @f] 00260 * @f[ 00261 * limit = min(limit_{rate},limit_{peak}) 00262 * @f] 00263 * 00264 * @return 0 on success or a negative error code. 00265 */ 00266 int rtnl_qdisc_tbf_set_limit_by_latency(struct rtnl_qdisc *qdisc, int latency) 00267 { 00268 struct rtnl_tbf *tbf; 00269 double limit, limit2; 00270 00271 tbf = tbf_alloc(qdisc); 00272 if (!tbf) 00273 return nl_errno(ENOMEM); 00274 00275 if (!(tbf->qt_mask & TBF_ATTR_RATE)) 00276 return nl_error(EINVAL, "The rate must be specified before " 00277 "limit can be calculated based on latency."); 00278 00279 limit = calc_limit(&tbf->qt_rate, latency, tbf->qt_rate_bucket); 00280 00281 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) { 00282 limit2 = calc_limit(&tbf->qt_peakrate, latency, 00283 tbf->qt_peakrate_bucket); 00284 00285 if (limit2 < limit) 00286 limit = limit2; 00287 } 00288 00289 return rtnl_qdisc_tbf_set_limit(qdisc, (int) limit); 00290 } 00291 00292 /** 00293 * Get limit of TBF qdisc. 00294 * @arg qdisc TBF qdisc. 00295 * @return Limit in bytes or a negative error code. 00296 */ 00297 int rtnl_qdisc_tbf_get_limit(struct rtnl_qdisc *qdisc) 00298 { 00299 struct rtnl_tbf *tbf; 00300 00301 tbf = tbf_qdisc(qdisc); 00302 if (tbf && (tbf->qt_mask & TBF_ATTR_LIMIT)) 00303 return tbf->qt_limit; 00304 return 00305 nl_errno(ENOENT); 00306 } 00307 00308 /** 00309 * Set MPU of TBF qdisc. 00310 * @arg qdisc TBF qdisc to be modified. 00311 * @arg mpu New MPU in bytes. 00312 * @return 0 on success or a negative error code. 00313 */ 00314 int rtnl_qdisc_tbf_set_mpu(struct rtnl_qdisc *qdisc, int mpu) 00315 { 00316 struct rtnl_tbf *tbf; 00317 00318 tbf = tbf_alloc(qdisc); 00319 if (!tbf) 00320 return nl_errno(ENOMEM); 00321 00322 tbf->qt_mpu = mpu; 00323 tbf->qt_mask |= TBF_ATTR_MPU; 00324 00325 return 0; 00326 } 00327 00328 /** 00329 * Get MPU of TBF qdisc. 00330 * @arg qdisc TBF qdisc. 00331 * @return MPU in bytes or a negative error code. 00332 */ 00333 int rtnl_qdisc_tbf_get_mpu(struct rtnl_qdisc *qdisc) 00334 { 00335 struct rtnl_tbf *tbf; 00336 00337 tbf = tbf_qdisc(qdisc); 00338 if (tbf && (tbf->qt_mask & TBF_ATTR_MPU)) 00339 return tbf->qt_mpu; 00340 return 00341 nl_errno(ENOENT); 00342 } 00343 00344 static inline int calc_cell_log(int cell, int bucket) 00345 { 00346 if (cell > 0) 00347 cell = rtnl_tc_calc_cell_log(cell); 00348 else { 00349 cell = 0; 00350 00351 if (!bucket) 00352 bucket = 2047; /* defaults to cell_log=3 */ 00353 00354 while ((bucket >> cell) > 255) 00355 cell++; 00356 } 00357 00358 return cell; 00359 } 00360 00361 /** 00362 * Set rate of TBF qdisc. 00363 * @arg qdisc TBF qdisc to be modified. 00364 * @arg rate New rate in bytes per second. 00365 * @arg bucket Size of bucket in bytes. 00366 * @arg cell Size of a rate cell or 0 to get default value. 00367 * @return 0 on success or a negative error code. 00368 */ 00369 int rtnl_qdisc_tbf_set_rate(struct rtnl_qdisc *qdisc, int rate, int bucket, 00370 int cell) 00371 { 00372 struct rtnl_tbf *tbf; 00373 int cell_log; 00374 00375 tbf = tbf_alloc(qdisc); 00376 if (!tbf) 00377 return nl_errno(ENOMEM); 00378 00379 cell_log = calc_cell_log(cell, bucket); 00380 if (cell_log < 0) 00381 return cell_log; 00382 00383 tbf->qt_rate.rs_rate = rate; 00384 tbf->qt_rate_bucket = bucket; 00385 tbf->qt_rate.rs_cell_log = cell_log; 00386 tbf->qt_rate_txtime = rtnl_tc_calc_txtime(bucket, rate); 00387 tbf->qt_mask |= TBF_ATTR_RATE; 00388 00389 return 0; 00390 } 00391 00392 /** 00393 * Get rate of TBF qdisc. 00394 * @arg qdisc TBF qdisc. 00395 * @return Rate in bytes per seconds or a negative error code. 00396 */ 00397 int rtnl_qdisc_tbf_get_rate(struct rtnl_qdisc *qdisc) 00398 { 00399 struct rtnl_tbf *tbf; 00400 00401 tbf = tbf_qdisc(qdisc); 00402 if (tbf && (tbf->qt_mask & TBF_ATTR_RATE)) 00403 return tbf->qt_rate.rs_rate; 00404 else 00405 return -1; 00406 } 00407 00408 /** 00409 * Get rate bucket size of TBF qdisc. 00410 * @arg qdisc TBF qdisc. 00411 * @return Size of rate bucket or a negative error code. 00412 */ 00413 int rtnl_qdisc_tbf_get_rate_bucket(struct rtnl_qdisc *qdisc) 00414 { 00415 struct rtnl_tbf *tbf; 00416 00417 tbf = tbf_qdisc(qdisc); 00418 if (tbf && (tbf->qt_mask & TBF_ATTR_RATE)) 00419 return tbf->qt_rate_bucket; 00420 else 00421 return -1; 00422 } 00423 00424 /** 00425 * Get rate cell size of TBF qdisc. 00426 * @arg qdisc TBF qdisc. 00427 * @return Size of rate cell in bytes or a negative error code. 00428 */ 00429 int rtnl_qdisc_tbf_get_rate_cell(struct rtnl_qdisc *qdisc) 00430 { 00431 struct rtnl_tbf *tbf; 00432 00433 tbf = tbf_qdisc(qdisc); 00434 if (tbf && (tbf->qt_mask & TBF_ATTR_RATE)) 00435 return (1 << tbf->qt_rate.rs_cell_log); 00436 else 00437 return -1; 00438 } 00439 00440 /** 00441 * Set peak rate of TBF qdisc. 00442 * @arg qdisc TBF qdisc to be modified. 00443 * @arg rate New peak rate in bytes per second. 00444 * @arg bucket Size of peakrate bucket. 00445 * @arg cell Size of a peakrate cell or 0 to get default value. 00446 * @return 0 on success or a negative error code. 00447 */ 00448 int rtnl_qdisc_tbf_set_peakrate(struct rtnl_qdisc *qdisc, int rate, int bucket, 00449 int cell) 00450 { 00451 struct rtnl_tbf *tbf; 00452 int cell_log; 00453 00454 tbf = tbf_alloc(qdisc); 00455 if (!tbf) 00456 return nl_errno(ENOMEM); 00457 00458 cell_log = calc_cell_log(cell, bucket); 00459 if (cell_log < 0) 00460 return cell_log; 00461 00462 tbf->qt_peakrate.rs_rate = rate; 00463 tbf->qt_peakrate_bucket = bucket; 00464 tbf->qt_peakrate.rs_cell_log = cell_log; 00465 tbf->qt_peakrate_txtime = rtnl_tc_calc_txtime(bucket, rate); 00466 00467 tbf->qt_mask |= TBF_ATTR_PEAKRATE; 00468 00469 return 0; 00470 } 00471 00472 /** 00473 * Get peak rate of TBF qdisc. 00474 * @arg qdisc TBF qdisc. 00475 * @return Peak rate in bytes per seconds or a negative error code. 00476 */ 00477 int rtnl_qdisc_tbf_get_peakrate(struct rtnl_qdisc *qdisc) 00478 { 00479 struct rtnl_tbf *tbf; 00480 00481 tbf = tbf_qdisc(qdisc); 00482 if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE)) 00483 return tbf->qt_peakrate.rs_rate; 00484 else 00485 return -1; 00486 } 00487 00488 /** 00489 * Get peak rate bucket size of TBF qdisc. 00490 * @arg qdisc TBF qdisc. 00491 * @return Size of peak rate bucket or a negative error code. 00492 */ 00493 int rtnl_qdisc_tbf_get_peakrate_bucket(struct rtnl_qdisc *qdisc) 00494 { 00495 struct rtnl_tbf *tbf; 00496 00497 tbf = tbf_qdisc(qdisc); 00498 if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE)) 00499 return tbf->qt_peakrate_bucket; 00500 else 00501 return -1; 00502 } 00503 00504 /** 00505 * Get peak rate cell size of TBF qdisc. 00506 * @arg qdisc TBF qdisc. 00507 * @return Size of peak rate cell in bytes or a negative error code. 00508 */ 00509 int rtnl_qdisc_tbf_get_peakrate_cell(struct rtnl_qdisc *qdisc) 00510 { 00511 struct rtnl_tbf *tbf; 00512 00513 tbf = tbf_qdisc(qdisc); 00514 if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE)) 00515 return (1 << tbf->qt_peakrate.rs_cell_log); 00516 else 00517 return -1; 00518 } 00519 00520 /** @} */ 00521 00522 static struct rtnl_qdisc_ops tbf_qdisc_ops = { 00523 .qo_kind = "tbf", 00524 .qo_msg_parser = tbf_msg_parser, 00525 .qo_dump[NL_DUMP_BRIEF] = tbf_dump_brief, 00526 .qo_dump[NL_DUMP_FULL] = tbf_dump_full, 00527 .qo_get_opts = tbf_get_opts, 00528 }; 00529 00530 static void __init tbf_init(void) 00531 { 00532 rtnl_qdisc_register(&tbf_qdisc_ops); 00533 } 00534 00535 static void __exit tbf_exit(void) 00536 { 00537 rtnl_qdisc_unregister(&tbf_qdisc_ops); 00538 } 00539 00540 /** @} */